1 | // |
2 | // Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. |
3 | // |
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | // of this software and associated documentation files (the "Software"), to deal |
6 | // in the Software without restriction, including without limitation the rights |
7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
8 | // copies of the Software, and to permit persons to whom the Software is |
9 | // furnished to do so, subject to the following conditions: |
10 | // |
11 | // The above copyright notice and this permission notice shall be included in |
12 | // all copies or substantial portions of the Software. |
13 | // |
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
20 | // THE SOFTWARE. |
21 | // |
22 | |
23 | #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H |
24 | #define AMD_VULKAN_MEMORY_ALLOCATOR_H |
25 | |
26 | #ifdef __cplusplus |
27 | extern "C" { |
28 | #endif |
29 | |
30 | /** \mainpage Vulkan Memory Allocator |
31 | |
32 | <b>Version 2.2.0</b> (2018-12-13) |
33 | |
34 | Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n |
35 | License: MIT |
36 | |
37 | Documentation of all members: vk_mem_alloc.h |
38 | |
39 | \section main_table_of_contents Table of contents |
40 | |
41 | - <b>User guide</b> |
42 | - \subpage quick_start |
43 | - [Project setup](@ref quick_start_project_setup) |
44 | - [Initialization](@ref quick_start_initialization) |
45 | - [Resource allocation](@ref quick_start_resource_allocation) |
46 | - \subpage choosing_memory_type |
47 | - [Usage](@ref choosing_memory_type_usage) |
48 | - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags) |
49 | - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types) |
50 | - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools) |
51 | - \subpage memory_mapping |
52 | - [Mapping functions](@ref memory_mapping_mapping_functions) |
53 | - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory) |
54 | - [Cache control](@ref memory_mapping_cache_control) |
55 | - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable) |
56 | - \subpage custom_memory_pools |
57 | - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex) |
58 | - [Linear allocation algorithm](@ref linear_algorithm) |
59 | - [Free-at-once](@ref linear_algorithm_free_at_once) |
60 | - [Stack](@ref linear_algorithm_stack) |
61 | - [Double stack](@ref linear_algorithm_double_stack) |
62 | - [Ring buffer](@ref linear_algorithm_ring_buffer) |
63 | - [Buddy allocation algorithm](@ref buddy_algorithm) |
64 | - \subpage defragmentation |
65 | - [Defragmenting CPU memory](@ref defragmentation_cpu) |
66 | - [Defragmenting GPU memory](@ref defragmentation_gpu) |
67 | - [Additional notes](@ref defragmentation_additional_notes) |
68 | - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm) |
69 | - \subpage lost_allocations |
70 | - \subpage statistics |
71 | - [Numeric statistics](@ref statistics_numeric_statistics) |
72 | - [JSON dump](@ref statistics_json_dump) |
73 | - \subpage allocation_annotation |
74 | - [Allocation user data](@ref allocation_user_data) |
75 | - [Allocation names](@ref allocation_names) |
76 | - \subpage debugging_memory_usage |
77 | - [Memory initialization](@ref debugging_memory_usage_initialization) |
78 | - [Margins](@ref debugging_memory_usage_margins) |
79 | - [Corruption detection](@ref debugging_memory_usage_corruption_detection) |
80 | - \subpage record_and_replay |
81 | - \subpage usage_patterns |
82 | - [Simple patterns](@ref usage_patterns_simple) |
83 | - [Advanced patterns](@ref usage_patterns_advanced) |
84 | - \subpage configuration |
85 | - [Pointers to Vulkan functions](@ref config_Vulkan_functions) |
86 | - [Custom host memory allocator](@ref custom_memory_allocator) |
87 | - [Device memory allocation callbacks](@ref allocation_callbacks) |
88 | - [Device heap memory limit](@ref heap_memory_limit) |
89 | - \subpage vk_khr_dedicated_allocation |
90 | - \subpage general_considerations |
91 | - [Thread safety](@ref general_considerations_thread_safety) |
92 | - [Validation layer warnings](@ref general_considerations_validation_layer_warnings) |
93 | - [Allocation algorithm](@ref general_considerations_allocation_algorithm) |
94 | - [Features not supported](@ref general_considerations_features_not_supported) |
95 | |
96 | \section main_see_also See also |
97 | |
98 | - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/) |
99 | - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) |
100 | |
101 | |
102 | |
103 | |
104 | \page quick_start Quick start |
105 | |
106 | \section quick_start_project_setup Project setup |
107 | |
108 | Vulkan Memory Allocator comes in form of a single header file. |
109 | You don't need to build it as a separate library project. |
110 | You can add this file directly to your project and submit it to code repository next to your other source files. |
111 | |
112 | "Single header" doesn't mean that everything is contained in C/C++ declarations, |
113 | like it tends to be in case of inline functions or C++ templates. |
114 | It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro. |
115 | If you don't do it properly, you will get linker errors. |
116 | |
117 | To do it properly: |
118 | |
119 | -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library. |
120 | This includes declarations of all members of the library. |
121 | -# In exacly one CPP file define following macro before this include. |
122 | It enables also internal definitions. |
123 | |
124 | \code |
125 | #define VMA_IMPLEMENTATION |
126 | #include "vk_mem_alloc.h" |
127 | \endcode |
128 | |
129 | It may be a good idea to create dedicated CPP file just for this purpose. |
130 | |
131 | Note on language: This library is written in C++, but has C-compatible interface. |
132 | Thus you can include and use vk_mem_alloc.h in C or C++ code, but full |
133 | implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C. |
134 | |
135 | Please note that this library includes header `<vulkan/vulkan.h>`, which in turn |
136 | includes `<windows.h>` on Windows. If you need some specific macros defined |
137 | before including these headers (like `WIN32_LEAN_AND_MEAN` or |
138 | `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define |
139 | them before every `#include` of this library. |
140 | |
141 | |
142 | \section quick_start_initialization Initialization |
143 | |
144 | At program startup: |
145 | |
146 | -# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object. |
147 | -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by |
148 | calling vmaCreateAllocator(). |
149 | |
150 | \code |
151 | VmaAllocatorCreateInfo allocatorInfo = {}; |
152 | allocatorInfo.physicalDevice = physicalDevice; |
153 | allocatorInfo.device = device; |
154 | |
155 | VmaAllocator allocator; |
156 | vmaCreateAllocator(&allocatorInfo, &allocator); |
157 | \endcode |
158 | |
159 | \section quick_start_resource_allocation Resource allocation |
160 | |
161 | When you want to create a buffer or image: |
162 | |
163 | -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure. |
164 | -# Fill VmaAllocationCreateInfo structure. |
165 | -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory |
166 | already allocated and bound to it. |
167 | |
168 | \code |
169 | VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
170 | bufferInfo.size = 65536; |
171 | bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
172 | |
173 | VmaAllocationCreateInfo allocInfo = {}; |
174 | allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
175 | |
176 | VkBuffer buffer; |
177 | VmaAllocation allocation; |
178 | vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); |
179 | \endcode |
180 | |
181 | Don't forget to destroy your objects when no longer needed: |
182 | |
183 | \code |
184 | vmaDestroyBuffer(allocator, buffer, allocation); |
185 | vmaDestroyAllocator(allocator); |
186 | \endcode |
187 | |
188 | |
189 | \page choosing_memory_type Choosing memory type |
190 | |
191 | Physical devices in Vulkan support various combinations of memory heaps and |
192 | types. Help with choosing correct and optimal memory type for your specific |
193 | resource is one of the key features of this library. You can use it by filling |
194 | appropriate members of VmaAllocationCreateInfo structure, as described below. |
195 | You can also combine multiple methods. |
196 | |
197 | -# If you just want to find memory type index that meets your requirements, you |
198 | can use function vmaFindMemoryTypeIndex(). |
199 | -# If you want to allocate a region of device memory without association with any |
200 | specific image or buffer, you can use function vmaAllocateMemory(). Usage of |
201 | this function is not recommended and usually not needed. |
202 | -# If you already have a buffer or an image created, you want to allocate memory |
203 | for it and then you will bind it yourself, you can use function |
204 | vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(). |
205 | For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory(). |
206 | -# If you want to create a buffer or an image, allocate memory for it and bind |
207 | them together, all in one call, you can use function vmaCreateBuffer(), |
208 | vmaCreateImage(). This is the recommended way to use this library. |
209 | |
210 | When using 3. or 4., the library internally queries Vulkan for memory types |
211 | supported for that buffer or image (function `vkGetBufferMemoryRequirements()`) |
212 | and uses only one of these types. |
213 | |
214 | If no memory type can be found that meets all the requirements, these functions |
215 | return `VK_ERROR_FEATURE_NOT_PRESENT`. |
216 | |
217 | You can leave VmaAllocationCreateInfo structure completely filled with zeros. |
218 | It means no requirements are specified for memory type. |
219 | It is valid, although not very useful. |
220 | |
221 | \section choosing_memory_type_usage Usage |
222 | |
223 | The easiest way to specify memory requirements is to fill member |
224 | VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage. |
225 | It defines high level, common usage types. |
226 | For more details, see description of this enum. |
227 | |
228 | For example, if you want to create a uniform buffer that will be filled using |
229 | transfer only once or infrequently and used for rendering every frame, you can |
230 | do it using following code: |
231 | |
232 | \code |
233 | VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
234 | bufferInfo.size = 65536; |
235 | bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
236 | |
237 | VmaAllocationCreateInfo allocInfo = {}; |
238 | allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
239 | |
240 | VkBuffer buffer; |
241 | VmaAllocation allocation; |
242 | vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); |
243 | \endcode |
244 | |
245 | \section choosing_memory_type_required_preferred_flags Required and preferred flags |
246 | |
247 | You can specify more detailed requirements by filling members |
248 | VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags |
249 | with a combination of bits from enum `VkMemoryPropertyFlags`. For example, |
250 | if you want to create a buffer that will be persistently mapped on host (so it |
251 | must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`, |
252 | use following code: |
253 | |
254 | \code |
255 | VmaAllocationCreateInfo allocInfo = {}; |
256 | allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
257 | allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; |
258 | allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; |
259 | |
260 | VkBuffer buffer; |
261 | VmaAllocation allocation; |
262 | vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); |
263 | \endcode |
264 | |
265 | A memory type is chosen that has all the required flags and as many preferred |
266 | flags set as possible. |
267 | |
268 | If you use VmaAllocationCreateInfo::usage, it is just internally converted to |
269 | a set of required and preferred flags. |
270 | |
271 | \section choosing_memory_type_explicit_memory_types Explicit memory types |
272 | |
273 | If you inspected memory types available on the physical device and you have |
274 | a preference for memory types that you want to use, you can fill member |
275 | VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set |
276 | means that a memory type with that index is allowed to be used for the |
277 | allocation. Special value 0, just like `UINT32_MAX`, means there are no |
278 | restrictions to memory type index. |
279 | |
280 | Please note that this member is NOT just a memory type index. |
281 | Still you can use it to choose just one, specific memory type. |
282 | For example, if you already determined that your buffer should be created in |
283 | memory type 2, use following code: |
284 | |
285 | \code |
286 | uint32_t memoryTypeIndex = 2; |
287 | |
288 | VmaAllocationCreateInfo allocInfo = {}; |
289 | allocInfo.memoryTypeBits = 1u << memoryTypeIndex; |
290 | |
291 | VkBuffer buffer; |
292 | VmaAllocation allocation; |
293 | vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); |
294 | \endcode |
295 | |
296 | \section choosing_memory_type_custom_memory_pools Custom memory pools |
297 | |
298 | If you allocate from custom memory pool, all the ways of specifying memory |
299 | requirements described above are not applicable and the aforementioned members |
300 | of VmaAllocationCreateInfo structure are ignored. Memory type is selected |
301 | explicitly when creating the pool and then used to make all the allocations from |
302 | that pool. For further details, see \ref custom_memory_pools. |
303 | |
304 | |
305 | \page memory_mapping Memory mapping |
306 | |
307 | To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`, |
308 | to be able to read from it or write to it in CPU code. |
309 | Mapping is possible only of memory allocated from a memory type that has |
310 | `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag. |
311 | Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose. |
312 | You can use them directly with memory allocated by this library, |
313 | but it is not recommended because of following issue: |
314 | Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed. |
315 | This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan. |
316 | Because of this, Vulkan Memory Allocator provides following facilities: |
317 | |
318 | \section memory_mapping_mapping_functions Mapping functions |
319 | |
320 | The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory(). |
321 | They are safer and more convenient to use than standard Vulkan functions. |
322 | You can map an allocation multiple times simultaneously - mapping is reference-counted internally. |
323 | You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block. |
324 | The way it's implemented is that the library always maps entire memory block, not just region of the allocation. |
325 | For further details, see description of vmaMapMemory() function. |
326 | Example: |
327 | |
328 | \code |
329 | // Having these objects initialized: |
330 | |
331 | struct ConstantBuffer |
332 | { |
333 | ... |
334 | }; |
335 | ConstantBuffer constantBufferData; |
336 | |
337 | VmaAllocator allocator; |
338 | VkBuffer constantBuffer; |
339 | VmaAllocation constantBufferAllocation; |
340 | |
341 | // You can map and fill your buffer using following code: |
342 | |
343 | void* mappedData; |
344 | vmaMapMemory(allocator, constantBufferAllocation, &mappedData); |
345 | memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); |
346 | vmaUnmapMemory(allocator, constantBufferAllocation); |
347 | \endcode |
348 | |
349 | When mapping, you may see a warning from Vulkan validation layer similar to this one: |
350 | |
351 | <i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i> |
352 | |
353 | It happens because the library maps entire `VkDeviceMemory` block, where different |
354 | types of images and buffers may end up together, especially on GPUs with unified memory like Intel. |
355 | You can safely ignore it if you are sure you access only memory of the intended |
356 | object that you wanted to map. |
357 | |
358 | |
359 | \section memory_mapping_persistently_mapped_memory Persistently mapped memory |
360 | |
361 | Kepping your memory persistently mapped is generally OK in Vulkan. |
362 | You don't need to unmap it before using its data on the GPU. |
363 | The library provides a special feature designed for that: |
364 | Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in |
365 | VmaAllocationCreateInfo::flags stay mapped all the time, |
366 | so you can just access CPU pointer to it any time |
367 | without a need to call any "map" or "unmap" function. |
368 | Example: |
369 | |
370 | \code |
371 | VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
372 | bufCreateInfo.size = sizeof(ConstantBuffer); |
373 | bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; |
374 | |
375 | VmaAllocationCreateInfo allocCreateInfo = {}; |
376 | allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; |
377 | allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; |
378 | |
379 | VkBuffer buf; |
380 | VmaAllocation alloc; |
381 | VmaAllocationInfo allocInfo; |
382 | vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); |
383 | |
384 | // Buffer is already mapped. You can access its memory. |
385 | memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); |
386 | \endcode |
387 | |
388 | There are some exceptions though, when you should consider mapping memory only for a short period of time: |
389 | |
390 | - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2), |
391 | device is discrete AMD GPU, |
392 | and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory |
393 | (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU), |
394 | then whenever a memory block allocated from this memory type stays mapped |
395 | for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this |
396 | block is migrated by WDDM to system RAM, which degrades performance. It doesn't |
397 | matter if that particular memory block is actually used by the command buffer |
398 | being submitted. |
399 | - On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175) |
400 | which requires unmapping before GPU can see updated texture. |
401 | - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools. |
402 | |
403 | \section memory_mapping_cache_control Cache control |
404 | |
405 | Memory in Vulkan doesn't need to be unmapped before using it on GPU, |
406 | but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set, |
407 | you need to manually invalidate cache before reading of mapped pointer |
408 | and flush cache after writing to mapped pointer. |
409 | Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`, |
410 | `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient |
411 | functions that refer to given allocation object: vmaFlushAllocation(), |
412 | vmaInvalidateAllocation(). |
413 | |
414 | Regions of memory specified for flush/invalidate must be aligned to |
415 | `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library. |
416 | In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations |
417 | within blocks are aligned to this value, so their offsets are always multiply of |
418 | `nonCoherentAtomSize` and two different allocations never share same "line" of this size. |
419 | |
420 | Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`. |
421 | |
422 | Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA) |
423 | currently provide `HOST_COHERENT` flag on all memory types that are |
424 | `HOST_VISIBLE`, so on this platform you may not need to bother. |
425 | |
426 | \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable |
427 | |
428 | It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping) |
429 | despite it wasn't explicitly requested. |
430 | For example, application may work on integrated graphics with unified memory (like Intel) or |
431 | allocation from video memory might have failed, so the library chose system memory as fallback. |
432 | |
433 | You can detect this case and map such allocation to access its memory on CPU directly, |
434 | instead of launching a transfer operation. |
435 | In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(), |
436 | and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type. |
437 | |
438 | \code |
439 | VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
440 | bufCreateInfo.size = sizeof(ConstantBuffer); |
441 | bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
442 | |
443 | VmaAllocationCreateInfo allocCreateInfo = {}; |
444 | allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
445 | allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
446 | |
447 | VkBuffer buf; |
448 | VmaAllocation alloc; |
449 | VmaAllocationInfo allocInfo; |
450 | vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); |
451 | |
452 | VkMemoryPropertyFlags memFlags; |
453 | vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags); |
454 | if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) |
455 | { |
456 | // Allocation ended up in mappable memory. You can map it and access it directly. |
457 | void* mappedData; |
458 | vmaMapMemory(allocator, alloc, &mappedData); |
459 | memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); |
460 | vmaUnmapMemory(allocator, alloc); |
461 | } |
462 | else |
463 | { |
464 | // Allocation ended up in non-mappable memory. |
465 | // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer. |
466 | } |
467 | \endcode |
468 | |
469 | You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations |
470 | that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY). |
471 | If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly. |
472 | If not, the flag is just ignored. |
473 | Example: |
474 | |
475 | \code |
476 | VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
477 | bufCreateInfo.size = sizeof(ConstantBuffer); |
478 | bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
479 | |
480 | VmaAllocationCreateInfo allocCreateInfo = {}; |
481 | allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
482 | allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; |
483 | |
484 | VkBuffer buf; |
485 | VmaAllocation alloc; |
486 | VmaAllocationInfo allocInfo; |
487 | vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); |
488 | |
489 | if(allocInfo.pUserData != nullptr) |
490 | { |
491 | // Allocation ended up in mappable memory. |
492 | // It's persistently mapped. You can access it directly. |
493 | memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); |
494 | } |
495 | else |
496 | { |
497 | // Allocation ended up in non-mappable memory. |
498 | // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer. |
499 | } |
500 | \endcode |
501 | |
502 | |
503 | \page custom_memory_pools Custom memory pools |
504 | |
505 | A memory pool contains a number of `VkDeviceMemory` blocks. |
506 | The library automatically creates and manages default pool for each memory type available on the device. |
507 | Default memory pool automatically grows in size. |
508 | Size of allocated blocks is also variable and managed automatically. |
509 | |
510 | You can create custom pool and allocate memory out of it. |
511 | It can be useful if you want to: |
512 | |
513 | - Keep certain kind of allocations separate from others. |
514 | - Enforce particular, fixed size of Vulkan memory blocks. |
515 | - Limit maximum amount of Vulkan memory allocated for that pool. |
516 | - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool. |
517 | |
518 | To use custom memory pools: |
519 | |
520 | -# Fill VmaPoolCreateInfo structure. |
521 | -# Call vmaCreatePool() to obtain #VmaPool handle. |
522 | -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle. |
523 | You don't need to specify any other parameters of this structure, like `usage`. |
524 | |
525 | Example: |
526 | |
527 | \code |
528 | // Create a pool that can have at most 2 blocks, 128 MiB each. |
529 | VmaPoolCreateInfo poolCreateInfo = {}; |
530 | poolCreateInfo.memoryTypeIndex = ... |
531 | poolCreateInfo.blockSize = 128ull * 1024 * 1024; |
532 | poolCreateInfo.maxBlockCount = 2; |
533 | |
534 | VmaPool pool; |
535 | vmaCreatePool(allocator, &poolCreateInfo, &pool); |
536 | |
537 | // Allocate a buffer out of it. |
538 | VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
539 | bufCreateInfo.size = 1024; |
540 | bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
541 | |
542 | VmaAllocationCreateInfo allocCreateInfo = {}; |
543 | allocCreateInfo.pool = pool; |
544 | |
545 | VkBuffer buf; |
546 | VmaAllocation alloc; |
547 | VmaAllocationInfo allocInfo; |
548 | vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); |
549 | \endcode |
550 | |
551 | You have to free all allocations made from this pool before destroying it. |
552 | |
553 | \code |
554 | vmaDestroyBuffer(allocator, buf, alloc); |
555 | vmaDestroyPool(allocator, pool); |
556 | \endcode |
557 | |
558 | \section custom_memory_pools_MemTypeIndex Choosing memory type index |
559 | |
560 | When creating a pool, you must explicitly specify memory type index. |
561 | To find the one suitable for your buffers or images, you can use helper functions |
562 | vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo(). |
563 | You need to provide structures with example parameters of buffers or images |
564 | that you are going to create in that pool. |
565 | |
566 | \code |
567 | VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
568 | exampleBufCreateInfo.size = 1024; // Whatever. |
569 | exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed. |
570 | |
571 | VmaAllocationCreateInfo allocCreateInfo = {}; |
572 | allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed. |
573 | |
574 | uint32_t memTypeIndex; |
575 | vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex); |
576 | |
577 | VmaPoolCreateInfo poolCreateInfo = {}; |
578 | poolCreateInfo.memoryTypeIndex = memTypeIndex; |
579 | // ... |
580 | \endcode |
581 | |
582 | When creating buffers/images allocated in that pool, provide following parameters: |
583 | |
584 | - `VkBufferCreateInfo`: Prefer to pass same parameters as above. |
585 | Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior. |
586 | Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers |
587 | or the other way around. |
588 | - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member. |
589 | Other members are ignored anyway. |
590 | |
591 | \section linear_algorithm Linear allocation algorithm |
592 | |
593 | Each Vulkan memory block managed by this library has accompanying metadata that |
594 | keeps track of used and unused regions. By default, the metadata structure and |
595 | algorithm tries to find best place for new allocations among free regions to |
596 | optimize memory usage. This way you can allocate and free objects in any order. |
597 | |
598 | ![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png) |
599 | |
600 | Sometimes there is a need to use simpler, linear allocation algorithm. You can |
601 | create custom pool that uses such algorithm by adding flag |
602 | #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating |
603 | #VmaPool object. Then an alternative metadata management is used. It always |
604 | creates new allocations after last one and doesn't reuse free regions after |
605 | allocations freed in the middle. It results in better allocation performance and |
606 | less memory consumed by metadata. |
607 | |
608 | ![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png) |
609 | |
610 | With this one flag, you can create a custom pool that can be used in many ways: |
611 | free-at-once, stack, double stack, and ring buffer. See below for details. |
612 | |
613 | \subsection linear_algorithm_free_at_once Free-at-once |
614 | |
615 | In a pool that uses linear algorithm, you still need to free all the allocations |
616 | individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free |
617 | them in any order. New allocations are always made after last one - free space |
618 | in the middle is not reused. However, when you release all the allocation and |
619 | the pool becomes empty, allocation starts from the beginning again. This way you |
620 | can use linear algorithm to speed up creation of allocations that you are going |
621 | to release all at once. |
622 | |
623 | ![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png) |
624 | |
625 | This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount |
626 | value that allows multiple memory blocks. |
627 | |
628 | \subsection linear_algorithm_stack Stack |
629 | |
630 | When you free an allocation that was created last, its space can be reused. |
631 | Thanks to this, if you always release allocations in the order opposite to their |
632 | creation (LIFO - Last In First Out), you can achieve behavior of a stack. |
633 | |
634 | ![Stack](../gfx/Linear_allocator_4_stack.png) |
635 | |
636 | This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount |
637 | value that allows multiple memory blocks. |
638 | |
639 | \subsection linear_algorithm_double_stack Double stack |
640 | |
641 | The space reserved by a custom pool with linear algorithm may be used by two |
642 | stacks: |
643 | |
644 | - First, default one, growing up from offset 0. |
645 | - Second, "upper" one, growing down from the end towards lower offsets. |
646 | |
647 | To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT |
648 | to VmaAllocationCreateInfo::flags. |
649 | |
650 | ![Double stack](../gfx/Linear_allocator_7_double_stack.png) |
651 | |
652 | Double stack is available only in pools with one memory block - |
653 | VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. |
654 | |
655 | When the two stacks' ends meet so there is not enough space between them for a |
656 | new allocation, such allocation fails with usual |
657 | `VK_ERROR_OUT_OF_DEVICE_MEMORY` error. |
658 | |
659 | \subsection linear_algorithm_ring_buffer Ring buffer |
660 | |
661 | When you free some allocations from the beginning and there is not enough free space |
662 | for a new one at the end of a pool, allocator's "cursor" wraps around to the |
663 | beginning and starts allocation there. Thanks to this, if you always release |
664 | allocations in the same order as you created them (FIFO - First In First Out), |
665 | you can achieve behavior of a ring buffer / queue. |
666 | |
667 | ![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png) |
668 | |
669 | Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer. |
670 | If there is not enough free space for a new allocation, but existing allocations |
671 | from the front of the queue can become lost, they become lost and the allocation |
672 | succeeds. |
673 | |
674 | ![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png) |
675 | |
676 | Ring buffer is available only in pools with one memory block - |
677 | VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. |
678 | |
679 | \section buddy_algorithm Buddy allocation algorithm |
680 | |
681 | There is another allocation algorithm that can be used with custom pools, called |
682 | "buddy". Its internal data structure is based on a tree of blocks, each having |
683 | size that is a power of two and a half of its parent's size. When you want to |
684 | allocate memory of certain size, a free node in the tree is located. If it's too |
685 | large, it is recursively split into two halves (called "buddies"). However, if |
686 | requested allocation size is not a power of two, the size of a tree node is |
687 | aligned up to the nearest power of two and the remaining space is wasted. When |
688 | two buddy nodes become free, they are merged back into one larger node. |
689 | |
690 | ![Buddy allocator](../gfx/Buddy_allocator.png) |
691 | |
692 | The advantage of buddy allocation algorithm over default algorithm is faster |
693 | allocation and deallocation, as well as smaller external fragmentation. The |
694 | disadvantage is more wasted space (internal fragmentation). |
695 | |
696 | For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation) |
697 | or other sources that describe this concept in general. |
698 | |
699 | To use buddy allocation algorithm with a custom pool, add flag |
700 | #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating |
701 | #VmaPool object. |
702 | |
703 | Several limitations apply to pools that use buddy algorithm: |
704 | |
705 | - It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two. |
706 | Otherwise, only largest power of two smaller than the size is used for |
707 | allocations. The remaining space always stays unused. |
708 | - [Margins](@ref debugging_memory_usage_margins) and |
709 | [corruption detection](@ref debugging_memory_usage_corruption_detection) |
710 | don't work in such pools. |
711 | - [Lost allocations](@ref lost_allocations) don't work in such pools. You can |
712 | use them, but they never become lost. Support may be added in the future. |
713 | - [Defragmentation](@ref defragmentation) doesn't work with allocations made from |
714 | such pool. |
715 | |
716 | \page defragmentation Defragmentation |
717 | |
718 | Interleaved allocations and deallocations of many objects of varying size can |
719 | cause fragmentation over time, which can lead to a situation where the library is unable |
720 | to find a continuous range of free memory for a new allocation despite there is |
721 | enough free space, just scattered across many small free ranges between existing |
722 | allocations. |
723 | |
724 | To mitigate this problem, you can use defragmentation feature: |
725 | structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd(). |
726 | Given set of allocations, |
727 | this function can move them to compact used memory, ensure more continuous free |
728 | space and possibly also free some `VkDeviceMemory` blocks. |
729 | |
730 | What the defragmentation does is: |
731 | |
732 | - Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset. |
733 | After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or |
734 | VmaAllocationInfo::offset changes. You must query them again using |
735 | vmaGetAllocationInfo() if you need them. |
736 | - Moves actual data in memory. |
737 | |
738 | What it doesn't do, so you need to do it yourself: |
739 | |
740 | - Recreate buffers and images that were bound to allocations that were defragmented and |
741 | bind them with their new places in memory. |
742 | You must use `vkDestroyBuffer()`, `vkDestroyImage()`, |
743 | `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(), |
744 | vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to |
745 | destroy or create allocation objects! |
746 | - Recreate views and update descriptors that point to these buffers and images. |
747 | |
748 | \section defragmentation_cpu Defragmenting CPU memory |
749 | |
750 | Following example demonstrates how you can run defragmentation on CPU. |
751 | Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented. |
752 | Others are ignored. |
753 | |
754 | The way it works is: |
755 | |
756 | - It temporarily maps entire memory blocks when necessary. |
757 | - It moves data using `memmove()` function. |
758 | |
759 | \code |
760 | // Given following variables already initialized: |
761 | VkDevice device; |
762 | VmaAllocator allocator; |
763 | std::vector<VkBuffer> buffers; |
764 | std::vector<VmaAllocation> allocations; |
765 | |
766 | |
767 | const uint32_t allocCount = (uint32_t)allocations.size(); |
768 | std::vector<VkBool32> allocationsChanged(allocCount); |
769 | |
770 | VmaDefragmentationInfo2 defragInfo = {}; |
771 | defragInfo.allocationCount = allocCount; |
772 | defragInfo.pAllocations = allocations.data(); |
773 | defragInfo.pAllocationsChanged = allocationsChanged.data(); |
774 | defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit. |
775 | defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit. |
776 | |
777 | VmaDefragmentationContext defragCtx; |
778 | vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx); |
779 | vmaDefragmentationEnd(allocator, defragCtx); |
780 | |
781 | for(uint32_t i = 0; i < allocCount; ++i) |
782 | { |
783 | if(allocationsChanged[i]) |
784 | { |
785 | // Destroy buffer that is immutably bound to memory region which is no longer valid. |
786 | vkDestroyBuffer(device, buffers[i], nullptr); |
787 | |
788 | // Create new buffer with same parameters. |
789 | VkBufferCreateInfo bufferInfo = ...; |
790 | vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]); |
791 | |
792 | // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning. |
793 | |
794 | // Bind new buffer to new memory region. Data contained in it is already moved. |
795 | VmaAllocationInfo allocInfo; |
796 | vmaGetAllocationInfo(allocator, allocations[i], &allocInfo); |
797 | vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset); |
798 | } |
799 | } |
800 | \endcode |
801 | |
802 | Setting VmaDefragmentationInfo2::pAllocationsChanged is optional. |
803 | This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index |
804 | has been modified during defragmentation. |
805 | You can pass null, but you then need to query every allocation passed to defragmentation |
806 | for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it. |
807 | |
808 | If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools), |
809 | you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools |
810 | instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations |
811 | to defragment all allocations in given pools. |
812 | You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case. |
813 | You can also combine both methods. |
814 | |
815 | \section defragmentation_gpu Defragmenting GPU memory |
816 | |
817 | It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`. |
818 | To do that, you need to pass a command buffer that meets requirements as described in |
819 | VmaDefragmentationInfo2::commandBuffer. The way it works is: |
820 | |
821 | - It creates temporary buffers and binds them to entire memory blocks when necessary. |
822 | - It issues `vkCmdCopyBuffer()` to passed command buffer. |
823 | |
824 | Example: |
825 | |
826 | \code |
827 | // Given following variables already initialized: |
828 | VkDevice device; |
829 | VmaAllocator allocator; |
830 | VkCommandBuffer commandBuffer; |
831 | std::vector<VkBuffer> buffers; |
832 | std::vector<VmaAllocation> allocations; |
833 | |
834 | |
835 | const uint32_t allocCount = (uint32_t)allocations.size(); |
836 | std::vector<VkBool32> allocationsChanged(allocCount); |
837 | |
838 | VkCommandBufferBeginInfo cmdBufBeginInfo = ...; |
839 | vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo); |
840 | |
841 | VmaDefragmentationInfo2 defragInfo = {}; |
842 | defragInfo.allocationCount = allocCount; |
843 | defragInfo.pAllocations = allocations.data(); |
844 | defragInfo.pAllocationsChanged = allocationsChanged.data(); |
845 | defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time. |
846 | defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time. |
847 | defragInfo.commandBuffer = commandBuffer; |
848 | |
849 | VmaDefragmentationContext defragCtx; |
850 | vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx); |
851 | |
852 | vkEndCommandBuffer(commandBuffer); |
853 | |
854 | // Submit commandBuffer. |
855 | // Wait for a fence that ensures commandBuffer execution finished. |
856 | |
857 | vmaDefragmentationEnd(allocator, defragCtx); |
858 | |
859 | for(uint32_t i = 0; i < allocCount; ++i) |
860 | { |
861 | if(allocationsChanged[i]) |
862 | { |
863 | // Destroy buffer that is immutably bound to memory region which is no longer valid. |
864 | vkDestroyBuffer(device, buffers[i], nullptr); |
865 | |
866 | // Create new buffer with same parameters. |
867 | VkBufferCreateInfo bufferInfo = ...; |
868 | vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]); |
869 | |
870 | // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning. |
871 | |
872 | // Bind new buffer to new memory region. Data contained in it is already moved. |
873 | VmaAllocationInfo allocInfo; |
874 | vmaGetAllocationInfo(allocator, allocations[i], &allocInfo); |
875 | vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset); |
876 | } |
877 | } |
878 | \endcode |
879 | |
880 | You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters. |
881 | The library automatically chooses best method to defragment each memory pool. |
882 | |
883 | You may try not to block your entire program to wait until defragmentation finishes, |
884 | but do it in the background, as long as you carefully fullfill requirements described |
885 | in function vmaDefragmentationBegin(). |
886 | |
887 | \section defragmentation_additional_notes Additional notes |
888 | |
889 | While using defragmentation, you may experience validation layer warnings, which you just need to ignore. |
890 | See [Validation layer warnings](@ref general_considerations_validation_layer_warnings). |
891 | |
892 | If you defragment allocations bound to images, these images should be created with |
893 | `VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same |
894 | parameters and pointing to data copied to another memory region will interpret |
895 | its contents consistently. Otherwise you may experience corrupted data on some |
896 | implementations, e.g. due to different pixel swizzling used internally by the graphics driver. |
897 | |
898 | If you defragment allocations bound to images, new images to be bound to new |
899 | memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED` |
900 | and then transitioned to their original layout from before defragmentation using |
901 | an image memory barrier. |
902 | |
903 | Please don't expect memory to be fully compacted after defragmentation. |
904 | Algorithms inside are based on some heuristics that try to maximize number of Vulkan |
905 | memory blocks to make totally empty to release them, as well as to maximimze continuous |
906 | empty space inside remaining blocks, while minimizing the number and size of allocations that |
907 | need to be moved. Some fragmentation may still remain - this is normal. |
908 | |
909 | \section defragmentation_custom_algorithm Writing custom defragmentation algorithm |
910 | |
911 | If you want to implement your own, custom defragmentation algorithm, |
912 | there is infrastructure prepared for that, |
913 | but it is not exposed through the library API - you need to hack its source code. |
914 | Here are steps needed to do this: |
915 | |
916 | -# Main thing you need to do is to define your own class derived from base abstract |
917 | class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods. |
918 | See definition and comments of this class for details. |
919 | -# Your code needs to interact with device memory block metadata. |
920 | If you need more access to its data than it's provided by its public interface, |
921 | declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`. |
922 | -# If you want to create a flag that would enable your algorithm or pass some additional |
923 | flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in |
924 | VmaDefragmentationInfo2::flags. |
925 | -# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object |
926 | of your new class whenever needed. |
927 | |
928 | |
929 | \page lost_allocations Lost allocations |
930 | |
931 | If your game oversubscribes video memory, if may work OK in previous-generation |
932 | graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically |
933 | paged to system RAM. In Vulkan you can't do it because when you run out of |
934 | memory, an allocation just fails. If you have more data (e.g. textures) that can |
935 | fit into VRAM and you don't need it all at once, you may want to upload them to |
936 | GPU on demand and "push out" ones that are not used for a long time to make room |
937 | for the new ones, effectively using VRAM (or a cartain memory pool) as a form of |
938 | cache. Vulkan Memory Allocator can help you with that by supporting a concept of |
939 | "lost allocations". |
940 | |
941 | To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
942 | flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to |
943 | such allocation in every new frame, you need to query it if it's not lost. |
944 | To check it, call vmaTouchAllocation(). |
945 | If the allocation is lost, you should not use it or buffer/image bound to it. |
946 | You mustn't forget to destroy this allocation and this buffer/image. |
947 | vmaGetAllocationInfo() can also be used for checking status of the allocation. |
948 | Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`. |
949 | |
950 | To create an allocation that can make some other allocations lost to make room |
951 | for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will |
952 | usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and |
953 | #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time. |
954 | |
955 | Warning! Current implementation uses quite naive, brute force algorithm, |
956 | which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT |
957 | flag quite slow. A new, more optimal algorithm and data structure to speed this |
958 | up is planned for the future. |
959 | |
960 | <b>Q: When interleaving creation of new allocations with usage of existing ones, |
961 | how do you make sure that an allocation won't become lost while it's used in the |
962 | current frame?</b> |
963 | |
964 | It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation |
965 | status/parameters and checks whether it's not lost, but when it's not, it also |
966 | atomically marks it as used in the current frame, which makes it impossible to |
967 | become lost in that frame. It uses lockless algorithm, so it works fast and |
968 | doesn't involve locking any internal mutex. |
969 | |
970 | <b>Q: What if my allocation may still be in use by the GPU when it's rendering a |
971 | previous frame while I already submit new frame on the CPU?</b> |
972 | |
973 | You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not |
974 | become lost for a number of additional frames back from the current one by |
975 | specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default |
976 | memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool). |
977 | |
978 | <b>Q: How do you inform the library when new frame starts?</b> |
979 | |
980 | You need to call function vmaSetCurrentFrameIndex(). |
981 | |
982 | Example code: |
983 | |
984 | \code |
985 | struct MyBuffer |
986 | { |
987 | VkBuffer m_Buf = nullptr; |
988 | VmaAllocation m_Alloc = nullptr; |
989 | |
990 | // Called when the buffer is really needed in the current frame. |
991 | void EnsureBuffer(); |
992 | }; |
993 | |
994 | void MyBuffer::EnsureBuffer() |
995 | { |
996 | // Buffer has been created. |
997 | if(m_Buf != VK_NULL_HANDLE) |
998 | { |
999 | // Check if its allocation is not lost + mark it as used in current frame. |
1000 | if(vmaTouchAllocation(allocator, m_Alloc)) |
1001 | { |
1002 | // It's all OK - safe to use m_Buf. |
1003 | return; |
1004 | } |
1005 | } |
1006 | |
1007 | // Buffer not yet exists or lost - destroy and recreate it. |
1008 | |
1009 | vmaDestroyBuffer(allocator, m_Buf, m_Alloc); |
1010 | |
1011 | VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
1012 | bufCreateInfo.size = 1024; |
1013 | bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
1014 | |
1015 | VmaAllocationCreateInfo allocCreateInfo = {}; |
1016 | allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
1017 | allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT | |
1018 | VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT; |
1019 | |
1020 | vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr); |
1021 | } |
1022 | \endcode |
1023 | |
1024 | When using lost allocations, you may see some Vulkan validation layer warnings |
1025 | about overlapping regions of memory bound to different kinds of buffers and |
1026 | images. This is still valid as long as you implement proper handling of lost |
1027 | allocations (like in the example above) and don't use them. |
1028 | |
1029 | You can create an allocation that is already in lost state from the beginning using function |
1030 | vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null. |
1031 | |
1032 | You can call function vmaMakePoolAllocationsLost() to set all eligible allocations |
1033 | in a specified custom pool to lost state. |
1034 | Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back |
1035 | cannot become lost. |
1036 | |
1037 | <b>Q: Can I touch allocation that cannot become lost?</b> |
1038 | |
1039 | Yes, although it has no visible effect. |
1040 | Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index |
1041 | also for allocations that cannot become lost, but the only way to observe it is to dump |
1042 | internal allocator state using vmaBuildStatsString(). |
1043 | You can use this feature for debugging purposes to explicitly mark allocations that you use |
1044 | in current frame and then analyze JSON dump to see for how long each allocation stays unused. |
1045 | |
1046 | |
1047 | \page statistics Statistics |
1048 | |
1049 | This library contains functions that return information about its internal state, |
1050 | especially the amount of memory allocated from Vulkan. |
1051 | Please keep in mind that these functions need to traverse all internal data structures |
1052 | to gather these information, so they may be quite time-consuming. |
1053 | Don't call them too often. |
1054 | |
1055 | \section statistics_numeric_statistics Numeric statistics |
1056 | |
1057 | You can query for overall statistics of the allocator using function vmaCalculateStats(). |
1058 | Information are returned using structure #VmaStats. |
1059 | It contains #VmaStatInfo - number of allocated blocks, number of allocations |
1060 | (occupied ranges in these blocks), number of unused (free) ranges in these blocks, |
1061 | number of bytes used and unused (but still allocated from Vulkan) and other information. |
1062 | They are summed across memory heaps, memory types and total for whole allocator. |
1063 | |
1064 | You can query for statistics of a custom pool using function vmaGetPoolStats(). |
1065 | Information are returned using structure #VmaPoolStats. |
1066 | |
1067 | You can query for information about specific allocation using function vmaGetAllocationInfo(). |
1068 | It fill structure #VmaAllocationInfo. |
1069 | |
1070 | \section statistics_json_dump JSON dump |
1071 | |
1072 | You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString(). |
1073 | The result is guaranteed to be correct JSON. |
1074 | It uses ANSI encoding. |
1075 | Any strings provided by user (see [Allocation names](@ref allocation_names)) |
1076 | are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding, |
1077 | this JSON string can be treated as using this encoding. |
1078 | It must be freed using function vmaFreeStatsString(). |
1079 | |
1080 | The format of this JSON string is not part of official documentation of the library, |
1081 | but it will not change in backward-incompatible way without increasing library major version number |
1082 | and appropriate mention in changelog. |
1083 | |
1084 | The JSON string contains all the data that can be obtained using vmaCalculateStats(). |
1085 | It can also contain detailed map of allocated memory blocks and their regions - |
1086 | free and occupied by allocations. |
1087 | This allows e.g. to visualize the memory or assess fragmentation. |
1088 | |
1089 | |
1090 | \page allocation_annotation Allocation names and user data |
1091 | |
1092 | \section allocation_user_data Allocation user data |
1093 | |
1094 | You can annotate allocations with your own information, e.g. for debugging purposes. |
1095 | To do that, fill VmaAllocationCreateInfo::pUserData field when creating |
1096 | an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer, |
1097 | some handle, index, key, ordinal number or any other value that would associate |
1098 | the allocation with your custom metadata. |
1099 | |
1100 | \code |
1101 | VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
1102 | // Fill bufferInfo... |
1103 | |
1104 | MyBufferMetadata* pMetadata = CreateBufferMetadata(); |
1105 | |
1106 | VmaAllocationCreateInfo allocCreateInfo = {}; |
1107 | allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
1108 | allocCreateInfo.pUserData = pMetadata; |
1109 | |
1110 | VkBuffer buffer; |
1111 | VmaAllocation allocation; |
1112 | vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr); |
1113 | \endcode |
1114 | |
1115 | The pointer may be later retrieved as VmaAllocationInfo::pUserData: |
1116 | |
1117 | \code |
1118 | VmaAllocationInfo allocInfo; |
1119 | vmaGetAllocationInfo(allocator, allocation, &allocInfo); |
1120 | MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData; |
1121 | \endcode |
1122 | |
1123 | It can also be changed using function vmaSetAllocationUserData(). |
1124 | |
1125 | Values of (non-zero) allocations' `pUserData` are printed in JSON report created by |
1126 | vmaBuildStatsString(), in hexadecimal form. |
1127 | |
1128 | \section allocation_names Allocation names |
1129 | |
1130 | There is alternative mode available where `pUserData` pointer is used to point to |
1131 | a null-terminated string, giving a name to the allocation. To use this mode, |
1132 | set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags. |
1133 | Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to |
1134 | vmaSetAllocationUserData() must be either null or pointer to a null-terminated string. |
1135 | The library creates internal copy of the string, so the pointer you pass doesn't need |
1136 | to be valid for whole lifetime of the allocation. You can free it after the call. |
1137 | |
1138 | \code |
1139 | VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; |
1140 | // Fill imageInfo... |
1141 | |
1142 | std::string imageName = "Texture: "; |
1143 | imageName += fileName; |
1144 | |
1145 | VmaAllocationCreateInfo allocCreateInfo = {}; |
1146 | allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
1147 | allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT; |
1148 | allocCreateInfo.pUserData = imageName.c_str(); |
1149 | |
1150 | VkImage image; |
1151 | VmaAllocation allocation; |
1152 | vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr); |
1153 | \endcode |
1154 | |
1155 | The value of `pUserData` pointer of the allocation will be different than the one |
1156 | you passed when setting allocation's name - pointing to a buffer managed |
1157 | internally that holds copy of the string. |
1158 | |
1159 | \code |
1160 | VmaAllocationInfo allocInfo; |
1161 | vmaGetAllocationInfo(allocator, allocation, &allocInfo); |
1162 | const char* imageName = (const char*)allocInfo.pUserData; |
1163 | printf("Image name: %s\n", imageName); |
1164 | \endcode |
1165 | |
1166 | That string is also printed in JSON report created by vmaBuildStatsString(). |
1167 | |
1168 | |
1169 | \page debugging_memory_usage Debugging incorrect memory usage |
1170 | |
1171 | If you suspect a bug with memory usage, like usage of uninitialized memory or |
1172 | memory being overwritten out of bounds of an allocation, |
1173 | you can use debug features of this library to verify this. |
1174 | |
1175 | \section debugging_memory_usage_initialization Memory initialization |
1176 | |
1177 | If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used, |
1178 | you can enable automatic memory initialization to verify this. |
1179 | To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1. |
1180 | |
1181 | \code |
1182 | #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 |
1183 | #include "vk_mem_alloc.h" |
1184 | \endcode |
1185 | |
1186 | It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`. |
1187 | Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`. |
1188 | Memory is automatically mapped and unmapped if necessary. |
1189 | |
1190 | If you find these values while debugging your program, good chances are that you incorrectly |
1191 | read Vulkan memory that is allocated but not initialized, or already freed, respectively. |
1192 | |
1193 | Memory initialization works only with memory types that are `HOST_VISIBLE`. |
1194 | It works also with dedicated allocations. |
1195 | It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag, |
1196 | as they cannot be mapped. |
1197 | |
1198 | \section debugging_memory_usage_margins Margins |
1199 | |
1200 | By default, allocations are laid out in memory blocks next to each other if possible |
1201 | (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`). |
1202 | |
1203 | ![Allocations without margin](../gfx/Margins_1.png) |
1204 | |
1205 | Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified |
1206 | number of bytes as a margin before and after every allocation. |
1207 | |
1208 | \code |
1209 | #define VMA_DEBUG_MARGIN 16 |
1210 | #include "vk_mem_alloc.h" |
1211 | \endcode |
1212 | |
1213 | ![Allocations with margin](../gfx/Margins_2.png) |
1214 | |
1215 | If your bug goes away after enabling margins, it means it may be caused by memory |
1216 | being overwritten outside of allocation boundaries. It is not 100% certain though. |
1217 | Change in application behavior may also be caused by different order and distribution |
1218 | of allocations across memory blocks after margins are applied. |
1219 | |
1220 | The margin is applied also before first and after last allocation in a block. |
1221 | It may occur only once between two adjacent allocations. |
1222 | |
1223 | Margins work with all types of memory. |
1224 | |
1225 | Margin is applied only to allocations made out of memory blocks and not to dedicated |
1226 | allocations, which have their own memory block of specific size. |
1227 | It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag |
1228 | or those automatically decided to put into dedicated allocations, e.g. due to its |
1229 | large size or recommended by VK_KHR_dedicated_allocation extension. |
1230 | Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag. |
1231 | |
1232 | Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space. |
1233 | |
1234 | Note that enabling margins increases memory usage and fragmentation. |
1235 | |
1236 | \section debugging_memory_usage_corruption_detection Corruption detection |
1237 | |
1238 | You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation |
1239 | of contents of the margins. |
1240 | |
1241 | \code |
1242 | #define VMA_DEBUG_MARGIN 16 |
1243 | #define VMA_DEBUG_DETECT_CORRUPTION 1 |
1244 | #include "vk_mem_alloc.h" |
1245 | \endcode |
1246 | |
1247 | When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN` |
1248 | (it must be multiply of 4) before and after every allocation is filled with a magic number. |
1249 | This idea is also know as "canary". |
1250 | Memory is automatically mapped and unmapped if necessary. |
1251 | |
1252 | This number is validated automatically when the allocation is destroyed. |
1253 | If it's not equal to the expected value, `VMA_ASSERT()` is executed. |
1254 | It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation, |
1255 | which indicates a serious bug. |
1256 | |
1257 | You can also explicitly request checking margins of all allocations in all memory blocks |
1258 | that belong to specified memory types by using function vmaCheckCorruption(), |
1259 | or in memory blocks that belong to specified custom pool, by using function |
1260 | vmaCheckPoolCorruption(). |
1261 | |
1262 | Margin validation (corruption detection) works only for memory types that are |
1263 | `HOST_VISIBLE` and `HOST_COHERENT`. |
1264 | |
1265 | |
1266 | \page record_and_replay Record and replay |
1267 | |
1268 | \section record_and_replay_introduction Introduction |
1269 | |
1270 | While using the library, sequence of calls to its functions together with their |
1271 | parameters can be recorded to a file and later replayed using standalone player |
1272 | application. It can be useful to: |
1273 | |
1274 | - Test correctness - check if same sequence of calls will not cause crash or |
1275 | failures on a target platform. |
1276 | - Gather statistics - see number of allocations, peak memory usage, number of |
1277 | calls etc. |
1278 | - Benchmark performance - see how much time it takes to replay the whole |
1279 | sequence. |
1280 | |
1281 | \section record_and_replay_usage Usage |
1282 | |
1283 | <b>To record sequence of calls to a file:</b> Fill in |
1284 | VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator |
1285 | object. File is opened and written during whole lifetime of the allocator. |
1286 | |
1287 | <b>To replay file:</b> Use VmaReplay - standalone command-line program. |
1288 | Precompiled binary can be found in "bin" directory. |
1289 | Its source can be found in "src/VmaReplay" directory. |
1290 | Its project is generated by Premake. |
1291 | Command line syntax is printed when the program is launched without parameters. |
1292 | Basic usage: |
1293 | |
1294 | VmaReplay.exe MyRecording.csv |
1295 | |
1296 | <b>Documentation of file format</b> can be found in file: "docs/Recording file format.md". |
1297 | It's a human-readable, text file in CSV format (Comma Separated Values). |
1298 | |
1299 | \section record_and_replay_additional_considerations Additional considerations |
1300 | |
1301 | - Replaying file that was recorded on a different GPU (with different parameters |
1302 | like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different |
1303 | set of memory heaps and types) may give different performance and memory usage |
1304 | results, as well as issue some warnings and errors. |
1305 | - Current implementation of recording in VMA, as well as VmaReplay application, is |
1306 | coded and tested only on Windows. Inclusion of recording code is driven by |
1307 | `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to |
1308 | add. Contributions are welcomed. |
1309 | - Currently calls to vmaDefragment() function are not recorded. |
1310 | |
1311 | |
1312 | \page usage_patterns Recommended usage patterns |
1313 | |
1314 | See also slides from talk: |
1315 | [Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New) |
1316 | |
1317 | |
1318 | \section usage_patterns_simple Simple patterns |
1319 | |
1320 | \subsection usage_patterns_simple_render_targets Render targets |
1321 | |
1322 | <b>When:</b> |
1323 | Any resources that you frequently write and read on GPU, |
1324 | e.g. images used as color attachments (aka "render targets"), depth-stencil attachments, |
1325 | images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)"). |
1326 | |
1327 | <b>What to do:</b> |
1328 | Create them in video memory that is fastest to access from GPU using |
1329 | #VMA_MEMORY_USAGE_GPU_ONLY. |
1330 | |
1331 | Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension |
1332 | and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, |
1333 | especially if they are large or if you plan to destroy and recreate them e.g. when |
1334 | display resolution changes. |
1335 | Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later. |
1336 | |
1337 | \subsection usage_patterns_simple_immutable_resources Immutable resources |
1338 | |
1339 | <b>When:</b> |
1340 | Any resources that you fill on CPU only once (aka "immutable") or infrequently |
1341 | and then read frequently on GPU, |
1342 | e.g. textures, vertex and index buffers, constant buffers that don't change often. |
1343 | |
1344 | <b>What to do:</b> |
1345 | Create them in video memory that is fastest to access from GPU using |
1346 | #VMA_MEMORY_USAGE_GPU_ONLY. |
1347 | |
1348 | To initialize content of such resource, create a CPU-side (aka "staging") copy of it |
1349 | in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it, |
1350 | and submit a transfer from it to the GPU resource. |
1351 | You can keep the staging copy if you need it for another upload transfer in the future. |
1352 | If you don't, you can destroy it or reuse this buffer for uploading different resource |
1353 | after the transfer finishes. |
1354 | |
1355 | Prefer to create just buffers in system memory rather than images, even for uploading textures. |
1356 | Use `vkCmdCopyBufferToImage()`. |
1357 | Dont use images with `VK_IMAGE_TILING_LINEAR`. |
1358 | |
1359 | \subsection usage_patterns_dynamic_resources Dynamic resources |
1360 | |
1361 | <b>When:</b> |
1362 | Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call, |
1363 | written on CPU, read on GPU. |
1364 | |
1365 | <b>What to do:</b> |
1366 | Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU. |
1367 | You can map it and write to it directly on CPU, as well as read from it on GPU. |
1368 | |
1369 | This is a more complex situation. Different solutions are possible, |
1370 | and the best one depends on specific GPU type, but you can use this simple approach for the start. |
1371 | Prefer to write to such resource sequentially (e.g. using `memcpy`). |
1372 | Don't perform random access or any reads from it on CPU, as it may be very slow. |
1373 | |
1374 | \subsection usage_patterns_readback Readback |
1375 | |
1376 | <b>When:</b> |
1377 | Resources that contain data written by GPU that you want to read back on CPU, |
1378 | e.g. results of some computations. |
1379 | |
1380 | <b>What to do:</b> |
1381 | Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU. |
1382 | You can write to them directly on GPU, as well as map and read them on CPU. |
1383 | |
1384 | \section usage_patterns_advanced Advanced patterns |
1385 | |
1386 | \subsection usage_patterns_integrated_graphics Detecting integrated graphics |
1387 | |
1388 | You can support integrated graphics (like Intel HD Graphics, AMD APU) better |
1389 | by detecting it in Vulkan. |
1390 | To do it, call `vkGetPhysicalDeviceProperties()`, inspect |
1391 | `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`. |
1392 | When you find it, you can assume that memory is unified and all memory types are comparably fast |
1393 | to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. |
1394 | |
1395 | You can then sum up sizes of all available memory heaps and treat them as useful for |
1396 | your GPU resources, instead of only `DEVICE_LOCAL` ones. |
1397 | You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them |
1398 | directly instead of submitting explicit transfer (see below). |
1399 | |
1400 | \subsection usage_patterns_direct_vs_transfer Direct access versus transfer |
1401 | |
1402 | For resources that you frequently write on CPU and read on GPU, many solutions are possible: |
1403 | |
1404 | -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY, |
1405 | second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time. |
1406 | -# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU, |
1407 | read it directly on GPU. |
1408 | -# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU, |
1409 | read it directly on GPU. |
1410 | |
1411 | Which solution is the most efficient depends on your resource and especially on the GPU. |
1412 | It is best to measure it and then make the decision. |
1413 | Some general recommendations: |
1414 | |
1415 | - On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead |
1416 | related to using a second copy and making transfer. |
1417 | - For small resources (e.g. constant buffers) use (2). |
1418 | Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable. |
1419 | Even if the resource ends up in system memory, its data may be cached on GPU after first |
1420 | fetch over PCIe bus. |
1421 | - For larger resources (e.g. textures), decide between (1) and (2). |
1422 | You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is |
1423 | both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1). |
1424 | |
1425 | Similarly, for resources that you frequently write on GPU and read on CPU, multiple |
1426 | solutions are possible: |
1427 | |
1428 | -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY, |
1429 | second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time. |
1430 | -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU, |
1431 | map it and read it on CPU. |
1432 | |
1433 | You should take some measurements to decide which option is faster in case of your specific |
1434 | resource. |
1435 | |
1436 | If you don't want to specialize your code for specific types of GPUs, you can still make |
1437 | an simple optimization for cases when your resource ends up in mappable memory to use it |
1438 | directly in this case instead of creating CPU-side staging copy. |
1439 | For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable). |
1440 | |
1441 | |
1442 | \page configuration Configuration |
1443 | |
1444 | Please check "CONFIGURATION SECTION" in the code to find macros that you can define |
1445 | before each include of this file or change directly in this file to provide |
1446 | your own implementation of basic facilities like assert, `min()` and `max()` functions, |
1447 | mutex, atomic etc. |
1448 | The library uses its own implementation of containers by default, but you can switch to using |
1449 | STL containers instead. |
1450 | |
1451 | \section config_Vulkan_functions Pointers to Vulkan functions |
1452 | |
1453 | The library uses Vulkan functions straight from the `vulkan.h` header by default. |
1454 | If you want to provide your own pointers to these functions, e.g. fetched using |
1455 | `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`: |
1456 | |
1457 | -# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`. |
1458 | -# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions. |
1459 | |
1460 | \section custom_memory_allocator Custom host memory allocator |
1461 | |
1462 | If you use custom allocator for CPU memory rather than default operator `new` |
1463 | and `delete` from C++, you can make this library using your allocator as well |
1464 | by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These |
1465 | functions will be passed to Vulkan, as well as used by the library itself to |
1466 | make any CPU-side allocations. |
1467 | |
1468 | \section allocation_callbacks Device memory allocation callbacks |
1469 | |
1470 | The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally. |
1471 | You can setup callbacks to be informed about these calls, e.g. for the purpose |
1472 | of gathering some statistics. To do it, fill optional member |
1473 | VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. |
1474 | |
1475 | \section heap_memory_limit Device heap memory limit |
1476 | |
1477 | If you want to test how your program behaves with limited amount of Vulkan device |
1478 | memory available without switching your graphics card to one that really has |
1479 | smaller VRAM, you can use a feature of this library intended for this purpose. |
1480 | To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit. |
1481 | |
1482 | |
1483 | |
1484 | \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation |
1485 | |
1486 | VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve |
1487 | performance on some GPUs. It augments Vulkan API with possibility to query |
1488 | driver whether it prefers particular buffer or image to have its own, dedicated |
1489 | allocation (separate `VkDeviceMemory` block) for better efficiency - to be able |
1490 | to do some internal optimizations. |
1491 | |
1492 | The extension is supported by this library. It will be used automatically when |
1493 | enabled. To enable it: |
1494 | |
1495 | 1 . When creating Vulkan device, check if following 2 device extensions are |
1496 | supported (call `vkEnumerateDeviceExtensionProperties()`). |
1497 | If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`). |
1498 | |
1499 | - VK_KHR_get_memory_requirements2 |
1500 | - VK_KHR_dedicated_allocation |
1501 | |
1502 | If you enabled these extensions: |
1503 | |
1504 | 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating |
1505 | your #VmaAllocator`to inform the library that you enabled required extensions |
1506 | and you want the library to use them. |
1507 | |
1508 | \code |
1509 | allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; |
1510 | |
1511 | vmaCreateAllocator(&allocatorInfo, &allocator); |
1512 | \endcode |
1513 | |
1514 | That's all. The extension will be automatically used whenever you create a |
1515 | buffer using vmaCreateBuffer() or image using vmaCreateImage(). |
1516 | |
1517 | When using the extension together with Vulkan Validation Layer, you will receive |
1518 | warnings like this: |
1519 | |
1520 | vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer. |
1521 | |
1522 | It is OK, you should just ignore it. It happens because you use function |
1523 | `vkGetBufferMemoryRequirements2KHR()` instead of standard |
1524 | `vkGetBufferMemoryRequirements()`, while the validation layer seems to be |
1525 | unaware of it. |
1526 | |
1527 | To learn more about this extension, see: |
1528 | |
1529 | - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation) |
1530 | - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5) |
1531 | |
1532 | |
1533 | |
1534 | \page general_considerations General considerations |
1535 | |
1536 | \section general_considerations_thread_safety Thread safety |
1537 | |
1538 | - The library has no global state, so separate #VmaAllocator objects can be used |
1539 | independently. |
1540 | There should be no need to create multiple such objects though - one per `VkDevice` is enough. |
1541 | - By default, all calls to functions that take #VmaAllocator as first parameter |
1542 | are safe to call from multiple threads simultaneously because they are |
1543 | synchronized internally when needed. |
1544 | - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT |
1545 | flag, calls to functions that take such #VmaAllocator object must be |
1546 | synchronized externally. |
1547 | - Access to a #VmaAllocation object must be externally synchronized. For example, |
1548 | you must not call vmaGetAllocationInfo() and vmaMapMemory() from different |
1549 | threads at the same time if you pass the same #VmaAllocation object to these |
1550 | functions. |
1551 | |
1552 | \section general_considerations_validation_layer_warnings Validation layer warnings |
1553 | |
1554 | When using this library, you can meet following types of warnings issued by |
1555 | Vulkan validation layer. They don't necessarily indicate a bug, so you may need |
1556 | to just ignore them. |
1557 | |
1558 | - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.* |
1559 | - It happens when VK_KHR_dedicated_allocation extension is enabled. |
1560 | `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it. |
1561 | - *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.* |
1562 | - It happens when you map a buffer or image, because the library maps entire |
1563 | `VkDeviceMemory` block, where different types of images and buffers may end |
1564 | up together, especially on GPUs with unified memory like Intel. |
1565 | - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.* |
1566 | - It happens when you use lost allocations, and a new image or buffer is |
1567 | created in place of an existing object that bacame lost. |
1568 | - It may happen also when you use [defragmentation](@ref defragmentation). |
1569 | |
1570 | \section general_considerations_allocation_algorithm Allocation algorithm |
1571 | |
1572 | The library uses following algorithm for allocation, in order: |
1573 | |
1574 | -# Try to find free range of memory in existing blocks. |
1575 | -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size. |
1576 | -# If failed, try to create such block with size/2, size/4, size/8. |
1577 | -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was |
1578 | specified, try to find space in existing blocks, possilby making some other |
1579 | allocations lost. |
1580 | -# If failed, try to allocate separate `VkDeviceMemory` for this allocation, |
1581 | just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. |
1582 | -# If failed, choose other memory type that meets the requirements specified in |
1583 | VmaAllocationCreateInfo and go to point 1. |
1584 | -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. |
1585 | |
1586 | \section general_considerations_features_not_supported Features not supported |
1587 | |
1588 | Features deliberately excluded from the scope of this library: |
1589 | |
1590 | - Data transfer. Uploading (straming) and downloading data of buffers and images |
1591 | between CPU and GPU memory and related synchronization is responsibility of the user. |
1592 | - Allocations for imported/exported external memory. They tend to require |
1593 | explicit memory type index and dedicated allocation anyway, so they don't |
1594 | interact with main features of this library. Such special purpose allocations |
1595 | should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`. |
1596 | - Recreation of buffers and images. Although the library has functions for |
1597 | buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to |
1598 | recreate these objects yourself after defragmentation. That's because the big |
1599 | structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in |
1600 | #VmaAllocation object. |
1601 | - Handling CPU memory allocation failures. When dynamically creating small C++ |
1602 | objects in CPU memory (not Vulkan memory), allocation failures are not checked |
1603 | and handled gracefully, because that would complicate code significantly and |
1604 | is usually not needed in desktop PC applications anyway. |
1605 | - Code free of any compiler warnings. Maintaining the library to compile and |
1606 | work correctly on so many different platforms is hard enough. Being free of |
1607 | any warnings, on any version of any compiler, is simply not feasible. |
1608 | - This is a C++ library with C interface. |
1609 | Bindings or ports to any other programming languages are welcomed as external projects and |
1610 | are not going to be included into this repository. |
1611 | |
1612 | */ |
1613 | |
1614 | /* |
1615 | Define this macro to 0/1 to disable/enable support for recording functionality, |
1616 | available through VmaAllocatorCreateInfo::pRecordSettings. |
1617 | */ |
1618 | #ifndef VMA_RECORDING_ENABLED |
1619 | #ifdef _WIN32 |
1620 | #define VMA_RECORDING_ENABLED 1 |
1621 | #else |
1622 | #define VMA_RECORDING_ENABLED 0 |
1623 | #endif |
1624 | #endif |
1625 | |
1626 | #ifndef NOMINMAX |
1627 | #define NOMINMAX // For windows.h |
1628 | #endif |
1629 | |
1630 | #ifndef VULKAN_H_ |
1631 | #include <vulkan/vulkan.h> |
1632 | #endif |
1633 | |
1634 | #if VMA_RECORDING_ENABLED |
1635 | #include <windows.h> |
1636 | #endif |
1637 | |
1638 | #if !defined(VMA_DEDICATED_ALLOCATION) |
1639 | #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation |
1640 | #define VMA_DEDICATED_ALLOCATION 1 |
1641 | #else |
1642 | #define VMA_DEDICATED_ALLOCATION 0 |
1643 | #endif |
1644 | #endif |
1645 | |
1646 | /** \struct VmaAllocator |
1647 | \brief Represents main object of this library initialized. |
1648 | |
1649 | Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it. |
1650 | Call function vmaDestroyAllocator() to destroy it. |
1651 | |
1652 | It is recommended to create just one object of this type per `VkDevice` object, |
1653 | right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed. |
1654 | */ |
1655 | VK_DEFINE_HANDLE(VmaAllocator) |
1656 | |
1657 | /// Callback function called after successful vkAllocateMemory. |
1658 | typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)( |
1659 | VmaAllocator allocator, |
1660 | uint32_t memoryType, |
1661 | VkDeviceMemory memory, |
1662 | VkDeviceSize size); |
1663 | /// Callback function called before vkFreeMemory. |
1664 | typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)( |
1665 | VmaAllocator allocator, |
1666 | uint32_t memoryType, |
1667 | VkDeviceMemory memory, |
1668 | VkDeviceSize size); |
1669 | |
1670 | /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`. |
1671 | |
1672 | Provided for informative purpose, e.g. to gather statistics about number of |
1673 | allocations or total amount of memory allocated in Vulkan. |
1674 | |
1675 | Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. |
1676 | */ |
1677 | typedef struct VmaDeviceMemoryCallbacks { |
1678 | /// Optional, can be null. |
1679 | PFN_vmaAllocateDeviceMemoryFunction pfnAllocate; |
1680 | /// Optional, can be null. |
1681 | PFN_vmaFreeDeviceMemoryFunction pfnFree; |
1682 | } VmaDeviceMemoryCallbacks; |
1683 | |
1684 | /// Flags for created #VmaAllocator. |
1685 | typedef enum VmaAllocatorCreateFlagBits { |
1686 | /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you. |
1687 | |
1688 | Using this flag may increase performance because internal mutexes are not used. |
1689 | */ |
1690 | VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, |
1691 | /** \brief Enables usage of VK_KHR_dedicated_allocation extension. |
1692 | |
1693 | Using this extenion will automatically allocate dedicated blocks of memory for |
1694 | some buffers and images instead of suballocating place for them out of bigger |
1695 | memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT |
1696 | flag) when it is recommended by the driver. It may improve performance on some |
1697 | GPUs. |
1698 | |
1699 | You may set this flag only if you found out that following device extensions are |
1700 | supported, you enabled them while creating Vulkan device passed as |
1701 | VmaAllocatorCreateInfo::device, and you want them to be used internally by this |
1702 | library: |
1703 | |
1704 | - VK_KHR_get_memory_requirements2 |
1705 | - VK_KHR_dedicated_allocation |
1706 | |
1707 | When this flag is set, you can experience following warnings reported by Vulkan |
1708 | validation layer. You can ignore them. |
1709 | |
1710 | > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer. |
1711 | */ |
1712 | VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002, |
1713 | |
1714 | VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF |
1715 | } VmaAllocatorCreateFlagBits; |
1716 | typedef VkFlags VmaAllocatorCreateFlags; |
1717 | |
1718 | /** \brief Pointers to some Vulkan functions - a subset used by the library. |
1719 | |
1720 | Used in VmaAllocatorCreateInfo::pVulkanFunctions. |
1721 | */ |
1722 | typedef struct VmaVulkanFunctions { |
1723 | PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; |
1724 | PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; |
1725 | PFN_vkAllocateMemory vkAllocateMemory; |
1726 | PFN_vkFreeMemory vkFreeMemory; |
1727 | PFN_vkMapMemory vkMapMemory; |
1728 | PFN_vkUnmapMemory vkUnmapMemory; |
1729 | PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; |
1730 | PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; |
1731 | PFN_vkBindBufferMemory vkBindBufferMemory; |
1732 | PFN_vkBindImageMemory vkBindImageMemory; |
1733 | PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; |
1734 | PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; |
1735 | PFN_vkCreateBuffer vkCreateBuffer; |
1736 | PFN_vkDestroyBuffer vkDestroyBuffer; |
1737 | PFN_vkCreateImage vkCreateImage; |
1738 | PFN_vkDestroyImage vkDestroyImage; |
1739 | PFN_vkCmdCopyBuffer vkCmdCopyBuffer; |
1740 | #if VMA_DEDICATED_ALLOCATION |
1741 | PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR; |
1742 | PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR; |
1743 | #endif |
1744 | } VmaVulkanFunctions; |
1745 | |
1746 | /// Flags to be used in VmaRecordSettings::flags. |
1747 | typedef enum VmaRecordFlagBits { |
1748 | /** \brief Enables flush after recording every function call. |
1749 | |
1750 | Enable it if you expect your application to crash, which may leave recording file truncated. |
1751 | It may degrade performance though. |
1752 | */ |
1753 | VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001, |
1754 | |
1755 | VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF |
1756 | } VmaRecordFlagBits; |
1757 | typedef VkFlags VmaRecordFlags; |
1758 | |
1759 | /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings. |
1760 | typedef struct VmaRecordSettings |
1761 | { |
1762 | /// Flags for recording. Use #VmaRecordFlagBits enum. |
1763 | VmaRecordFlags flags; |
1764 | /** \brief Path to the file that should be written by the recording. |
1765 | |
1766 | Suggested extension: "csv". |
1767 | If the file already exists, it will be overwritten. |
1768 | It will be opened for the whole time #VmaAllocator object is alive. |
1769 | If opening this file fails, creation of the whole allocator object fails. |
1770 | */ |
1771 | const char* pFilePath; |
1772 | } VmaRecordSettings; |
1773 | |
1774 | /// Description of a Allocator to be created. |
1775 | typedef struct VmaAllocatorCreateInfo |
1776 | { |
1777 | /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum. |
1778 | VmaAllocatorCreateFlags flags; |
1779 | /// Vulkan physical device. |
1780 | /** It must be valid throughout whole lifetime of created allocator. */ |
1781 | VkPhysicalDevice physicalDevice; |
1782 | /// Vulkan device. |
1783 | /** It must be valid throughout whole lifetime of created allocator. */ |
1784 | VkDevice device; |
1785 | /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional. |
1786 | /** Set to 0 to use default, which is currently 256 MiB. */ |
1787 | VkDeviceSize preferredLargeHeapBlockSize; |
1788 | /// Custom CPU memory allocation callbacks. Optional. |
1789 | /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */ |
1790 | const VkAllocationCallbacks* pAllocationCallbacks; |
1791 | /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional. |
1792 | /** Optional, can be null. */ |
1793 | const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks; |
1794 | /** \brief Maximum number of additional frames that are in use at the same time as current frame. |
1795 | |
1796 | This value is used only when you make allocations with |
1797 | VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become |
1798 | lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount. |
1799 | |
1800 | For example, if you double-buffer your command buffers, so resources used for |
1801 | rendering in previous frame may still be in use by the GPU at the moment you |
1802 | allocate resources needed for the current frame, set this value to 1. |
1803 | |
1804 | If you want to allow any allocations other than used in the current frame to |
1805 | become lost, set this value to 0. |
1806 | */ |
1807 | uint32_t frameInUseCount; |
1808 | /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap. |
1809 | |
1810 | If not NULL, it must be a pointer to an array of |
1811 | `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on |
1812 | maximum number of bytes that can be allocated out of particular Vulkan memory |
1813 | heap. |
1814 | |
1815 | Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that |
1816 | heap. This is also the default in case of `pHeapSizeLimit` = NULL. |
1817 | |
1818 | If there is a limit defined for a heap: |
1819 | |
1820 | - If user tries to allocate more memory from that heap using this allocator, |
1821 | the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. |
1822 | - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the |
1823 | value of this limit will be reported instead when using vmaGetMemoryProperties(). |
1824 | |
1825 | Warning! Using this feature may not be equivalent to installing a GPU with |
1826 | smaller amount of memory, because graphics driver doesn't necessary fail new |
1827 | allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is |
1828 | exceeded. It may return success and just silently migrate some device memory |
1829 | blocks to system RAM. This driver behavior can also be controlled using |
1830 | VK_AMD_memory_overallocation_behavior extension. |
1831 | */ |
1832 | const VkDeviceSize* pHeapSizeLimit; |
1833 | /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`. |
1834 | |
1835 | If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section, |
1836 | you can pass null as this member, because the library will fetch pointers to |
1837 | Vulkan functions internally in a static way, like: |
1838 | |
1839 | vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; |
1840 | |
1841 | Fill this member if you want to provide your own pointers to Vulkan functions, |
1842 | e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`. |
1843 | */ |
1844 | const VmaVulkanFunctions* pVulkanFunctions; |
1845 | /** \brief Parameters for recording of VMA calls. Can be null. |
1846 | |
1847 | If not null, it enables recording of calls to VMA functions to a file. |
1848 | If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro, |
1849 | creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`. |
1850 | */ |
1851 | const VmaRecordSettings* pRecordSettings; |
1852 | } VmaAllocatorCreateInfo; |
1853 | |
1854 | /// Creates Allocator object. |
1855 | VkResult vmaCreateAllocator( |
1856 | const VmaAllocatorCreateInfo* pCreateInfo, |
1857 | VmaAllocator* pAllocator); |
1858 | |
1859 | /// Destroys allocator object. |
1860 | void vmaDestroyAllocator( |
1861 | VmaAllocator allocator); |
1862 | |
1863 | /** |
1864 | PhysicalDeviceProperties are fetched from physicalDevice by the allocator. |
1865 | You can access it here, without fetching it again on your own. |
1866 | */ |
1867 | void vmaGetPhysicalDeviceProperties( |
1868 | VmaAllocator allocator, |
1869 | const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties); |
1870 | |
1871 | /** |
1872 | PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator. |
1873 | You can access it here, without fetching it again on your own. |
1874 | */ |
1875 | void vmaGetMemoryProperties( |
1876 | VmaAllocator allocator, |
1877 | const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties); |
1878 | |
1879 | /** |
1880 | \brief Given Memory Type Index, returns Property Flags of this memory type. |
1881 | |
1882 | This is just a convenience function. Same information can be obtained using |
1883 | vmaGetMemoryProperties(). |
1884 | */ |
1885 | void vmaGetMemoryTypeProperties( |
1886 | VmaAllocator allocator, |
1887 | uint32_t memoryTypeIndex, |
1888 | VkMemoryPropertyFlags* pFlags); |
1889 | |
1890 | /** \brief Sets index of the current frame. |
1891 | |
1892 | This function must be used if you make allocations with |
1893 | #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and |
1894 | #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator |
1895 | when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot |
1896 | become lost in the current frame. |
1897 | */ |
1898 | void vmaSetCurrentFrameIndex( |
1899 | VmaAllocator allocator, |
1900 | uint32_t frameIndex); |
1901 | |
1902 | /** \brief Calculated statistics of memory usage in entire allocator. |
1903 | */ |
1904 | typedef struct VmaStatInfo |
1905 | { |
1906 | /// Number of `VkDeviceMemory` Vulkan memory blocks allocated. |
1907 | uint32_t blockCount; |
1908 | /// Number of #VmaAllocation allocation objects allocated. |
1909 | uint32_t allocationCount; |
1910 | /// Number of free ranges of memory between allocations. |
1911 | uint32_t unusedRangeCount; |
1912 | /// Total number of bytes occupied by all allocations. |
1913 | VkDeviceSize usedBytes; |
1914 | /// Total number of bytes occupied by unused ranges. |
1915 | VkDeviceSize unusedBytes; |
1916 | VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax; |
1917 | VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax; |
1918 | } VmaStatInfo; |
1919 | |
1920 | /// General statistics from current state of Allocator. |
1921 | typedef struct VmaStats |
1922 | { |
1923 | VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]; |
1924 | VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS]; |
1925 | VmaStatInfo total; |
1926 | } VmaStats; |
1927 | |
1928 | /// Retrieves statistics from current state of the Allocator. |
1929 | void vmaCalculateStats( |
1930 | VmaAllocator allocator, |
1931 | VmaStats* pStats); |
1932 | |
1933 | #define VMA_STATS_STRING_ENABLED 1 |
1934 | |
1935 | #if VMA_STATS_STRING_ENABLED |
1936 | |
1937 | /// Builds and returns statistics as string in JSON format. |
1938 | /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function. |
1939 | */ |
1940 | void vmaBuildStatsString( |
1941 | VmaAllocator allocator, |
1942 | char** ppStatsString, |
1943 | VkBool32 detailedMap); |
1944 | |
1945 | void vmaFreeStatsString( |
1946 | VmaAllocator allocator, |
1947 | char* pStatsString); |
1948 | |
1949 | #endif // #if VMA_STATS_STRING_ENABLED |
1950 | |
1951 | /** \struct VmaPool |
1952 | \brief Represents custom memory pool |
1953 | |
1954 | Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it. |
1955 | Call function vmaDestroyPool() to destroy it. |
1956 | |
1957 | For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools). |
1958 | */ |
1959 | VK_DEFINE_HANDLE(VmaPool) |
1960 | |
1961 | typedef enum VmaMemoryUsage |
1962 | { |
1963 | /** No intended memory usage specified. |
1964 | Use other members of VmaAllocationCreateInfo to specify your requirements. |
1965 | */ |
1966 | VMA_MEMORY_USAGE_UNKNOWN = 0, |
1967 | /** Memory will be used on device only, so fast access from the device is preferred. |
1968 | It usually means device-local GPU (video) memory. |
1969 | No need to be mappable on host. |
1970 | It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`. |
1971 | |
1972 | Usage: |
1973 | |
1974 | - Resources written and read by device, e.g. images used as attachments. |
1975 | - Resources transferred from host once (immutable) or infrequently and read by |
1976 | device multiple times, e.g. textures to be sampled, vertex buffers, uniform |
1977 | (constant) buffers, and majority of other types of resources used on GPU. |
1978 | |
1979 | Allocation may still end up in `HOST_VISIBLE` memory on some implementations. |
1980 | In such case, you are free to map it. |
1981 | You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type. |
1982 | */ |
1983 | VMA_MEMORY_USAGE_GPU_ONLY = 1, |
1984 | /** Memory will be mappable on host. |
1985 | It usually means CPU (system) memory. |
1986 | Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`. |
1987 | CPU access is typically uncached. Writes may be write-combined. |
1988 | Resources created in this pool may still be accessible to the device, but access to them can be slow. |
1989 | It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`. |
1990 | |
1991 | Usage: Staging copy of resources used as transfer source. |
1992 | */ |
1993 | VMA_MEMORY_USAGE_CPU_ONLY = 2, |
1994 | /** |
1995 | Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU. |
1996 | CPU access is typically uncached. Writes may be write-combined. |
1997 | |
1998 | Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call. |
1999 | */ |
2000 | VMA_MEMORY_USAGE_CPU_TO_GPU = 3, |
2001 | /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached. |
2002 | It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`. |
2003 | |
2004 | Usage: |
2005 | |
2006 | - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping. |
2007 | - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection. |
2008 | */ |
2009 | VMA_MEMORY_USAGE_GPU_TO_CPU = 4, |
2010 | VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF |
2011 | } VmaMemoryUsage; |
2012 | |
2013 | /// Flags to be passed as VmaAllocationCreateInfo::flags. |
2014 | typedef enum VmaAllocationCreateFlagBits { |
2015 | /** \brief Set this flag if the allocation should have its own memory block. |
2016 | |
2017 | Use it for special, big resources, like fullscreen images used as attachments. |
2018 | |
2019 | This flag must also be used for host visible resources that you want to map |
2020 | simultaneously because otherwise they might end up as regions of the same |
2021 | `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times |
2022 | simultaneously is illegal. |
2023 | |
2024 | You should not use this flag if VmaAllocationCreateInfo::pool is not null. |
2025 | */ |
2026 | VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001, |
2027 | |
2028 | /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block. |
2029 | |
2030 | If new allocation cannot be placed in any of the existing blocks, allocation |
2031 | fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error. |
2032 | |
2033 | You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and |
2034 | #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense. |
2035 | |
2036 | If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */ |
2037 | VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002, |
2038 | /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. |
2039 | |
2040 | Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData. |
2041 | |
2042 | Is it valid to use this flag for allocation made from memory type that is not |
2043 | `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is |
2044 | useful if you need an allocation that is efficient to use on GPU |
2045 | (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that |
2046 | support it (e.g. Intel GPU). |
2047 | |
2048 | You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT. |
2049 | */ |
2050 | VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004, |
2051 | /** Allocation created with this flag can become lost as a result of another |
2052 | allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you |
2053 | must check it before use. |
2054 | |
2055 | To check if allocation is not lost, call vmaGetAllocationInfo() and check if |
2056 | VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`. |
2057 | |
2058 | For details about supporting lost allocations, see Lost Allocations |
2059 | chapter of User Guide on Main Page. |
2060 | |
2061 | You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT. |
2062 | */ |
2063 | VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008, |
2064 | /** While creating allocation using this flag, other allocations that were |
2065 | created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost. |
2066 | |
2067 | For details about supporting lost allocations, see Lost Allocations |
2068 | chapter of User Guide on Main Page. |
2069 | */ |
2070 | VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010, |
2071 | /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a |
2072 | null-terminated string. Instead of copying pointer value, a local copy of the |
2073 | string is made and stored in allocation's `pUserData`. The string is automatically |
2074 | freed together with the allocation. It is also used in vmaBuildStatsString(). |
2075 | */ |
2076 | VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020, |
2077 | /** Allocation will be created from upper stack in a double stack pool. |
2078 | |
2079 | This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag. |
2080 | */ |
2081 | VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040, |
2082 | |
2083 | /** Allocation strategy that chooses smallest possible free range for the |
2084 | allocation. |
2085 | */ |
2086 | VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000, |
2087 | /** Allocation strategy that chooses biggest possible free range for the |
2088 | allocation. |
2089 | */ |
2090 | VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000, |
2091 | /** Allocation strategy that chooses first suitable free range for the |
2092 | allocation. |
2093 | |
2094 | "First" doesn't necessarily means the one with smallest offset in memory, |
2095 | but rather the one that is easiest and fastest to find. |
2096 | */ |
2097 | VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000, |
2098 | |
2099 | /** Allocation strategy that tries to minimize memory usage. |
2100 | */ |
2101 | VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT, |
2102 | /** Allocation strategy that tries to minimize allocation time. |
2103 | */ |
2104 | VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT, |
2105 | /** Allocation strategy that tries to minimize memory fragmentation. |
2106 | */ |
2107 | VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT, |
2108 | |
2109 | /** A bit mask to extract only `STRATEGY` bits from entire set of flags. |
2110 | */ |
2111 | VMA_ALLOCATION_CREATE_STRATEGY_MASK = |
2112 | VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT | |
2113 | VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT | |
2114 | VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT, |
2115 | |
2116 | VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF |
2117 | } VmaAllocationCreateFlagBits; |
2118 | typedef VkFlags VmaAllocationCreateFlags; |
2119 | |
2120 | typedef struct VmaAllocationCreateInfo |
2121 | { |
2122 | /// Use #VmaAllocationCreateFlagBits enum. |
2123 | VmaAllocationCreateFlags flags; |
2124 | /** \brief Intended usage of memory. |
2125 | |
2126 | You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n |
2127 | If `pool` is not null, this member is ignored. |
2128 | */ |
2129 | VmaMemoryUsage usage; |
2130 | /** \brief Flags that must be set in a Memory Type chosen for an allocation. |
2131 | |
2132 | Leave 0 if you specify memory requirements in other way. \n |
2133 | If `pool` is not null, this member is ignored.*/ |
2134 | VkMemoryPropertyFlags requiredFlags; |
2135 | /** \brief Flags that preferably should be set in a memory type chosen for an allocation. |
2136 | |
2137 | Set to 0 if no additional flags are prefered. \n |
2138 | If `pool` is not null, this member is ignored. */ |
2139 | VkMemoryPropertyFlags preferredFlags; |
2140 | /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation. |
2141 | |
2142 | Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if |
2143 | it meets other requirements specified by this structure, with no further |
2144 | restrictions on memory type index. \n |
2145 | If `pool` is not null, this member is ignored. |
2146 | */ |
2147 | uint32_t memoryTypeBits; |
2148 | /** \brief Pool that this allocation should be created in. |
2149 | |
2150 | Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members: |
2151 | `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored. |
2152 | */ |
2153 | VmaPool pool; |
2154 | /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). |
2155 | |
2156 | If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either |
2157 | null or pointer to a null-terminated string. The string will be then copied to |
2158 | internal buffer, so it doesn't need to be valid after allocation call. |
2159 | */ |
2160 | void* pUserData; |
2161 | } VmaAllocationCreateInfo; |
2162 | |
2163 | /** |
2164 | \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo. |
2165 | |
2166 | This algorithm tries to find a memory type that: |
2167 | |
2168 | - Is allowed by memoryTypeBits. |
2169 | - Contains all the flags from pAllocationCreateInfo->requiredFlags. |
2170 | - Matches intended usage. |
2171 | - Has as many flags from pAllocationCreateInfo->preferredFlags as possible. |
2172 | |
2173 | \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result |
2174 | from this function or any other allocating function probably means that your |
2175 | device doesn't support any memory type with requested features for the specific |
2176 | type of resource you want to use it for. Please check parameters of your |
2177 | resource, like image layout (OPTIMAL versus LINEAR) or mip level count. |
2178 | */ |
2179 | VkResult vmaFindMemoryTypeIndex( |
2180 | VmaAllocator allocator, |
2181 | uint32_t memoryTypeBits, |
2182 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
2183 | uint32_t* pMemoryTypeIndex); |
2184 | |
2185 | /** |
2186 | \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo. |
2187 | |
2188 | It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. |
2189 | It internally creates a temporary, dummy buffer that never has memory bound. |
2190 | It is just a convenience function, equivalent to calling: |
2191 | |
2192 | - `vkCreateBuffer` |
2193 | - `vkGetBufferMemoryRequirements` |
2194 | - `vmaFindMemoryTypeIndex` |
2195 | - `vkDestroyBuffer` |
2196 | */ |
2197 | VkResult vmaFindMemoryTypeIndexForBufferInfo( |
2198 | VmaAllocator allocator, |
2199 | const VkBufferCreateInfo* pBufferCreateInfo, |
2200 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
2201 | uint32_t* pMemoryTypeIndex); |
2202 | |
2203 | /** |
2204 | \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo. |
2205 | |
2206 | It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. |
2207 | It internally creates a temporary, dummy image that never has memory bound. |
2208 | It is just a convenience function, equivalent to calling: |
2209 | |
2210 | - `vkCreateImage` |
2211 | - `vkGetImageMemoryRequirements` |
2212 | - `vmaFindMemoryTypeIndex` |
2213 | - `vkDestroyImage` |
2214 | */ |
2215 | VkResult vmaFindMemoryTypeIndexForImageInfo( |
2216 | VmaAllocator allocator, |
2217 | const VkImageCreateInfo* pImageCreateInfo, |
2218 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
2219 | uint32_t* pMemoryTypeIndex); |
2220 | |
2221 | /// Flags to be passed as VmaPoolCreateInfo::flags. |
2222 | typedef enum VmaPoolCreateFlagBits { |
2223 | /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored. |
2224 | |
2225 | This is an optional optimization flag. |
2226 | |
2227 | If you always allocate using vmaCreateBuffer(), vmaCreateImage(), |
2228 | vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator |
2229 | knows exact type of your allocations so it can handle Buffer-Image Granularity |
2230 | in the optimal way. |
2231 | |
2232 | If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(), |
2233 | exact type of such allocations is not known, so allocator must be conservative |
2234 | in handling Buffer-Image Granularity, which can lead to suboptimal allocation |
2235 | (wasted memory). In that case, if you can make sure you always allocate only |
2236 | buffers and linear images or only optimal images out of this pool, use this flag |
2237 | to make allocator disregard Buffer-Image Granularity and so make allocations |
2238 | faster and more optimal. |
2239 | */ |
2240 | VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002, |
2241 | |
2242 | /** \brief Enables alternative, linear allocation algorithm in this pool. |
2243 | |
2244 | Specify this flag to enable linear allocation algorithm, which always creates |
2245 | new allocations after last one and doesn't reuse space from allocations freed in |
2246 | between. It trades memory consumption for simplified algorithm and data |
2247 | structure, which has better performance and uses less memory for metadata. |
2248 | |
2249 | By using this flag, you can achieve behavior of free-at-once, stack, |
2250 | ring buffer, and double stack. For details, see documentation chapter |
2251 | \ref linear_algorithm. |
2252 | |
2253 | When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default). |
2254 | |
2255 | For more details, see [Linear allocation algorithm](@ref linear_algorithm). |
2256 | */ |
2257 | VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004, |
2258 | |
2259 | /** \brief Enables alternative, buddy allocation algorithm in this pool. |
2260 | |
2261 | It operates on a tree of blocks, each having size that is a power of two and |
2262 | a half of its parent's size. Comparing to default algorithm, this one provides |
2263 | faster allocation and deallocation and decreased external fragmentation, |
2264 | at the expense of more memory wasted (internal fragmentation). |
2265 | |
2266 | For more details, see [Buddy allocation algorithm](@ref buddy_algorithm). |
2267 | */ |
2268 | VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008, |
2269 | |
2270 | /** Bit mask to extract only `ALGORITHM` bits from entire set of flags. |
2271 | */ |
2272 | VMA_POOL_CREATE_ALGORITHM_MASK = |
2273 | VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT | |
2274 | VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT, |
2275 | |
2276 | VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF |
2277 | } VmaPoolCreateFlagBits; |
2278 | typedef VkFlags VmaPoolCreateFlags; |
2279 | |
2280 | /** \brief Describes parameter of created #VmaPool. |
2281 | */ |
2282 | typedef struct VmaPoolCreateInfo { |
2283 | /** \brief Vulkan memory type index to allocate this pool from. |
2284 | */ |
2285 | uint32_t memoryTypeIndex; |
2286 | /** \brief Use combination of #VmaPoolCreateFlagBits. |
2287 | */ |
2288 | VmaPoolCreateFlags flags; |
2289 | /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional. |
2290 | |
2291 | Specify nonzero to set explicit, constant size of memory blocks used by this |
2292 | pool. |
2293 | |
2294 | Leave 0 to use default and let the library manage block sizes automatically. |
2295 | Sizes of particular blocks may vary. |
2296 | */ |
2297 | VkDeviceSize blockSize; |
2298 | /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty. |
2299 | |
2300 | Set to 0 to have no preallocated blocks and allow the pool be completely empty. |
2301 | */ |
2302 | size_t minBlockCount; |
2303 | /** \brief Maximum number of blocks that can be allocated in this pool. Optional. |
2304 | |
2305 | Set to 0 to use default, which is `SIZE_MAX`, which means no limit. |
2306 | |
2307 | Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated |
2308 | throughout whole lifetime of this pool. |
2309 | */ |
2310 | size_t maxBlockCount; |
2311 | /** \brief Maximum number of additional frames that are in use at the same time as current frame. |
2312 | |
2313 | This value is used only when you make allocations with |
2314 | #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become |
2315 | lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount. |
2316 | |
2317 | For example, if you double-buffer your command buffers, so resources used for |
2318 | rendering in previous frame may still be in use by the GPU at the moment you |
2319 | allocate resources needed for the current frame, set this value to 1. |
2320 | |
2321 | If you want to allow any allocations other than used in the current frame to |
2322 | become lost, set this value to 0. |
2323 | */ |
2324 | uint32_t frameInUseCount; |
2325 | } VmaPoolCreateInfo; |
2326 | |
2327 | /** \brief Describes parameter of existing #VmaPool. |
2328 | */ |
2329 | typedef struct VmaPoolStats { |
2330 | /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes. |
2331 | */ |
2332 | VkDeviceSize size; |
2333 | /** \brief Total number of bytes in the pool not used by any #VmaAllocation. |
2334 | */ |
2335 | VkDeviceSize unusedSize; |
2336 | /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost. |
2337 | */ |
2338 | size_t allocationCount; |
2339 | /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation. |
2340 | */ |
2341 | size_t unusedRangeCount; |
2342 | /** \brief Size of the largest continuous free memory region available for new allocation. |
2343 | |
2344 | Making a new allocation of that size is not guaranteed to succeed because of |
2345 | possible additional margin required to respect alignment and buffer/image |
2346 | granularity. |
2347 | */ |
2348 | VkDeviceSize unusedRangeSizeMax; |
2349 | /** \brief Number of `VkDeviceMemory` blocks allocated for this pool. |
2350 | */ |
2351 | size_t blockCount; |
2352 | } VmaPoolStats; |
2353 | |
2354 | /** \brief Allocates Vulkan device memory and creates #VmaPool object. |
2355 | |
2356 | @param allocator Allocator object. |
2357 | @param pCreateInfo Parameters of pool to create. |
2358 | @param[out] pPool Handle to created pool. |
2359 | */ |
2360 | VkResult vmaCreatePool( |
2361 | VmaAllocator allocator, |
2362 | const VmaPoolCreateInfo* pCreateInfo, |
2363 | VmaPool* pPool); |
2364 | |
2365 | /** \brief Destroys #VmaPool object and frees Vulkan device memory. |
2366 | */ |
2367 | void vmaDestroyPool( |
2368 | VmaAllocator allocator, |
2369 | VmaPool pool); |
2370 | |
2371 | /** \brief Retrieves statistics of existing #VmaPool object. |
2372 | |
2373 | @param allocator Allocator object. |
2374 | @param pool Pool object. |
2375 | @param[out] pPoolStats Statistics of specified pool. |
2376 | */ |
2377 | void vmaGetPoolStats( |
2378 | VmaAllocator allocator, |
2379 | VmaPool pool, |
2380 | VmaPoolStats* pPoolStats); |
2381 | |
2382 | /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now. |
2383 | |
2384 | @param allocator Allocator object. |
2385 | @param pool Pool. |
2386 | @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information. |
2387 | */ |
2388 | void vmaMakePoolAllocationsLost( |
2389 | VmaAllocator allocator, |
2390 | VmaPool pool, |
2391 | size_t* pLostAllocationCount); |
2392 | |
2393 | /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions. |
2394 | |
2395 | Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, |
2396 | `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is |
2397 | `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). |
2398 | |
2399 | Possible return values: |
2400 | |
2401 | - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool. |
2402 | - `VK_SUCCESS` - corruption detection has been performed and succeeded. |
2403 | - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations. |
2404 | `VMA_ASSERT` is also fired in that case. |
2405 | - Other value: Error returned by Vulkan, e.g. memory mapping failure. |
2406 | */ |
2407 | VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool); |
2408 | |
2409 | /** \struct VmaAllocation |
2410 | \brief Represents single memory allocation. |
2411 | |
2412 | It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type |
2413 | plus unique offset. |
2414 | |
2415 | There are multiple ways to create such object. |
2416 | You need to fill structure VmaAllocationCreateInfo. |
2417 | For more information see [Choosing memory type](@ref choosing_memory_type). |
2418 | |
2419 | Although the library provides convenience functions that create Vulkan buffer or image, |
2420 | allocate memory for it and bind them together, |
2421 | binding of the allocation to a buffer or an image is out of scope of the allocation itself. |
2422 | Allocation object can exist without buffer/image bound, |
2423 | binding can be done manually by the user, and destruction of it can be done |
2424 | independently of destruction of the allocation. |
2425 | |
2426 | The object also remembers its size and some other information. |
2427 | To retrieve this information, use function vmaGetAllocationInfo() and inspect |
2428 | returned structure VmaAllocationInfo. |
2429 | |
2430 | Some kinds allocations can be in lost state. |
2431 | For more information, see [Lost allocations](@ref lost_allocations). |
2432 | */ |
2433 | VK_DEFINE_HANDLE(VmaAllocation) |
2434 | |
2435 | /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). |
2436 | */ |
2437 | typedef struct VmaAllocationInfo { |
2438 | /** \brief Memory type index that this allocation was allocated from. |
2439 | |
2440 | It never changes. |
2441 | */ |
2442 | uint32_t memoryType; |
2443 | /** \brief Handle to Vulkan memory object. |
2444 | |
2445 | Same memory object can be shared by multiple allocations. |
2446 | |
2447 | It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost. |
2448 | |
2449 | If the allocation is lost, it is equal to `VK_NULL_HANDLE`. |
2450 | */ |
2451 | VkDeviceMemory deviceMemory; |
2452 | /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation. |
2453 | |
2454 | It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost. |
2455 | */ |
2456 | VkDeviceSize offset; |
2457 | /** \brief Size of this allocation, in bytes. |
2458 | |
2459 | It never changes, unless allocation is lost. |
2460 | */ |
2461 | VkDeviceSize size; |
2462 | /** \brief Pointer to the beginning of this allocation as mapped data. |
2463 | |
2464 | If the allocation hasn't been mapped using vmaMapMemory() and hasn't been |
2465 | created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null. |
2466 | |
2467 | It can change after call to vmaMapMemory(), vmaUnmapMemory(). |
2468 | It can also change after call to vmaDefragment() if this allocation is passed to the function. |
2469 | */ |
2470 | void* pMappedData; |
2471 | /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData(). |
2472 | |
2473 | It can change after call to vmaSetAllocationUserData() for this allocation. |
2474 | */ |
2475 | void* pUserData; |
2476 | } VmaAllocationInfo; |
2477 | |
2478 | /** \brief General purpose memory allocation. |
2479 | |
2480 | @param[out] pAllocation Handle to allocated memory. |
2481 | @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). |
2482 | |
2483 | You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). |
2484 | |
2485 | It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), |
2486 | vmaCreateBuffer(), vmaCreateImage() instead whenever possible. |
2487 | */ |
2488 | VkResult vmaAllocateMemory( |
2489 | VmaAllocator allocator, |
2490 | const VkMemoryRequirements* pVkMemoryRequirements, |
2491 | const VmaAllocationCreateInfo* pCreateInfo, |
2492 | VmaAllocation* pAllocation, |
2493 | VmaAllocationInfo* pAllocationInfo); |
2494 | |
2495 | /** \brief General purpose memory allocation for multiple allocation objects at once. |
2496 | |
2497 | @param allocator Allocator object. |
2498 | @param pVkMemoryRequirements Memory requirements for each allocation. |
2499 | @param pCreateInfo Creation parameters for each alloction. |
2500 | @param allocationCount Number of allocations to make. |
2501 | @param[out] pAllocations Pointer to array that will be filled with handles to created allocations. |
2502 | @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations. |
2503 | |
2504 | You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). |
2505 | |
2506 | Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding. |
2507 | It is just a general purpose allocation function able to make multiple allocations at once. |
2508 | It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times. |
2509 | |
2510 | All allocations are made using same parameters. All of them are created out of the same memory pool and type. |
2511 | If any allocation fails, all allocations already made within this function call are also freed, so that when |
2512 | returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`. |
2513 | */ |
2514 | VkResult vmaAllocateMemoryPages( |
2515 | VmaAllocator allocator, |
2516 | const VkMemoryRequirements* pVkMemoryRequirements, |
2517 | const VmaAllocationCreateInfo* pCreateInfo, |
2518 | size_t allocationCount, |
2519 | VmaAllocation* pAllocations, |
2520 | VmaAllocationInfo* pAllocationInfo); |
2521 | |
2522 | /** |
2523 | @param[out] pAllocation Handle to allocated memory. |
2524 | @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). |
2525 | |
2526 | You should free the memory using vmaFreeMemory(). |
2527 | */ |
2528 | VkResult vmaAllocateMemoryForBuffer( |
2529 | VmaAllocator allocator, |
2530 | VkBuffer buffer, |
2531 | const VmaAllocationCreateInfo* pCreateInfo, |
2532 | VmaAllocation* pAllocation, |
2533 | VmaAllocationInfo* pAllocationInfo); |
2534 | |
2535 | /// Function similar to vmaAllocateMemoryForBuffer(). |
2536 | VkResult vmaAllocateMemoryForImage( |
2537 | VmaAllocator allocator, |
2538 | VkImage image, |
2539 | const VmaAllocationCreateInfo* pCreateInfo, |
2540 | VmaAllocation* pAllocation, |
2541 | VmaAllocationInfo* pAllocationInfo); |
2542 | |
2543 | /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). |
2544 | |
2545 | Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped. |
2546 | */ |
2547 | void vmaFreeMemory( |
2548 | VmaAllocator allocator, |
2549 | VmaAllocation allocation); |
2550 | |
2551 | /** \brief Frees memory and destroys multiple allocations. |
2552 | |
2553 | Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding. |
2554 | It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(), |
2555 | vmaAllocateMemoryPages() and other functions. |
2556 | It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times. |
2557 | |
2558 | Allocations in `pAllocations` array can come from any memory pools and types. |
2559 | Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped. |
2560 | */ |
2561 | void vmaFreeMemoryPages( |
2562 | VmaAllocator allocator, |
2563 | size_t allocationCount, |
2564 | VmaAllocation* pAllocations); |
2565 | |
2566 | /** \brief Tries to resize an allocation in place, if there is enough free memory after it. |
2567 | |
2568 | Tries to change allocation's size without moving or reallocating it. |
2569 | You can both shrink and grow allocation size. |
2570 | When growing, it succeeds only when the allocation belongs to a memory block with enough |
2571 | free space after it. |
2572 | |
2573 | Returns `VK_SUCCESS` if allocation's size has been successfully changed. |
2574 | Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed. |
2575 | |
2576 | After successful call to this function, VmaAllocationInfo::size of this allocation changes. |
2577 | All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer. |
2578 | |
2579 | - Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`. |
2580 | - Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`. |
2581 | - Resizing dedicated allocations, as well as allocations created in pools that use linear |
2582 | or buddy algorithm, is not supported. |
2583 | The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases. |
2584 | Support may be added in the future. |
2585 | */ |
2586 | VkResult vmaResizeAllocation( |
2587 | VmaAllocator allocator, |
2588 | VmaAllocation allocation, |
2589 | VkDeviceSize newSize); |
2590 | |
2591 | /** \brief Returns current information about specified allocation and atomically marks it as used in current frame. |
2592 | |
2593 | Current paramters of given allocation are returned in `pAllocationInfo`. |
2594 | |
2595 | This function also atomically "touches" allocation - marks it as used in current frame, |
2596 | just like vmaTouchAllocation(). |
2597 | If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`. |
2598 | |
2599 | Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient, |
2600 | you can avoid calling it too often. |
2601 | |
2602 | - You can retrieve same VmaAllocationInfo structure while creating your resource, from function |
2603 | vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change |
2604 | (e.g. due to defragmentation or allocation becoming lost). |
2605 | - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster. |
2606 | */ |
2607 | void vmaGetAllocationInfo( |
2608 | VmaAllocator allocator, |
2609 | VmaAllocation allocation, |
2610 | VmaAllocationInfo* pAllocationInfo); |
2611 | |
2612 | /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame. |
2613 | |
2614 | If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag, |
2615 | this function returns `VK_TRUE` if it's not in lost state, so it can still be used. |
2616 | It then also atomically "touches" the allocation - marks it as used in current frame, |
2617 | so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames. |
2618 | |
2619 | If the allocation is in lost state, the function returns `VK_FALSE`. |
2620 | Memory of such allocation, as well as buffer or image bound to it, should not be used. |
2621 | Lost allocation and the buffer/image still need to be destroyed. |
2622 | |
2623 | If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag, |
2624 | this function always returns `VK_TRUE`. |
2625 | */ |
2626 | VkBool32 vmaTouchAllocation( |
2627 | VmaAllocator allocator, |
2628 | VmaAllocation allocation); |
2629 | |
2630 | /** \brief Sets pUserData in given allocation to new value. |
2631 | |
2632 | If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT, |
2633 | pUserData must be either null, or pointer to a null-terminated string. The function |
2634 | makes local copy of the string and sets it as allocation's `pUserData`. String |
2635 | passed as pUserData doesn't need to be valid for whole lifetime of the allocation - |
2636 | you can free it after this call. String previously pointed by allocation's |
2637 | pUserData is freed from memory. |
2638 | |
2639 | If the flag was not used, the value of pointer `pUserData` is just copied to |
2640 | allocation's `pUserData`. It is opaque, so you can use it however you want - e.g. |
2641 | as a pointer, ordinal number or some handle to you own data. |
2642 | */ |
2643 | void vmaSetAllocationUserData( |
2644 | VmaAllocator allocator, |
2645 | VmaAllocation allocation, |
2646 | void* pUserData); |
2647 | |
2648 | /** \brief Creates new allocation that is in lost state from the beginning. |
2649 | |
2650 | It can be useful if you need a dummy, non-null allocation. |
2651 | |
2652 | You still need to destroy created object using vmaFreeMemory(). |
2653 | |
2654 | Returned allocation is not tied to any specific memory pool or memory type and |
2655 | not bound to any image or buffer. It has size = 0. It cannot be turned into |
2656 | a real, non-empty allocation. |
2657 | */ |
2658 | void vmaCreateLostAllocation( |
2659 | VmaAllocator allocator, |
2660 | VmaAllocation* pAllocation); |
2661 | |
2662 | /** \brief Maps memory represented by given allocation and returns pointer to it. |
2663 | |
2664 | Maps memory represented by given allocation to make it accessible to CPU code. |
2665 | When succeeded, `*ppData` contains pointer to first byte of this memory. |
2666 | If the allocation is part of bigger `VkDeviceMemory` block, the pointer is |
2667 | correctly offseted to the beginning of region assigned to this particular |
2668 | allocation. |
2669 | |
2670 | Mapping is internally reference-counted and synchronized, so despite raw Vulkan |
2671 | function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory` |
2672 | multiple times simultaneously, it is safe to call this function on allocations |
2673 | assigned to the same memory block. Actual Vulkan memory will be mapped on first |
2674 | mapping and unmapped on last unmapping. |
2675 | |
2676 | If the function succeeded, you must call vmaUnmapMemory() to unmap the |
2677 | allocation when mapping is no longer needed or before freeing the allocation, at |
2678 | the latest. |
2679 | |
2680 | It also safe to call this function multiple times on the same allocation. You |
2681 | must call vmaUnmapMemory() same number of times as you called vmaMapMemory(). |
2682 | |
2683 | It is also safe to call this function on allocation created with |
2684 | #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time. |
2685 | You must still call vmaUnmapMemory() same number of times as you called |
2686 | vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the |
2687 | "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. |
2688 | |
2689 | This function fails when used on allocation made in memory type that is not |
2690 | `HOST_VISIBLE`. |
2691 | |
2692 | This function always fails when called for allocation that was created with |
2693 | #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be |
2694 | mapped. |
2695 | */ |
2696 | VkResult vmaMapMemory( |
2697 | VmaAllocator allocator, |
2698 | VmaAllocation allocation, |
2699 | void** ppData); |
2700 | |
2701 | /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory(). |
2702 | |
2703 | For details, see description of vmaMapMemory(). |
2704 | */ |
2705 | void vmaUnmapMemory( |
2706 | VmaAllocator allocator, |
2707 | VmaAllocation allocation); |
2708 | |
2709 | /** \brief Flushes memory of given allocation. |
2710 | |
2711 | Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation. |
2712 | |
2713 | - `offset` must be relative to the beginning of allocation. |
2714 | - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. |
2715 | - `offset` and `size` don't have to be aligned. |
2716 | They are internally rounded down/up to multiply of `nonCoherentAtomSize`. |
2717 | - If `size` is 0, this call is ignored. |
2718 | - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, |
2719 | this call is ignored. |
2720 | */ |
2721 | void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); |
2722 | |
2723 | /** \brief Invalidates memory of given allocation. |
2724 | |
2725 | Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation. |
2726 | |
2727 | - `offset` must be relative to the beginning of allocation. |
2728 | - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. |
2729 | - `offset` and `size` don't have to be aligned. |
2730 | They are internally rounded down/up to multiply of `nonCoherentAtomSize`. |
2731 | - If `size` is 0, this call is ignored. |
2732 | - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, |
2733 | this call is ignored. |
2734 | */ |
2735 | void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); |
2736 | |
2737 | /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions. |
2738 | |
2739 | @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked. |
2740 | |
2741 | Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, |
2742 | `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are |
2743 | `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). |
2744 | |
2745 | Possible return values: |
2746 | |
2747 | - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types. |
2748 | - `VK_SUCCESS` - corruption detection has been performed and succeeded. |
2749 | - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations. |
2750 | `VMA_ASSERT` is also fired in that case. |
2751 | - Other value: Error returned by Vulkan, e.g. memory mapping failure. |
2752 | */ |
2753 | VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits); |
2754 | |
2755 | /** \struct VmaDefragmentationContext |
2756 | \brief Represents Opaque object that represents started defragmentation process. |
2757 | |
2758 | Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it. |
2759 | Call function vmaDefragmentationEnd() to destroy it. |
2760 | */ |
2761 | VK_DEFINE_HANDLE(VmaDefragmentationContext) |
2762 | |
2763 | /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use. |
2764 | typedef enum VmaDefragmentationFlagBits { |
2765 | VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF |
2766 | } VmaDefragmentationFlagBits; |
2767 | typedef VkFlags VmaDefragmentationFlags; |
2768 | |
2769 | /** \brief Parameters for defragmentation. |
2770 | |
2771 | To be used with function vmaDefragmentationBegin(). |
2772 | */ |
2773 | typedef struct VmaDefragmentationInfo2 { |
2774 | /** \brief Reserved for future use. Should be 0. |
2775 | */ |
2776 | VmaDefragmentationFlags flags; |
2777 | /** \brief Number of allocations in `pAllocations` array. |
2778 | */ |
2779 | uint32_t allocationCount; |
2780 | /** \brief Pointer to array of allocations that can be defragmented. |
2781 | |
2782 | The array should have `allocationCount` elements. |
2783 | The array should not contain nulls. |
2784 | Elements in the array should be unique - same allocation cannot occur twice. |
2785 | It is safe to pass allocations that are in the lost state - they are ignored. |
2786 | All allocations not present in this array are considered non-moveable during this defragmentation. |
2787 | */ |
2788 | VmaAllocation* pAllocations; |
2789 | /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation. |
2790 | |
2791 | The array should have `allocationCount` elements. |
2792 | You can pass null if you are not interested in this information. |
2793 | */ |
2794 | VkBool32* pAllocationsChanged; |
2795 | /** \brief Numer of pools in `pPools` array. |
2796 | */ |
2797 | uint32_t poolCount; |
2798 | /** \brief Either null or pointer to array of pools to be defragmented. |
2799 | |
2800 | All the allocations in the specified pools can be moved during defragmentation |
2801 | and there is no way to check if they were really moved as in `pAllocationsChanged`, |
2802 | so you must query all the allocations in all these pools for new `VkDeviceMemory` |
2803 | and offset using vmaGetAllocationInfo() if you might need to recreate buffers |
2804 | and images bound to them. |
2805 | |
2806 | The array should have `poolCount` elements. |
2807 | The array should not contain nulls. |
2808 | Elements in the array should be unique - same pool cannot occur twice. |
2809 | |
2810 | Using this array is equivalent to specifying all allocations from the pools in `pAllocations`. |
2811 | It might be more efficient. |
2812 | */ |
2813 | VmaPool* pPools; |
2814 | /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`. |
2815 | |
2816 | `VK_WHOLE_SIZE` means no limit. |
2817 | */ |
2818 | VkDeviceSize maxCpuBytesToMove; |
2819 | /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`. |
2820 | |
2821 | `UINT32_MAX` means no limit. |
2822 | */ |
2823 | uint32_t maxCpuAllocationsToMove; |
2824 | /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`. |
2825 | |
2826 | `VK_WHOLE_SIZE` means no limit. |
2827 | */ |
2828 | VkDeviceSize maxGpuBytesToMove; |
2829 | /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`. |
2830 | |
2831 | `UINT32_MAX` means no limit. |
2832 | */ |
2833 | uint32_t maxGpuAllocationsToMove; |
2834 | /** \brief Optional. Command buffer where GPU copy commands will be posted. |
2835 | |
2836 | If not null, it must be a valid command buffer handle that supports Transfer queue type. |
2837 | It must be in the recording state and outside of a render pass instance. |
2838 | You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd(). |
2839 | |
2840 | Passing null means that only CPU defragmentation will be performed. |
2841 | */ |
2842 | VkCommandBuffer commandBuffer; |
2843 | } VmaDefragmentationInfo2; |
2844 | |
2845 | /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment(). |
2846 | |
2847 | \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead. |
2848 | */ |
2849 | typedef struct VmaDefragmentationInfo { |
2850 | /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places. |
2851 | |
2852 | Default is `VK_WHOLE_SIZE`, which means no limit. |
2853 | */ |
2854 | VkDeviceSize maxBytesToMove; |
2855 | /** \brief Maximum number of allocations that can be moved to different place. |
2856 | |
2857 | Default is `UINT32_MAX`, which means no limit. |
2858 | */ |
2859 | uint32_t maxAllocationsToMove; |
2860 | } VmaDefragmentationInfo; |
2861 | |
2862 | /** \brief Statistics returned by function vmaDefragment(). */ |
2863 | typedef struct VmaDefragmentationStats { |
2864 | /// Total number of bytes that have been copied while moving allocations to different places. |
2865 | VkDeviceSize bytesMoved; |
2866 | /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects. |
2867 | VkDeviceSize bytesFreed; |
2868 | /// Number of allocations that have been moved to different places. |
2869 | uint32_t allocationsMoved; |
2870 | /// Number of empty `VkDeviceMemory` objects that have been released to the system. |
2871 | uint32_t deviceMemoryBlocksFreed; |
2872 | } VmaDefragmentationStats; |
2873 | |
2874 | /** \brief Begins defragmentation process. |
2875 | |
2876 | @param allocator Allocator object. |
2877 | @param pInfo Structure filled with parameters of defragmentation. |
2878 | @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information. |
2879 | @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation. |
2880 | @return `VK_SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `VK_NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error. |
2881 | |
2882 | Use this function instead of old, deprecated vmaDefragment(). |
2883 | |
2884 | Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd(): |
2885 | |
2886 | - You should not use any of allocations passed as `pInfo->pAllocations` or |
2887 | any allocations that belong to pools passed as `pInfo->pPools`, |
2888 | including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access |
2889 | their data. |
2890 | - Some mutexes protecting internal data structures may be locked, so trying to |
2891 | make or free any allocations, bind buffers or images, map memory, or launch |
2892 | another simultaneous defragmentation in between may cause stall (when done on |
2893 | another thread) or deadlock (when done on the same thread), unless you are |
2894 | 100% sure that defragmented allocations are in different pools. |
2895 | - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined. |
2896 | They become valid after call to vmaDefragmentationEnd(). |
2897 | - If `pInfo->commandBuffer` is not null, you must submit that command buffer |
2898 | and make sure it finished execution before calling vmaDefragmentationEnd(). |
2899 | */ |
2900 | VkResult vmaDefragmentationBegin( |
2901 | VmaAllocator allocator, |
2902 | const VmaDefragmentationInfo2* pInfo, |
2903 | VmaDefragmentationStats* pStats, |
2904 | VmaDefragmentationContext *pContext); |
2905 | |
2906 | /** \brief Ends defragmentation process. |
2907 | |
2908 | Use this function to finish defragmentation started by vmaDefragmentationBegin(). |
2909 | It is safe to pass `context == null`. The function then does nothing. |
2910 | */ |
2911 | VkResult vmaDefragmentationEnd( |
2912 | VmaAllocator allocator, |
2913 | VmaDefragmentationContext context); |
2914 | |
2915 | /** \brief Deprecated. Compacts memory by moving allocations. |
2916 | |
2917 | @param pAllocations Array of allocations that can be moved during this compation. |
2918 | @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays. |
2919 | @param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information. |
2920 | @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values. |
2921 | @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information. |
2922 | @return `VK_SUCCESS` if completed, negative error code in case of error. |
2923 | |
2924 | \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead. |
2925 | |
2926 | This function works by moving allocations to different places (different |
2927 | `VkDeviceMemory` objects and/or different offsets) in order to optimize memory |
2928 | usage. Only allocations that are in `pAllocations` array can be moved. All other |
2929 | allocations are considered nonmovable in this call. Basic rules: |
2930 | |
2931 | - Only allocations made in memory types that have |
2932 | `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` |
2933 | flags can be compacted. You may pass other allocations but it makes no sense - |
2934 | these will never be moved. |
2935 | - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or |
2936 | #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations |
2937 | passed to this function that come from such pools are ignored. |
2938 | - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or |
2939 | created as dedicated allocations for any other reason are also ignored. |
2940 | - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT |
2941 | flag can be compacted. If not persistently mapped, memory will be mapped |
2942 | temporarily inside this function if needed. |
2943 | - You must not pass same #VmaAllocation object multiple times in `pAllocations` array. |
2944 | |
2945 | The function also frees empty `VkDeviceMemory` blocks. |
2946 | |
2947 | Warning: This function may be time-consuming, so you shouldn't call it too often |
2948 | (like after every resource creation/destruction). |
2949 | You can call it on special occasions (like when reloading a game level or |
2950 | when you just destroyed a lot of objects). Calling it every frame may be OK, but |
2951 | you should measure that on your platform. |
2952 | |
2953 | For more information, see [Defragmentation](@ref defragmentation) chapter. |
2954 | */ |
2955 | VkResult vmaDefragment( |
2956 | VmaAllocator allocator, |
2957 | VmaAllocation* pAllocations, |
2958 | size_t allocationCount, |
2959 | VkBool32* pAllocationsChanged, |
2960 | const VmaDefragmentationInfo *pDefragmentationInfo, |
2961 | VmaDefragmentationStats* pDefragmentationStats); |
2962 | |
2963 | /** \brief Binds buffer to allocation. |
2964 | |
2965 | Binds specified buffer to region of memory represented by specified allocation. |
2966 | Gets `VkDeviceMemory` handle and offset from the allocation. |
2967 | If you want to create a buffer, allocate memory for it and bind them together separately, |
2968 | you should use this function for binding instead of standard `vkBindBufferMemory()`, |
2969 | because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple |
2970 | allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously |
2971 | (which is illegal in Vulkan). |
2972 | |
2973 | It is recommended to use function vmaCreateBuffer() instead of this one. |
2974 | */ |
2975 | VkResult vmaBindBufferMemory( |
2976 | VmaAllocator allocator, |
2977 | VmaAllocation allocation, |
2978 | VkBuffer buffer); |
2979 | |
2980 | /** \brief Binds image to allocation. |
2981 | |
2982 | Binds specified image to region of memory represented by specified allocation. |
2983 | Gets `VkDeviceMemory` handle and offset from the allocation. |
2984 | If you want to create an image, allocate memory for it and bind them together separately, |
2985 | you should use this function for binding instead of standard `vkBindImageMemory()`, |
2986 | because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple |
2987 | allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously |
2988 | (which is illegal in Vulkan). |
2989 | |
2990 | It is recommended to use function vmaCreateImage() instead of this one. |
2991 | */ |
2992 | VkResult vmaBindImageMemory( |
2993 | VmaAllocator allocator, |
2994 | VmaAllocation allocation, |
2995 | VkImage image); |
2996 | |
2997 | /** |
2998 | @param[out] pBuffer Buffer that was created. |
2999 | @param[out] pAllocation Allocation that was created. |
3000 | @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). |
3001 | |
3002 | This function automatically: |
3003 | |
3004 | -# Creates buffer. |
3005 | -# Allocates appropriate memory for it. |
3006 | -# Binds the buffer with the memory. |
3007 | |
3008 | If any of these operations fail, buffer and allocation are not created, |
3009 | returned value is negative error code, *pBuffer and *pAllocation are null. |
3010 | |
3011 | If the function succeeded, you must destroy both buffer and allocation when you |
3012 | no longer need them using either convenience function vmaDestroyBuffer() or |
3013 | separately, using `vkDestroyBuffer()` and vmaFreeMemory(). |
3014 | |
3015 | If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used, |
3016 | VK_KHR_dedicated_allocation extension is used internally to query driver whether |
3017 | it requires or prefers the new buffer to have dedicated allocation. If yes, |
3018 | and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null |
3019 | and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated |
3020 | allocation for this buffer, just like when using |
3021 | VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. |
3022 | */ |
3023 | VkResult vmaCreateBuffer( |
3024 | VmaAllocator allocator, |
3025 | const VkBufferCreateInfo* pBufferCreateInfo, |
3026 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
3027 | VkBuffer* pBuffer, |
3028 | VmaAllocation* pAllocation, |
3029 | VmaAllocationInfo* pAllocationInfo); |
3030 | |
3031 | /** \brief Destroys Vulkan buffer and frees allocated memory. |
3032 | |
3033 | This is just a convenience function equivalent to: |
3034 | |
3035 | \code |
3036 | vkDestroyBuffer(device, buffer, allocationCallbacks); |
3037 | vmaFreeMemory(allocator, allocation); |
3038 | \endcode |
3039 | |
3040 | It it safe to pass null as buffer and/or allocation. |
3041 | */ |
3042 | void vmaDestroyBuffer( |
3043 | VmaAllocator allocator, |
3044 | VkBuffer buffer, |
3045 | VmaAllocation allocation); |
3046 | |
3047 | /// Function similar to vmaCreateBuffer(). |
3048 | VkResult vmaCreateImage( |
3049 | VmaAllocator allocator, |
3050 | const VkImageCreateInfo* pImageCreateInfo, |
3051 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
3052 | VkImage* pImage, |
3053 | VmaAllocation* pAllocation, |
3054 | VmaAllocationInfo* pAllocationInfo); |
3055 | |
3056 | /** \brief Destroys Vulkan image and frees allocated memory. |
3057 | |
3058 | This is just a convenience function equivalent to: |
3059 | |
3060 | \code |
3061 | vkDestroyImage(device, image, allocationCallbacks); |
3062 | vmaFreeMemory(allocator, allocation); |
3063 | \endcode |
3064 | |
3065 | It it safe to pass null as image and/or allocation. |
3066 | */ |
3067 | void vmaDestroyImage( |
3068 | VmaAllocator allocator, |
3069 | VkImage image, |
3070 | VmaAllocation allocation); |
3071 | |
3072 | #ifdef __cplusplus |
3073 | } |
3074 | #endif |
3075 | |
3076 | #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H |
3077 | |
3078 | // For Visual Studio IntelliSense. |
3079 | #if defined(__cplusplus) && defined(__INTELLISENSE__) |
3080 | #define VMA_IMPLEMENTATION |
3081 | #endif |
3082 | |
3083 | #ifdef VMA_IMPLEMENTATION |
3084 | #undef VMA_IMPLEMENTATION |
3085 | |
3086 | #include <cstdint> |
3087 | #include <cstdlib> |
3088 | #include <cstring> |
3089 | |
3090 | /******************************************************************************* |
3091 | CONFIGURATION SECTION |
3092 | |
3093 | Define some of these macros before each #include of this header or change them |
3094 | here if you need other then default behavior depending on your environment. |
3095 | */ |
3096 | |
3097 | /* |
3098 | Define this macro to 1 to make the library fetch pointers to Vulkan functions |
3099 | internally, like: |
3100 | |
3101 | vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; |
3102 | |
3103 | Define to 0 if you are going to provide you own pointers to Vulkan functions via |
3104 | VmaAllocatorCreateInfo::pVulkanFunctions. |
3105 | */ |
3106 | #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES) |
3107 | #define VMA_STATIC_VULKAN_FUNCTIONS 1 |
3108 | #endif |
3109 | |
3110 | // Define this macro to 1 to make the library use STL containers instead of its own implementation. |
3111 | //#define VMA_USE_STL_CONTAINERS 1 |
3112 | |
3113 | /* Set this macro to 1 to make the library including and using STL containers: |
3114 | std::pair, std::vector, std::list, std::unordered_map. |
3115 | |
3116 | Set it to 0 or undefined to make the library using its own implementation of |
3117 | the containers. |
3118 | */ |
3119 | #if VMA_USE_STL_CONTAINERS |
3120 | #define VMA_USE_STL_VECTOR 1 |
3121 | #define VMA_USE_STL_UNORDERED_MAP 1 |
3122 | #define VMA_USE_STL_LIST 1 |
3123 | #endif |
3124 | |
3125 | #ifndef VMA_USE_STL_SHARED_MUTEX |
3126 | // Minimum Visual Studio 2015 Update 2 |
3127 | #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 |
3128 | #define VMA_USE_STL_SHARED_MUTEX 1 |
3129 | #endif |
3130 | #endif |
3131 | |
3132 | #if VMA_USE_STL_VECTOR |
3133 | #include <vector> |
3134 | #endif |
3135 | |
3136 | #if VMA_USE_STL_UNORDERED_MAP |
3137 | #include <unordered_map> |
3138 | #endif |
3139 | |
3140 | #if VMA_USE_STL_LIST |
3141 | #include <list> |
3142 | #endif |
3143 | |
3144 | /* |
3145 | Following headers are used in this CONFIGURATION section only, so feel free to |
3146 | remove them if not needed. |
3147 | */ |
3148 | #include <cassert> // for assert |
3149 | #include <algorithm> // for min, max |
3150 | #include <mutex> |
3151 | #include <atomic> // for std::atomic |
3152 | |
3153 | #ifndef VMA_NULL |
3154 | // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. |
3155 | #define VMA_NULL nullptr |
3156 | #endif |
3157 | |
3158 | #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16) |
3159 | #include <cstdlib> |
3160 | void *aligned_alloc(size_t alignment, size_t size) |
3161 | { |
3162 | // alignment must be >= sizeof(void*) |
3163 | if(alignment < sizeof(void*)) |
3164 | { |
3165 | alignment = sizeof(void*); |
3166 | } |
3167 | |
3168 | return memalign(alignment, size); |
3169 | } |
3170 | #elif defined(__ANDROID__) |
3171 | #include <cstdlib> |
3172 | void *aligned_alloc(size_t alignment, size_t size) |
3173 | { |
3174 | // alignment must be >= sizeof(void*) |
3175 | if(alignment < sizeof(void*)) |
3176 | { |
3177 | alignment = sizeof(void*); |
3178 | } |
3179 | |
3180 | void *pointer; |
3181 | if(posix_memalign(&pointer, alignment, size) == 0) |
3182 | return pointer; |
3183 | return VMA_NULL; |
3184 | } |
3185 | #elif defined(__APPLE__) |
3186 | #include <cstdlib> |
3187 | // aligned_alloc() is marked as macOS 10.15 only in the 10.15 SDK, |
3188 | // avoid the mess by using a different name |
3189 | void *vma_aligned_alloc(size_t alignment, size_t size) |
3190 | { |
3191 | // alignment must be >= sizeof(void*) |
3192 | if(alignment < sizeof(void*)) |
3193 | { |
3194 | alignment = sizeof(void*); |
3195 | } |
3196 | |
3197 | void *pointer; |
3198 | if(posix_memalign(&pointer, alignment, size) == 0) |
3199 | return pointer; |
3200 | return VMA_NULL; |
3201 | } |
3202 | #endif |
3203 | |
3204 | // If your compiler is not compatible with C++11 and definition of |
3205 | // aligned_alloc() function is missing, uncommeting following line may help: |
3206 | |
3207 | //#include <malloc.h> |
3208 | |
3209 | // Normal assert to check for programmer's errors, especially in Debug configuration. |
3210 | #ifndef VMA_ASSERT |
3211 | #ifdef _DEBUG |
3212 | #define VMA_ASSERT(expr) assert(expr) |
3213 | #else |
3214 | #define VMA_ASSERT(expr) |
3215 | #endif |
3216 | #endif |
3217 | |
3218 | // Assert that will be called very often, like inside data structures e.g. operator[]. |
3219 | // Making it non-empty can make program slow. |
3220 | #ifndef VMA_HEAVY_ASSERT |
3221 | #ifdef _DEBUG |
3222 | #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) |
3223 | #else |
3224 | #define VMA_HEAVY_ASSERT(expr) |
3225 | #endif |
3226 | #endif |
3227 | |
3228 | #ifndef VMA_ALIGN_OF |
3229 | #define VMA_ALIGN_OF(type) (__alignof(type)) |
3230 | #endif |
3231 | |
3232 | #ifndef VMA_SYSTEM_ALIGNED_MALLOC |
3233 | #if defined(_WIN32) |
3234 | #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment))) |
3235 | #elif defined(__APPLE__) |
3236 | #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (vma_aligned_alloc((alignment), (size) )) |
3237 | #else |
3238 | #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) )) |
3239 | #endif |
3240 | #endif |
3241 | |
3242 | #ifndef VMA_SYSTEM_FREE |
3243 | #if defined(_WIN32) |
3244 | #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr) |
3245 | #else |
3246 | #define VMA_SYSTEM_FREE(ptr) free(ptr) |
3247 | #endif |
3248 | #endif |
3249 | |
3250 | #ifndef VMA_MIN |
3251 | #define VMA_MIN(v1, v2) (std::min((v1), (v2))) |
3252 | #endif |
3253 | |
3254 | #ifndef VMA_MAX |
3255 | #define VMA_MAX(v1, v2) (std::max((v1), (v2))) |
3256 | #endif |
3257 | |
3258 | #ifndef VMA_SWAP |
3259 | #define VMA_SWAP(v1, v2) std::swap((v1), (v2)) |
3260 | #endif |
3261 | |
3262 | #ifndef VMA_SORT |
3263 | #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp) |
3264 | #endif |
3265 | |
3266 | #ifndef VMA_DEBUG_LOG |
3267 | #define VMA_DEBUG_LOG(format, ...) |
3268 | /* |
3269 | #define VMA_DEBUG_LOG(format, ...) do { \ |
3270 | printf(format, __VA_ARGS__); \ |
3271 | printf("\n"); \ |
3272 | } while(false) |
3273 | */ |
3274 | #endif |
3275 | |
3276 | // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString. |
3277 | #if VMA_STATS_STRING_ENABLED |
3278 | static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num) |
3279 | { |
3280 | snprintf(s: outStr, maxlen: strLen, format: "%u" , static_cast<unsigned int>(num)); |
3281 | } |
3282 | static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num) |
3283 | { |
3284 | snprintf(s: outStr, maxlen: strLen, format: "%llu" , static_cast<unsigned long long>(num)); |
3285 | } |
3286 | static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr) |
3287 | { |
3288 | snprintf(s: outStr, maxlen: strLen, format: "%p" , ptr); |
3289 | } |
3290 | #endif |
3291 | |
3292 | #ifndef VMA_MUTEX |
3293 | class VmaMutex |
3294 | { |
3295 | public: |
3296 | void Lock() { m_Mutex.lock(); } |
3297 | void Unlock() { m_Mutex.unlock(); } |
3298 | private: |
3299 | std::mutex m_Mutex; |
3300 | }; |
3301 | #define VMA_MUTEX VmaMutex |
3302 | #endif |
3303 | |
3304 | // Read-write mutex, where "read" is shared access, "write" is exclusive access. |
3305 | #ifndef VMA_RW_MUTEX |
3306 | #if VMA_USE_STL_SHARED_MUTEX |
3307 | // Use std::shared_mutex from C++17. |
3308 | #include <shared_mutex> |
3309 | class VmaRWMutex |
3310 | { |
3311 | public: |
3312 | void LockRead() { m_Mutex.lock_shared(); } |
3313 | void UnlockRead() { m_Mutex.unlock_shared(); } |
3314 | void LockWrite() { m_Mutex.lock(); } |
3315 | void UnlockWrite() { m_Mutex.unlock(); } |
3316 | private: |
3317 | std::shared_mutex m_Mutex; |
3318 | }; |
3319 | #define VMA_RW_MUTEX VmaRWMutex |
3320 | #elif defined(_WIN32) && !defined(__MINGW32__) |
3321 | // Use SRWLOCK from WinAPI. |
3322 | class VmaRWMutex |
3323 | { |
3324 | public: |
3325 | VmaRWMutex() { InitializeSRWLock(&m_Lock); } |
3326 | void LockRead() { AcquireSRWLockShared(&m_Lock); } |
3327 | void UnlockRead() { ReleaseSRWLockShared(&m_Lock); } |
3328 | void LockWrite() { AcquireSRWLockExclusive(&m_Lock); } |
3329 | void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); } |
3330 | private: |
3331 | SRWLOCK m_Lock; |
3332 | }; |
3333 | #define VMA_RW_MUTEX VmaRWMutex |
3334 | #else |
3335 | // Less efficient fallback: Use normal mutex. |
3336 | class VmaRWMutex |
3337 | { |
3338 | public: |
3339 | void LockRead() { m_Mutex.Lock(); } |
3340 | void UnlockRead() { m_Mutex.Unlock(); } |
3341 | void LockWrite() { m_Mutex.Lock(); } |
3342 | void UnlockWrite() { m_Mutex.Unlock(); } |
3343 | private: |
3344 | VMA_MUTEX m_Mutex; |
3345 | }; |
3346 | #define VMA_RW_MUTEX VmaRWMutex |
3347 | #endif // #if VMA_USE_STL_SHARED_MUTEX |
3348 | #endif // #ifndef VMA_RW_MUTEX |
3349 | |
3350 | /* |
3351 | If providing your own implementation, you need to implement a subset of std::atomic: |
3352 | |
3353 | - Constructor(uint32_t desired) |
3354 | - uint32_t load() const |
3355 | - void store(uint32_t desired) |
3356 | - bool compare_exchange_weak(uint32_t& expected, uint32_t desired) |
3357 | */ |
3358 | #ifndef VMA_ATOMIC_UINT32 |
3359 | #define VMA_ATOMIC_UINT32 std::atomic<uint32_t> |
3360 | #endif |
3361 | |
3362 | #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY |
3363 | /** |
3364 | Every allocation will have its own memory block. |
3365 | Define to 1 for debugging purposes only. |
3366 | */ |
3367 | #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0) |
3368 | #endif |
3369 | |
3370 | #ifndef VMA_DEBUG_ALIGNMENT |
3371 | /** |
3372 | Minimum alignment of all allocations, in bytes. |
3373 | Set to more than 1 for debugging purposes only. Must be power of two. |
3374 | */ |
3375 | #define VMA_DEBUG_ALIGNMENT (1) |
3376 | #endif |
3377 | |
3378 | #ifndef VMA_DEBUG_MARGIN |
3379 | /** |
3380 | Minimum margin before and after every allocation, in bytes. |
3381 | Set nonzero for debugging purposes only. |
3382 | */ |
3383 | #define VMA_DEBUG_MARGIN (0) |
3384 | #endif |
3385 | |
3386 | #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS |
3387 | /** |
3388 | Define this macro to 1 to automatically fill new allocations and destroyed |
3389 | allocations with some bit pattern. |
3390 | */ |
3391 | #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0) |
3392 | #endif |
3393 | |
3394 | #ifndef VMA_DEBUG_DETECT_CORRUPTION |
3395 | /** |
3396 | Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to |
3397 | enable writing magic value to the margin before and after every allocation and |
3398 | validating it, so that memory corruptions (out-of-bounds writes) are detected. |
3399 | */ |
3400 | #define VMA_DEBUG_DETECT_CORRUPTION (0) |
3401 | #endif |
3402 | |
3403 | #ifndef VMA_DEBUG_GLOBAL_MUTEX |
3404 | /** |
3405 | Set this to 1 for debugging purposes only, to enable single mutex protecting all |
3406 | entry calls to the library. Can be useful for debugging multithreading issues. |
3407 | */ |
3408 | #define VMA_DEBUG_GLOBAL_MUTEX (0) |
3409 | #endif |
3410 | |
3411 | #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY |
3412 | /** |
3413 | Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity. |
3414 | Set to more than 1 for debugging purposes only. Must be power of two. |
3415 | */ |
3416 | #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1) |
3417 | #endif |
3418 | |
3419 | #ifndef VMA_SMALL_HEAP_MAX_SIZE |
3420 | /// Maximum size of a memory heap in Vulkan to consider it "small". |
3421 | #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024) |
3422 | #endif |
3423 | |
3424 | #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE |
3425 | /// Default size of a block allocated as single VkDeviceMemory from a "large" heap. |
3426 | #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024) |
3427 | #endif |
3428 | |
3429 | #ifndef VMA_CLASS_NO_COPY |
3430 | #define VMA_CLASS_NO_COPY(className) \ |
3431 | private: \ |
3432 | className(const className&) = delete; \ |
3433 | className& operator=(const className&) = delete; |
3434 | #endif |
3435 | |
3436 | static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX; |
3437 | |
3438 | // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F. |
3439 | static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666; |
3440 | |
3441 | static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC; |
3442 | static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF; |
3443 | |
3444 | /******************************************************************************* |
3445 | END OF CONFIGURATION |
3446 | */ |
3447 | |
3448 | static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u; |
3449 | |
3450 | static VkAllocationCallbacks VmaEmptyAllocationCallbacks = { |
3451 | VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; |
3452 | |
3453 | // Returns number of bits set to 1 in (v). |
3454 | static inline uint32_t VmaCountBitsSet(uint32_t v) |
3455 | { |
3456 | uint32_t c = v - ((v >> 1) & 0x55555555); |
3457 | c = ((c >> 2) & 0x33333333) + (c & 0x33333333); |
3458 | c = ((c >> 4) + c) & 0x0F0F0F0F; |
3459 | c = ((c >> 8) + c) & 0x00FF00FF; |
3460 | c = ((c >> 16) + c) & 0x0000FFFF; |
3461 | return c; |
3462 | } |
3463 | |
3464 | // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16. |
3465 | // Use types like uint32_t, uint64_t as T. |
3466 | template <typename T> |
3467 | static inline T VmaAlignUp(T val, T align) |
3468 | { |
3469 | return (val + align - 1) / align * align; |
3470 | } |
3471 | // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8. |
3472 | // Use types like uint32_t, uint64_t as T. |
3473 | template <typename T> |
3474 | static inline T VmaAlignDown(T val, T align) |
3475 | { |
3476 | return val / align * align; |
3477 | } |
3478 | |
3479 | // Division with mathematical rounding to nearest number. |
3480 | template <typename T> |
3481 | static inline T VmaRoundDiv(T x, T y) |
3482 | { |
3483 | return (x + (y / (T)2)) / y; |
3484 | } |
3485 | |
3486 | /* |
3487 | Returns true if given number is a power of two. |
3488 | T must be unsigned integer number or signed integer but always nonnegative. |
3489 | For 0 returns true. |
3490 | */ |
3491 | template <typename T> |
3492 | inline bool VmaIsPow2(T x) |
3493 | { |
3494 | return (x & (x-1)) == 0; |
3495 | } |
3496 | |
3497 | // Returns smallest power of 2 greater or equal to v. |
3498 | static inline uint32_t VmaNextPow2(uint32_t v) |
3499 | { |
3500 | v--; |
3501 | v |= v >> 1; |
3502 | v |= v >> 2; |
3503 | v |= v >> 4; |
3504 | v |= v >> 8; |
3505 | v |= v >> 16; |
3506 | v++; |
3507 | return v; |
3508 | } |
3509 | static inline uint64_t VmaNextPow2(uint64_t v) |
3510 | { |
3511 | v--; |
3512 | v |= v >> 1; |
3513 | v |= v >> 2; |
3514 | v |= v >> 4; |
3515 | v |= v >> 8; |
3516 | v |= v >> 16; |
3517 | v |= v >> 32; |
3518 | v++; |
3519 | return v; |
3520 | } |
3521 | |
3522 | // Returns largest power of 2 less or equal to v. |
3523 | static inline uint32_t VmaPrevPow2(uint32_t v) |
3524 | { |
3525 | v |= v >> 1; |
3526 | v |= v >> 2; |
3527 | v |= v >> 4; |
3528 | v |= v >> 8; |
3529 | v |= v >> 16; |
3530 | v = v ^ (v >> 1); |
3531 | return v; |
3532 | } |
3533 | static inline uint64_t VmaPrevPow2(uint64_t v) |
3534 | { |
3535 | v |= v >> 1; |
3536 | v |= v >> 2; |
3537 | v |= v >> 4; |
3538 | v |= v >> 8; |
3539 | v |= v >> 16; |
3540 | v |= v >> 32; |
3541 | v = v ^ (v >> 1); |
3542 | return v; |
3543 | } |
3544 | |
3545 | static inline bool VmaStrIsEmpty(const char* pStr) |
3546 | { |
3547 | return pStr == VMA_NULL || *pStr == '\0'; |
3548 | } |
3549 | |
3550 | static const char* VmaAlgorithmToStr(uint32_t algorithm) |
3551 | { |
3552 | switch(algorithm) |
3553 | { |
3554 | case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: |
3555 | return "Linear" ; |
3556 | case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT: |
3557 | return "Buddy" ; |
3558 | case 0: |
3559 | return "Default" ; |
3560 | default: |
3561 | VMA_ASSERT(0); |
3562 | return "" ; |
3563 | } |
3564 | } |
3565 | |
3566 | #ifndef VMA_SORT |
3567 | |
3568 | template<typename Iterator, typename Compare> |
3569 | Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp) |
3570 | { |
3571 | Iterator centerValue = end; --centerValue; |
3572 | Iterator insertIndex = beg; |
3573 | for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex) |
3574 | { |
3575 | if(cmp(*memTypeIndex, *centerValue)) |
3576 | { |
3577 | if(insertIndex != memTypeIndex) |
3578 | { |
3579 | VMA_SWAP(*memTypeIndex, *insertIndex); |
3580 | } |
3581 | ++insertIndex; |
3582 | } |
3583 | } |
3584 | if(insertIndex != centerValue) |
3585 | { |
3586 | VMA_SWAP(*insertIndex, *centerValue); |
3587 | } |
3588 | return insertIndex; |
3589 | } |
3590 | |
3591 | template<typename Iterator, typename Compare> |
3592 | void VmaQuickSort(Iterator beg, Iterator end, Compare cmp) |
3593 | { |
3594 | if(beg < end) |
3595 | { |
3596 | Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp); |
3597 | VmaQuickSort<Iterator, Compare>(beg, it, cmp); |
3598 | VmaQuickSort<Iterator, Compare>(it + 1, end, cmp); |
3599 | } |
3600 | } |
3601 | |
3602 | #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp) |
3603 | |
3604 | #endif // #ifndef VMA_SORT |
3605 | |
3606 | /* |
3607 | Returns true if two memory blocks occupy overlapping pages. |
3608 | ResourceA must be in less memory offset than ResourceB. |
3609 | |
3610 | Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)" |
3611 | chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity". |
3612 | */ |
3613 | static inline bool VmaBlocksOnSamePage( |
3614 | VkDeviceSize resourceAOffset, |
3615 | VkDeviceSize resourceASize, |
3616 | VkDeviceSize resourceBOffset, |
3617 | VkDeviceSize pageSize) |
3618 | { |
3619 | VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0); |
3620 | VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1; |
3621 | VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1); |
3622 | VkDeviceSize resourceBStart = resourceBOffset; |
3623 | VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1); |
3624 | return resourceAEndPage == resourceBStartPage; |
3625 | } |
3626 | |
3627 | enum VmaSuballocationType |
3628 | { |
3629 | VMA_SUBALLOCATION_TYPE_FREE = 0, |
3630 | VMA_SUBALLOCATION_TYPE_UNKNOWN = 1, |
3631 | VMA_SUBALLOCATION_TYPE_BUFFER = 2, |
3632 | VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3, |
3633 | VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4, |
3634 | VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5, |
3635 | VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF |
3636 | }; |
3637 | |
3638 | /* |
3639 | Returns true if given suballocation types could conflict and must respect |
3640 | VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer |
3641 | or linear image and another one is optimal image. If type is unknown, behave |
3642 | conservatively. |
3643 | */ |
3644 | static inline bool VmaIsBufferImageGranularityConflict( |
3645 | VmaSuballocationType suballocType1, |
3646 | VmaSuballocationType suballocType2) |
3647 | { |
3648 | if(suballocType1 > suballocType2) |
3649 | { |
3650 | VMA_SWAP(suballocType1, suballocType2); |
3651 | } |
3652 | |
3653 | switch(suballocType1) |
3654 | { |
3655 | case VMA_SUBALLOCATION_TYPE_FREE: |
3656 | return false; |
3657 | case VMA_SUBALLOCATION_TYPE_UNKNOWN: |
3658 | return true; |
3659 | case VMA_SUBALLOCATION_TYPE_BUFFER: |
3660 | return |
3661 | suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || |
3662 | suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; |
3663 | case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN: |
3664 | return |
3665 | suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || |
3666 | suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR || |
3667 | suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; |
3668 | case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR: |
3669 | return |
3670 | suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; |
3671 | case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL: |
3672 | return false; |
3673 | default: |
3674 | VMA_ASSERT(0); |
3675 | return true; |
3676 | } |
3677 | } |
3678 | |
3679 | static void VmaWriteMagicValue(void* pData, VkDeviceSize offset) |
3680 | { |
3681 | uint32_t* pDst = (uint32_t*)((char*)pData + offset); |
3682 | const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); |
3683 | for(size_t i = 0; i != numberCount; ++i, ++pDst) |
3684 | { |
3685 | *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE; |
3686 | } |
3687 | } |
3688 | |
3689 | static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset) |
3690 | { |
3691 | const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset); |
3692 | const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); |
3693 | for(size_t i = 0; i != numberCount; ++i, ++pSrc) |
3694 | { |
3695 | if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE) |
3696 | { |
3697 | return false; |
3698 | } |
3699 | } |
3700 | return true; |
3701 | } |
3702 | |
3703 | // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope). |
3704 | struct VmaMutexLock |
3705 | { |
3706 | VMA_CLASS_NO_COPY(VmaMutexLock) |
3707 | public: |
3708 | VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) : |
3709 | m_pMutex(useMutex ? &mutex : VMA_NULL) |
3710 | { if(m_pMutex) { m_pMutex->Lock(); } } |
3711 | ~VmaMutexLock() |
3712 | { if(m_pMutex) { m_pMutex->Unlock(); } } |
3713 | private: |
3714 | VMA_MUTEX* m_pMutex; |
3715 | }; |
3716 | |
3717 | // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading. |
3718 | struct VmaMutexLockRead |
3719 | { |
3720 | VMA_CLASS_NO_COPY(VmaMutexLockRead) |
3721 | public: |
3722 | VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) : |
3723 | m_pMutex(useMutex ? &mutex : VMA_NULL) |
3724 | { if(m_pMutex) { m_pMutex->LockRead(); } } |
3725 | ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } } |
3726 | private: |
3727 | VMA_RW_MUTEX* m_pMutex; |
3728 | }; |
3729 | |
3730 | // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing. |
3731 | struct VmaMutexLockWrite |
3732 | { |
3733 | VMA_CLASS_NO_COPY(VmaMutexLockWrite) |
3734 | public: |
3735 | VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) : |
3736 | m_pMutex(useMutex ? &mutex : VMA_NULL) |
3737 | { if(m_pMutex) { m_pMutex->LockWrite(); } } |
3738 | ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } } |
3739 | private: |
3740 | VMA_RW_MUTEX* m_pMutex; |
3741 | }; |
3742 | |
3743 | #if VMA_DEBUG_GLOBAL_MUTEX |
3744 | static VMA_MUTEX gDebugGlobalMutex; |
3745 | #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true); |
3746 | #else |
3747 | #define VMA_DEBUG_GLOBAL_MUTEX_LOCK |
3748 | #endif |
3749 | |
3750 | // Minimum size of a free suballocation to register it in the free suballocation collection. |
3751 | static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16; |
3752 | |
3753 | /* |
3754 | Performs binary search and returns iterator to first element that is greater or |
3755 | equal to (key), according to comparison (cmp). |
3756 | |
3757 | Cmp should return true if first argument is less than second argument. |
3758 | |
3759 | Returned value is the found element, if present in the collection or place where |
3760 | new element with value (key) should be inserted. |
3761 | */ |
3762 | template <typename CmpLess, typename IterT, typename KeyT> |
3763 | static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp) |
3764 | { |
3765 | size_t down = 0, up = (end - beg); |
3766 | while(down < up) |
3767 | { |
3768 | const size_t mid = (down + up) / 2; |
3769 | if(cmp(*(beg+mid), key)) |
3770 | { |
3771 | down = mid + 1; |
3772 | } |
3773 | else |
3774 | { |
3775 | up = mid; |
3776 | } |
3777 | } |
3778 | return beg + down; |
3779 | } |
3780 | |
3781 | /* |
3782 | Returns true if all pointers in the array are not-null and unique. |
3783 | Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT. |
3784 | T must be pointer type, e.g. VmaAllocation, VmaPool. |
3785 | */ |
3786 | template<typename T> |
3787 | static bool VmaValidatePointerArray(uint32_t count, const T* arr) |
3788 | { |
3789 | for(uint32_t i = 0; i < count; ++i) |
3790 | { |
3791 | const T iPtr = arr[i]; |
3792 | if(iPtr == VMA_NULL) |
3793 | { |
3794 | return false; |
3795 | } |
3796 | for(uint32_t j = i + 1; j < count; ++j) |
3797 | { |
3798 | if(iPtr == arr[j]) |
3799 | { |
3800 | return false; |
3801 | } |
3802 | } |
3803 | } |
3804 | return true; |
3805 | } |
3806 | |
3807 | //////////////////////////////////////////////////////////////////////////////// |
3808 | // Memory allocation |
3809 | |
3810 | static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment) |
3811 | { |
3812 | if((pAllocationCallbacks != VMA_NULL) && |
3813 | (pAllocationCallbacks->pfnAllocation != VMA_NULL)) |
3814 | { |
3815 | return (*pAllocationCallbacks->pfnAllocation)( |
3816 | pAllocationCallbacks->pUserData, |
3817 | size, |
3818 | alignment, |
3819 | VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); |
3820 | } |
3821 | else |
3822 | { |
3823 | return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment); |
3824 | } |
3825 | } |
3826 | |
3827 | static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr) |
3828 | { |
3829 | if((pAllocationCallbacks != VMA_NULL) && |
3830 | (pAllocationCallbacks->pfnFree != VMA_NULL)) |
3831 | { |
3832 | (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr); |
3833 | } |
3834 | else |
3835 | { |
3836 | VMA_SYSTEM_FREE(ptr); |
3837 | } |
3838 | } |
3839 | |
3840 | template<typename T> |
3841 | static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks) |
3842 | { |
3843 | return (T*)VmaMalloc(pAllocationCallbacks, size: sizeof(T), VMA_ALIGN_OF(T)); |
3844 | } |
3845 | |
3846 | template<typename T> |
3847 | static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count) |
3848 | { |
3849 | return (T*)VmaMalloc(pAllocationCallbacks, size: sizeof(T) * count, VMA_ALIGN_OF(T)); |
3850 | } |
3851 | |
3852 | #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type) |
3853 | |
3854 | #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type) |
3855 | |
3856 | template<typename T> |
3857 | static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr) |
3858 | { |
3859 | ptr->~T(); |
3860 | VmaFree(pAllocationCallbacks, ptr); |
3861 | } |
3862 | |
3863 | template<typename T> |
3864 | static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count) |
3865 | { |
3866 | if(ptr != VMA_NULL) |
3867 | { |
3868 | for(size_t i = count; i--; ) |
3869 | { |
3870 | ptr[i].~T(); |
3871 | } |
3872 | VmaFree(pAllocationCallbacks, ptr); |
3873 | } |
3874 | } |
3875 | |
3876 | // STL-compatible allocator. |
3877 | template<typename T> |
3878 | class VmaStlAllocator |
3879 | { |
3880 | public: |
3881 | const VkAllocationCallbacks* const m_pCallbacks; |
3882 | typedef T value_type; |
3883 | |
3884 | VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { } |
3885 | template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { } |
3886 | |
3887 | T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); } |
3888 | void deallocate(T* p, size_t /*n*/) { VmaFree(m_pCallbacks, p); } |
3889 | |
3890 | template<typename U> |
3891 | bool operator==(const VmaStlAllocator<U>& rhs) const |
3892 | { |
3893 | return m_pCallbacks == rhs.m_pCallbacks; |
3894 | } |
3895 | template<typename U> |
3896 | bool operator!=(const VmaStlAllocator<U>& rhs) const |
3897 | { |
3898 | return m_pCallbacks != rhs.m_pCallbacks; |
3899 | } |
3900 | |
3901 | VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete; |
3902 | }; |
3903 | |
3904 | #if VMA_USE_STL_VECTOR |
3905 | |
3906 | #define VmaVector std::vector |
3907 | |
3908 | template<typename T, typename allocatorT> |
3909 | static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item) |
3910 | { |
3911 | vec.insert(vec.begin() + index, item); |
3912 | } |
3913 | |
3914 | template<typename T, typename allocatorT> |
3915 | static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index) |
3916 | { |
3917 | vec.erase(vec.begin() + index); |
3918 | } |
3919 | |
3920 | #else // #if VMA_USE_STL_VECTOR |
3921 | |
3922 | /* Class with interface compatible with subset of std::vector. |
3923 | T must be POD because constructors and destructors are not called and memcpy is |
3924 | used for these objects. */ |
3925 | template<typename T, typename AllocatorT> |
3926 | class VmaVector |
3927 | { |
3928 | public: |
3929 | typedef T value_type; |
3930 | |
3931 | VmaVector(const AllocatorT& allocator) : |
3932 | m_Allocator(allocator), |
3933 | m_pArray(VMA_NULL), |
3934 | m_Count(0), |
3935 | m_Capacity(0) |
3936 | { |
3937 | } |
3938 | |
3939 | VmaVector(size_t count, const AllocatorT& allocator) : |
3940 | m_Allocator(allocator), |
3941 | m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL), |
3942 | m_Count(count), |
3943 | m_Capacity(count) |
3944 | { |
3945 | } |
3946 | |
3947 | VmaVector(const VmaVector<T, AllocatorT>& src) : |
3948 | m_Allocator(src.m_Allocator), |
3949 | m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL), |
3950 | m_Count(src.m_Count), |
3951 | m_Capacity(src.m_Count) |
3952 | { |
3953 | if(m_Count != 0) |
3954 | { |
3955 | memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T)); |
3956 | } |
3957 | } |
3958 | |
3959 | ~VmaVector() |
3960 | { |
3961 | VmaFree(m_Allocator.m_pCallbacks, m_pArray); |
3962 | } |
3963 | |
3964 | VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs) |
3965 | { |
3966 | if(&rhs != this) |
3967 | { |
3968 | resize(newCount: rhs.m_Count); |
3969 | if(m_Count != 0) |
3970 | { |
3971 | memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T)); |
3972 | } |
3973 | } |
3974 | return *this; |
3975 | } |
3976 | |
3977 | bool empty() const { return m_Count == 0; } |
3978 | size_t size() const { return m_Count; } |
3979 | T* data() { return m_pArray; } |
3980 | const T* data() const { return m_pArray; } |
3981 | |
3982 | T& operator[](size_t index) |
3983 | { |
3984 | VMA_HEAVY_ASSERT(index < m_Count); |
3985 | return m_pArray[index]; |
3986 | } |
3987 | const T& operator[](size_t index) const |
3988 | { |
3989 | VMA_HEAVY_ASSERT(index < m_Count); |
3990 | return m_pArray[index]; |
3991 | } |
3992 | |
3993 | T& front() |
3994 | { |
3995 | VMA_HEAVY_ASSERT(m_Count > 0); |
3996 | return m_pArray[0]; |
3997 | } |
3998 | const T& front() const |
3999 | { |
4000 | VMA_HEAVY_ASSERT(m_Count > 0); |
4001 | return m_pArray[0]; |
4002 | } |
4003 | T& back() |
4004 | { |
4005 | VMA_HEAVY_ASSERT(m_Count > 0); |
4006 | return m_pArray[m_Count - 1]; |
4007 | } |
4008 | const T& back() const |
4009 | { |
4010 | VMA_HEAVY_ASSERT(m_Count > 0); |
4011 | return m_pArray[m_Count - 1]; |
4012 | } |
4013 | |
4014 | void reserve(size_t newCapacity, bool freeMemory = false) |
4015 | { |
4016 | newCapacity = VMA_MAX(newCapacity, m_Count); |
4017 | |
4018 | if((newCapacity < m_Capacity) && !freeMemory) |
4019 | { |
4020 | newCapacity = m_Capacity; |
4021 | } |
4022 | |
4023 | if(newCapacity != m_Capacity) |
4024 | { |
4025 | T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL; |
4026 | if(m_Count != 0) |
4027 | { |
4028 | memcpy(newArray, m_pArray, m_Count * sizeof(T)); |
4029 | } |
4030 | VmaFree(m_Allocator.m_pCallbacks, m_pArray); |
4031 | m_Capacity = newCapacity; |
4032 | m_pArray = newArray; |
4033 | } |
4034 | } |
4035 | |
4036 | void resize(size_t newCount, bool freeMemory = false) |
4037 | { |
4038 | size_t newCapacity = m_Capacity; |
4039 | if(newCount > m_Capacity) |
4040 | { |
4041 | newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8)); |
4042 | } |
4043 | else if(freeMemory) |
4044 | { |
4045 | newCapacity = newCount; |
4046 | } |
4047 | |
4048 | if(newCapacity != m_Capacity) |
4049 | { |
4050 | T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL; |
4051 | const size_t elementsToCopy = VMA_MIN(m_Count, newCount); |
4052 | if(elementsToCopy != 0) |
4053 | { |
4054 | memcpy(newArray, m_pArray, elementsToCopy * sizeof(T)); |
4055 | } |
4056 | VmaFree(m_Allocator.m_pCallbacks, m_pArray); |
4057 | m_Capacity = newCapacity; |
4058 | m_pArray = newArray; |
4059 | } |
4060 | |
4061 | m_Count = newCount; |
4062 | } |
4063 | |
4064 | void clear(bool freeMemory = false) |
4065 | { |
4066 | resize(newCount: 0, freeMemory); |
4067 | } |
4068 | |
4069 | void insert(size_t index, const T& src) |
4070 | { |
4071 | VMA_HEAVY_ASSERT(index <= m_Count); |
4072 | const size_t oldCount = size(); |
4073 | resize(newCount: oldCount + 1); |
4074 | if(index < oldCount) |
4075 | { |
4076 | memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T)); |
4077 | } |
4078 | m_pArray[index] = src; |
4079 | } |
4080 | |
4081 | void remove(size_t index) |
4082 | { |
4083 | VMA_HEAVY_ASSERT(index < m_Count); |
4084 | const size_t oldCount = size(); |
4085 | if(index < oldCount - 1) |
4086 | { |
4087 | memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T)); |
4088 | } |
4089 | resize(newCount: oldCount - 1); |
4090 | } |
4091 | |
4092 | void push_back(const T& src) |
4093 | { |
4094 | const size_t newIndex = size(); |
4095 | resize(newCount: newIndex + 1); |
4096 | m_pArray[newIndex] = src; |
4097 | } |
4098 | |
4099 | void pop_back() |
4100 | { |
4101 | VMA_HEAVY_ASSERT(m_Count > 0); |
4102 | resize(newCount: size() - 1); |
4103 | } |
4104 | |
4105 | void push_front(const T& src) |
4106 | { |
4107 | insert(index: 0, src); |
4108 | } |
4109 | |
4110 | void pop_front() |
4111 | { |
4112 | VMA_HEAVY_ASSERT(m_Count > 0); |
4113 | remove(index: 0); |
4114 | } |
4115 | |
4116 | typedef T* iterator; |
4117 | |
4118 | iterator begin() { return m_pArray; } |
4119 | iterator end() { return m_pArray + m_Count; } |
4120 | |
4121 | private: |
4122 | AllocatorT m_Allocator; |
4123 | T* m_pArray; |
4124 | size_t m_Count; |
4125 | size_t m_Capacity; |
4126 | }; |
4127 | |
4128 | template<typename T, typename allocatorT> |
4129 | static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item) |
4130 | { |
4131 | vec.insert(index, item); |
4132 | } |
4133 | |
4134 | template<typename T, typename allocatorT> |
4135 | static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index) |
4136 | { |
4137 | vec.remove(index); |
4138 | } |
4139 | |
4140 | #endif // #if VMA_USE_STL_VECTOR |
4141 | |
4142 | template<typename CmpLess, typename VectorT> |
4143 | size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value) |
4144 | { |
4145 | const size_t indexToInsert = VmaBinaryFindFirstNotLess( |
4146 | vector.data(), |
4147 | vector.data() + vector.size(), |
4148 | value, |
4149 | CmpLess()) - vector.data(); |
4150 | VmaVectorInsert(vector, indexToInsert, value); |
4151 | return indexToInsert; |
4152 | } |
4153 | |
4154 | template<typename CmpLess, typename VectorT> |
4155 | bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value) |
4156 | { |
4157 | CmpLess comparator; |
4158 | typename VectorT::iterator it = VmaBinaryFindFirstNotLess( |
4159 | vector.begin(), |
4160 | vector.end(), |
4161 | value, |
4162 | comparator); |
4163 | if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it)) |
4164 | { |
4165 | size_t indexToRemove = it - vector.begin(); |
4166 | VmaVectorRemove(vector, indexToRemove); |
4167 | return true; |
4168 | } |
4169 | return false; |
4170 | } |
4171 | |
4172 | template<typename CmpLess, typename IterT, typename KeyT> |
4173 | IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value) |
4174 | { |
4175 | CmpLess comparator; |
4176 | IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>( |
4177 | beg, end, value, comparator); |
4178 | if(it == end || |
4179 | (!comparator(*it, value) && !comparator(value, *it))) |
4180 | { |
4181 | return it; |
4182 | } |
4183 | return end; |
4184 | } |
4185 | |
4186 | //////////////////////////////////////////////////////////////////////////////// |
4187 | // class VmaPoolAllocator |
4188 | |
4189 | /* |
4190 | Allocator for objects of type T using a list of arrays (pools) to speed up |
4191 | allocation. Number of elements that can be allocated is not bounded because |
4192 | allocator can create multiple blocks. |
4193 | */ |
4194 | template<typename T> |
4195 | class VmaPoolAllocator |
4196 | { |
4197 | VMA_CLASS_NO_COPY(VmaPoolAllocator) |
4198 | public: |
4199 | VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock); |
4200 | ~VmaPoolAllocator(); |
4201 | void Clear(); |
4202 | T* Alloc(); |
4203 | void Free(T* ptr); |
4204 | |
4205 | private: |
4206 | union Item |
4207 | { |
4208 | uint32_t NextFreeIndex; |
4209 | T Value; |
4210 | }; |
4211 | |
4212 | struct ItemBlock |
4213 | { |
4214 | Item* pItems; |
4215 | uint32_t FirstFreeIndex; |
4216 | }; |
4217 | |
4218 | const VkAllocationCallbacks* m_pAllocationCallbacks; |
4219 | size_t m_ItemsPerBlock; |
4220 | VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks; |
4221 | |
4222 | ItemBlock& CreateNewBlock(); |
4223 | }; |
4224 | |
4225 | template<typename T> |
4226 | VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) : |
4227 | m_pAllocationCallbacks(pAllocationCallbacks), |
4228 | m_ItemsPerBlock(itemsPerBlock), |
4229 | m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks)) |
4230 | { |
4231 | VMA_ASSERT(itemsPerBlock > 0); |
4232 | } |
4233 | |
4234 | template<typename T> |
4235 | VmaPoolAllocator<T>::~VmaPoolAllocator() |
4236 | { |
4237 | Clear(); |
4238 | } |
4239 | |
4240 | template<typename T> |
4241 | void VmaPoolAllocator<T>::Clear() |
4242 | { |
4243 | for(size_t i = m_ItemBlocks.size(); i--; ) |
4244 | vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock); |
4245 | m_ItemBlocks.clear(); |
4246 | } |
4247 | |
4248 | template<typename T> |
4249 | T* VmaPoolAllocator<T>::Alloc() |
4250 | { |
4251 | for(size_t i = m_ItemBlocks.size(); i--; ) |
4252 | { |
4253 | ItemBlock& block = m_ItemBlocks[i]; |
4254 | // This block has some free items: Use first one. |
4255 | if(block.FirstFreeIndex != UINT32_MAX) |
4256 | { |
4257 | Item* const pItem = &block.pItems[block.FirstFreeIndex]; |
4258 | block.FirstFreeIndex = pItem->NextFreeIndex; |
4259 | return &pItem->Value; |
4260 | } |
4261 | } |
4262 | |
4263 | // No block has free item: Create new one and use it. |
4264 | ItemBlock& newBlock = CreateNewBlock(); |
4265 | Item* const pItem = &newBlock.pItems[0]; |
4266 | newBlock.FirstFreeIndex = pItem->NextFreeIndex; |
4267 | return &pItem->Value; |
4268 | } |
4269 | |
4270 | template<typename T> |
4271 | void VmaPoolAllocator<T>::Free(T* ptr) |
4272 | { |
4273 | // Search all memory blocks to find ptr. |
4274 | for(size_t i = 0; i < m_ItemBlocks.size(); ++i) |
4275 | { |
4276 | ItemBlock& block = m_ItemBlocks[i]; |
4277 | |
4278 | // Casting to union. |
4279 | Item* pItemPtr; |
4280 | memcpy(&pItemPtr, &ptr, sizeof(pItemPtr)); |
4281 | |
4282 | // Check if pItemPtr is in address range of this block. |
4283 | if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock)) |
4284 | { |
4285 | const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems); |
4286 | pItemPtr->NextFreeIndex = block.FirstFreeIndex; |
4287 | block.FirstFreeIndex = index; |
4288 | return; |
4289 | } |
4290 | } |
4291 | VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool." ); |
4292 | } |
4293 | |
4294 | template<typename T> |
4295 | typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock() |
4296 | { |
4297 | ItemBlock newBlock = { |
4298 | vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 }; |
4299 | |
4300 | m_ItemBlocks.push_back(newBlock); |
4301 | |
4302 | // Setup singly-linked list of all free items in this block. |
4303 | for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i) |
4304 | newBlock.pItems[i].NextFreeIndex = i + 1; |
4305 | newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX; |
4306 | return m_ItemBlocks.back(); |
4307 | } |
4308 | |
4309 | //////////////////////////////////////////////////////////////////////////////// |
4310 | // class VmaRawList, VmaList |
4311 | |
4312 | #if VMA_USE_STL_LIST |
4313 | |
4314 | #define VmaList std::list |
4315 | |
4316 | #else // #if VMA_USE_STL_LIST |
4317 | |
4318 | template<typename T> |
4319 | struct VmaListItem |
4320 | { |
4321 | VmaListItem* pPrev; |
4322 | VmaListItem* pNext; |
4323 | T Value; |
4324 | }; |
4325 | |
4326 | // Doubly linked list. |
4327 | template<typename T> |
4328 | class VmaRawList |
4329 | { |
4330 | VMA_CLASS_NO_COPY(VmaRawList) |
4331 | public: |
4332 | typedef VmaListItem<T> ItemType; |
4333 | |
4334 | VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks); |
4335 | ~VmaRawList(); |
4336 | void Clear(); |
4337 | |
4338 | size_t GetCount() const { return m_Count; } |
4339 | bool IsEmpty() const { return m_Count == 0; } |
4340 | |
4341 | ItemType* Front() { return m_pFront; } |
4342 | const ItemType* Front() const { return m_pFront; } |
4343 | ItemType* Back() { return m_pBack; } |
4344 | const ItemType* Back() const { return m_pBack; } |
4345 | |
4346 | ItemType* PushBack(); |
4347 | ItemType* PushFront(); |
4348 | ItemType* PushBack(const T& value); |
4349 | ItemType* PushFront(const T& value); |
4350 | void PopBack(); |
4351 | void PopFront(); |
4352 | |
4353 | // Item can be null - it means PushBack. |
4354 | ItemType* InsertBefore(ItemType* pItem); |
4355 | // Item can be null - it means PushFront. |
4356 | ItemType* InsertAfter(ItemType* pItem); |
4357 | |
4358 | ItemType* InsertBefore(ItemType* pItem, const T& value); |
4359 | ItemType* InsertAfter(ItemType* pItem, const T& value); |
4360 | |
4361 | void Remove(ItemType* pItem); |
4362 | |
4363 | private: |
4364 | const VkAllocationCallbacks* const m_pAllocationCallbacks; |
4365 | VmaPoolAllocator<ItemType> m_ItemAllocator; |
4366 | ItemType* m_pFront; |
4367 | ItemType* m_pBack; |
4368 | size_t m_Count; |
4369 | }; |
4370 | |
4371 | template<typename T> |
4372 | VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) : |
4373 | m_pAllocationCallbacks(pAllocationCallbacks), |
4374 | m_ItemAllocator(pAllocationCallbacks, 128), |
4375 | m_pFront(VMA_NULL), |
4376 | m_pBack(VMA_NULL), |
4377 | m_Count(0) |
4378 | { |
4379 | } |
4380 | |
4381 | template<typename T> |
4382 | VmaRawList<T>::~VmaRawList() |
4383 | { |
4384 | // Intentionally not calling Clear, because that would be unnecessary |
4385 | // computations to return all items to m_ItemAllocator as free. |
4386 | } |
4387 | |
4388 | template<typename T> |
4389 | void VmaRawList<T>::Clear() |
4390 | { |
4391 | if(IsEmpty() == false) |
4392 | { |
4393 | ItemType* pItem = m_pBack; |
4394 | while(pItem != VMA_NULL) |
4395 | { |
4396 | ItemType* const pPrevItem = pItem->pPrev; |
4397 | m_ItemAllocator.Free(pItem); |
4398 | pItem = pPrevItem; |
4399 | } |
4400 | m_pFront = VMA_NULL; |
4401 | m_pBack = VMA_NULL; |
4402 | m_Count = 0; |
4403 | } |
4404 | } |
4405 | |
4406 | template<typename T> |
4407 | VmaListItem<T>* VmaRawList<T>::PushBack() |
4408 | { |
4409 | ItemType* const pNewItem = m_ItemAllocator.Alloc(); |
4410 | pNewItem->pNext = VMA_NULL; |
4411 | if(IsEmpty()) |
4412 | { |
4413 | pNewItem->pPrev = VMA_NULL; |
4414 | m_pFront = pNewItem; |
4415 | m_pBack = pNewItem; |
4416 | m_Count = 1; |
4417 | } |
4418 | else |
4419 | { |
4420 | pNewItem->pPrev = m_pBack; |
4421 | m_pBack->pNext = pNewItem; |
4422 | m_pBack = pNewItem; |
4423 | ++m_Count; |
4424 | } |
4425 | return pNewItem; |
4426 | } |
4427 | |
4428 | template<typename T> |
4429 | VmaListItem<T>* VmaRawList<T>::PushFront() |
4430 | { |
4431 | ItemType* const pNewItem = m_ItemAllocator.Alloc(); |
4432 | pNewItem->pPrev = VMA_NULL; |
4433 | if(IsEmpty()) |
4434 | { |
4435 | pNewItem->pNext = VMA_NULL; |
4436 | m_pFront = pNewItem; |
4437 | m_pBack = pNewItem; |
4438 | m_Count = 1; |
4439 | } |
4440 | else |
4441 | { |
4442 | pNewItem->pNext = m_pFront; |
4443 | m_pFront->pPrev = pNewItem; |
4444 | m_pFront = pNewItem; |
4445 | ++m_Count; |
4446 | } |
4447 | return pNewItem; |
4448 | } |
4449 | |
4450 | template<typename T> |
4451 | VmaListItem<T>* VmaRawList<T>::PushBack(const T& value) |
4452 | { |
4453 | ItemType* const pNewItem = PushBack(); |
4454 | pNewItem->Value = value; |
4455 | return pNewItem; |
4456 | } |
4457 | |
4458 | template<typename T> |
4459 | VmaListItem<T>* VmaRawList<T>::PushFront(const T& value) |
4460 | { |
4461 | ItemType* const pNewItem = PushFront(); |
4462 | pNewItem->Value = value; |
4463 | return pNewItem; |
4464 | } |
4465 | |
4466 | template<typename T> |
4467 | void VmaRawList<T>::PopBack() |
4468 | { |
4469 | VMA_HEAVY_ASSERT(m_Count > 0); |
4470 | ItemType* const pBackItem = m_pBack; |
4471 | ItemType* const pPrevItem = pBackItem->pPrev; |
4472 | if(pPrevItem != VMA_NULL) |
4473 | { |
4474 | pPrevItem->pNext = VMA_NULL; |
4475 | } |
4476 | m_pBack = pPrevItem; |
4477 | m_ItemAllocator.Free(pBackItem); |
4478 | --m_Count; |
4479 | } |
4480 | |
4481 | template<typename T> |
4482 | void VmaRawList<T>::PopFront() |
4483 | { |
4484 | VMA_HEAVY_ASSERT(m_Count > 0); |
4485 | ItemType* const pFrontItem = m_pFront; |
4486 | ItemType* const pNextItem = pFrontItem->pNext; |
4487 | if(pNextItem != VMA_NULL) |
4488 | { |
4489 | pNextItem->pPrev = VMA_NULL; |
4490 | } |
4491 | m_pFront = pNextItem; |
4492 | m_ItemAllocator.Free(pFrontItem); |
4493 | --m_Count; |
4494 | } |
4495 | |
4496 | template<typename T> |
4497 | void VmaRawList<T>::Remove(ItemType* pItem) |
4498 | { |
4499 | VMA_HEAVY_ASSERT(pItem != VMA_NULL); |
4500 | VMA_HEAVY_ASSERT(m_Count > 0); |
4501 | |
4502 | if(pItem->pPrev != VMA_NULL) |
4503 | { |
4504 | pItem->pPrev->pNext = pItem->pNext; |
4505 | } |
4506 | else |
4507 | { |
4508 | VMA_HEAVY_ASSERT(m_pFront == pItem); |
4509 | m_pFront = pItem->pNext; |
4510 | } |
4511 | |
4512 | if(pItem->pNext != VMA_NULL) |
4513 | { |
4514 | pItem->pNext->pPrev = pItem->pPrev; |
4515 | } |
4516 | else |
4517 | { |
4518 | VMA_HEAVY_ASSERT(m_pBack == pItem); |
4519 | m_pBack = pItem->pPrev; |
4520 | } |
4521 | |
4522 | m_ItemAllocator.Free(pItem); |
4523 | --m_Count; |
4524 | } |
4525 | |
4526 | template<typename T> |
4527 | VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem) |
4528 | { |
4529 | if(pItem != VMA_NULL) |
4530 | { |
4531 | ItemType* const prevItem = pItem->pPrev; |
4532 | ItemType* const newItem = m_ItemAllocator.Alloc(); |
4533 | newItem->pPrev = prevItem; |
4534 | newItem->pNext = pItem; |
4535 | pItem->pPrev = newItem; |
4536 | if(prevItem != VMA_NULL) |
4537 | { |
4538 | prevItem->pNext = newItem; |
4539 | } |
4540 | else |
4541 | { |
4542 | VMA_HEAVY_ASSERT(m_pFront == pItem); |
4543 | m_pFront = newItem; |
4544 | } |
4545 | ++m_Count; |
4546 | return newItem; |
4547 | } |
4548 | else |
4549 | return PushBack(); |
4550 | } |
4551 | |
4552 | template<typename T> |
4553 | VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem) |
4554 | { |
4555 | if(pItem != VMA_NULL) |
4556 | { |
4557 | ItemType* const nextItem = pItem->pNext; |
4558 | ItemType* const newItem = m_ItemAllocator.Alloc(); |
4559 | newItem->pNext = nextItem; |
4560 | newItem->pPrev = pItem; |
4561 | pItem->pNext = newItem; |
4562 | if(nextItem != VMA_NULL) |
4563 | { |
4564 | nextItem->pPrev = newItem; |
4565 | } |
4566 | else |
4567 | { |
4568 | VMA_HEAVY_ASSERT(m_pBack == pItem); |
4569 | m_pBack = newItem; |
4570 | } |
4571 | ++m_Count; |
4572 | return newItem; |
4573 | } |
4574 | else |
4575 | return PushFront(); |
4576 | } |
4577 | |
4578 | template<typename T> |
4579 | VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value) |
4580 | { |
4581 | ItemType* const newItem = InsertBefore(pItem); |
4582 | newItem->Value = value; |
4583 | return newItem; |
4584 | } |
4585 | |
4586 | template<typename T> |
4587 | VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value) |
4588 | { |
4589 | ItemType* const newItem = InsertAfter(pItem); |
4590 | newItem->Value = value; |
4591 | return newItem; |
4592 | } |
4593 | |
4594 | template<typename T, typename AllocatorT> |
4595 | class VmaList |
4596 | { |
4597 | VMA_CLASS_NO_COPY(VmaList) |
4598 | public: |
4599 | class iterator |
4600 | { |
4601 | public: |
4602 | iterator() : |
4603 | m_pList(VMA_NULL), |
4604 | m_pItem(VMA_NULL) |
4605 | { |
4606 | } |
4607 | |
4608 | T& operator*() const |
4609 | { |
4610 | VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); |
4611 | return m_pItem->Value; |
4612 | } |
4613 | T* operator->() const |
4614 | { |
4615 | VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); |
4616 | return &m_pItem->Value; |
4617 | } |
4618 | |
4619 | iterator& operator++() |
4620 | { |
4621 | VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); |
4622 | m_pItem = m_pItem->pNext; |
4623 | return *this; |
4624 | } |
4625 | iterator& operator--() |
4626 | { |
4627 | if(m_pItem != VMA_NULL) |
4628 | { |
4629 | m_pItem = m_pItem->pPrev; |
4630 | } |
4631 | else |
4632 | { |
4633 | VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); |
4634 | m_pItem = m_pList->Back(); |
4635 | } |
4636 | return *this; |
4637 | } |
4638 | |
4639 | iterator operator++(int) |
4640 | { |
4641 | iterator result = *this; |
4642 | ++*this; |
4643 | return result; |
4644 | } |
4645 | iterator operator--(int) |
4646 | { |
4647 | iterator result = *this; |
4648 | --*this; |
4649 | return result; |
4650 | } |
4651 | |
4652 | bool operator==(const iterator& rhs) const |
4653 | { |
4654 | VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); |
4655 | return m_pItem == rhs.m_pItem; |
4656 | } |
4657 | bool operator!=(const iterator& rhs) const |
4658 | { |
4659 | VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); |
4660 | return m_pItem != rhs.m_pItem; |
4661 | } |
4662 | |
4663 | private: |
4664 | VmaRawList<T>* m_pList; |
4665 | VmaListItem<T>* m_pItem; |
4666 | |
4667 | iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : |
4668 | m_pList(pList), |
4669 | m_pItem(pItem) |
4670 | { |
4671 | } |
4672 | |
4673 | friend class VmaList<T, AllocatorT>; |
4674 | }; |
4675 | |
4676 | class const_iterator |
4677 | { |
4678 | public: |
4679 | const_iterator() : |
4680 | m_pList(VMA_NULL), |
4681 | m_pItem(VMA_NULL) |
4682 | { |
4683 | } |
4684 | |
4685 | const_iterator(const iterator& src) : |
4686 | m_pList(src.m_pList), |
4687 | m_pItem(src.m_pItem) |
4688 | { |
4689 | } |
4690 | |
4691 | const T& operator*() const |
4692 | { |
4693 | VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); |
4694 | return m_pItem->Value; |
4695 | } |
4696 | const T* operator->() const |
4697 | { |
4698 | VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); |
4699 | return &m_pItem->Value; |
4700 | } |
4701 | |
4702 | const_iterator& operator++() |
4703 | { |
4704 | VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); |
4705 | m_pItem = m_pItem->pNext; |
4706 | return *this; |
4707 | } |
4708 | const_iterator& operator--() |
4709 | { |
4710 | if(m_pItem != VMA_NULL) |
4711 | { |
4712 | m_pItem = m_pItem->pPrev; |
4713 | } |
4714 | else |
4715 | { |
4716 | VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); |
4717 | m_pItem = m_pList->Back(); |
4718 | } |
4719 | return *this; |
4720 | } |
4721 | |
4722 | const_iterator operator++(int) |
4723 | { |
4724 | const_iterator result = *this; |
4725 | ++*this; |
4726 | return result; |
4727 | } |
4728 | const_iterator operator--(int) |
4729 | { |
4730 | const_iterator result = *this; |
4731 | --*this; |
4732 | return result; |
4733 | } |
4734 | |
4735 | bool operator==(const const_iterator& rhs) const |
4736 | { |
4737 | VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); |
4738 | return m_pItem == rhs.m_pItem; |
4739 | } |
4740 | bool operator!=(const const_iterator& rhs) const |
4741 | { |
4742 | VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); |
4743 | return m_pItem != rhs.m_pItem; |
4744 | } |
4745 | |
4746 | private: |
4747 | const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : |
4748 | m_pList(pList), |
4749 | m_pItem(pItem) |
4750 | { |
4751 | } |
4752 | |
4753 | const VmaRawList<T>* m_pList; |
4754 | const VmaListItem<T>* m_pItem; |
4755 | |
4756 | friend class VmaList<T, AllocatorT>; |
4757 | }; |
4758 | |
4759 | VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { } |
4760 | |
4761 | bool empty() const { return m_RawList.IsEmpty(); } |
4762 | size_t size() const { return m_RawList.GetCount(); } |
4763 | |
4764 | iterator begin() { return iterator(&m_RawList, m_RawList.Front()); } |
4765 | iterator end() { return iterator(&m_RawList, VMA_NULL); } |
4766 | |
4767 | const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); } |
4768 | const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); } |
4769 | |
4770 | void clear() { m_RawList.Clear(); } |
4771 | void push_back(const T& value) { m_RawList.PushBack(value); } |
4772 | void erase(iterator it) { m_RawList.Remove(it.m_pItem); } |
4773 | iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); } |
4774 | |
4775 | private: |
4776 | VmaRawList<T> m_RawList; |
4777 | }; |
4778 | |
4779 | #endif // #if VMA_USE_STL_LIST |
4780 | |
4781 | //////////////////////////////////////////////////////////////////////////////// |
4782 | // class VmaMap |
4783 | |
4784 | // Unused in this version. |
4785 | #if 0 |
4786 | |
4787 | #if VMA_USE_STL_UNORDERED_MAP |
4788 | |
4789 | #define VmaPair std::pair |
4790 | |
4791 | #define VMA_MAP_TYPE(KeyT, ValueT) \ |
4792 | std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > > |
4793 | |
4794 | #else // #if VMA_USE_STL_UNORDERED_MAP |
4795 | |
4796 | template<typename T1, typename T2> |
4797 | struct VmaPair |
4798 | { |
4799 | T1 first; |
4800 | T2 second; |
4801 | |
4802 | VmaPair() : first(), second() { } |
4803 | VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { } |
4804 | }; |
4805 | |
4806 | /* Class compatible with subset of interface of std::unordered_map. |
4807 | KeyT, ValueT must be POD because they will be stored in VmaVector. |
4808 | */ |
4809 | template<typename KeyT, typename ValueT> |
4810 | class VmaMap |
4811 | { |
4812 | public: |
4813 | typedef VmaPair<KeyT, ValueT> PairType; |
4814 | typedef PairType* iterator; |
4815 | |
4816 | VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { } |
4817 | |
4818 | iterator begin() { return m_Vector.begin(); } |
4819 | iterator end() { return m_Vector.end(); } |
4820 | |
4821 | void insert(const PairType& pair); |
4822 | iterator find(const KeyT& key); |
4823 | void erase(iterator it); |
4824 | |
4825 | private: |
4826 | VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector; |
4827 | }; |
4828 | |
4829 | #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT> |
4830 | |
4831 | template<typename FirstT, typename SecondT> |
4832 | struct VmaPairFirstLess |
4833 | { |
4834 | bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const |
4835 | { |
4836 | return lhs.first < rhs.first; |
4837 | } |
4838 | bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const |
4839 | { |
4840 | return lhs.first < rhsFirst; |
4841 | } |
4842 | }; |
4843 | |
4844 | template<typename KeyT, typename ValueT> |
4845 | void VmaMap<KeyT, ValueT>::insert(const PairType& pair) |
4846 | { |
4847 | const size_t indexToInsert = VmaBinaryFindFirstNotLess( |
4848 | m_Vector.data(), |
4849 | m_Vector.data() + m_Vector.size(), |
4850 | pair, |
4851 | VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data(); |
4852 | VmaVectorInsert(m_Vector, indexToInsert, pair); |
4853 | } |
4854 | |
4855 | template<typename KeyT, typename ValueT> |
4856 | VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key) |
4857 | { |
4858 | PairType* it = VmaBinaryFindFirstNotLess( |
4859 | m_Vector.data(), |
4860 | m_Vector.data() + m_Vector.size(), |
4861 | key, |
4862 | VmaPairFirstLess<KeyT, ValueT>()); |
4863 | if((it != m_Vector.end()) && (it->first == key)) |
4864 | { |
4865 | return it; |
4866 | } |
4867 | else |
4868 | { |
4869 | return m_Vector.end(); |
4870 | } |
4871 | } |
4872 | |
4873 | template<typename KeyT, typename ValueT> |
4874 | void VmaMap<KeyT, ValueT>::erase(iterator it) |
4875 | { |
4876 | VmaVectorRemove(m_Vector, it - m_Vector.begin()); |
4877 | } |
4878 | |
4879 | #endif // #if VMA_USE_STL_UNORDERED_MAP |
4880 | |
4881 | #endif // #if 0 |
4882 | |
4883 | //////////////////////////////////////////////////////////////////////////////// |
4884 | |
4885 | class VmaDeviceMemoryBlock; |
4886 | |
4887 | enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE }; |
4888 | |
4889 | struct VmaAllocation_T |
4890 | { |
4891 | VMA_CLASS_NO_COPY(VmaAllocation_T) |
4892 | private: |
4893 | static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80; |
4894 | |
4895 | enum FLAGS |
4896 | { |
4897 | FLAG_USER_DATA_STRING = 0x01, |
4898 | }; |
4899 | |
4900 | public: |
4901 | enum ALLOCATION_TYPE |
4902 | { |
4903 | ALLOCATION_TYPE_NONE, |
4904 | ALLOCATION_TYPE_BLOCK, |
4905 | ALLOCATION_TYPE_DEDICATED, |
4906 | }; |
4907 | |
4908 | VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) : |
4909 | m_Alignment(1), |
4910 | m_Size(0), |
4911 | m_pUserData(VMA_NULL), |
4912 | m_LastUseFrameIndex(currentFrameIndex), |
4913 | m_Type((uint8_t)ALLOCATION_TYPE_NONE), |
4914 | m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN), |
4915 | m_MapCount(0), |
4916 | m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0) |
4917 | { |
4918 | #if VMA_STATS_STRING_ENABLED |
4919 | m_CreationFrameIndex = currentFrameIndex; |
4920 | m_BufferImageUsage = 0; |
4921 | #endif |
4922 | } |
4923 | |
4924 | ~VmaAllocation_T() |
4925 | { |
4926 | VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction." ); |
4927 | |
4928 | // Check if owned string was freed. |
4929 | VMA_ASSERT(m_pUserData == VMA_NULL); |
4930 | } |
4931 | |
4932 | void InitBlockAllocation( |
4933 | VmaPool hPool, |
4934 | VmaDeviceMemoryBlock* block, |
4935 | VkDeviceSize offset, |
4936 | VkDeviceSize alignment, |
4937 | VkDeviceSize size, |
4938 | VmaSuballocationType suballocationType, |
4939 | bool mapped, |
4940 | bool canBecomeLost) |
4941 | { |
4942 | VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); |
4943 | VMA_ASSERT(block != VMA_NULL); |
4944 | m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; |
4945 | m_Alignment = alignment; |
4946 | m_Size = size; |
4947 | m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; |
4948 | m_SuballocationType = (uint8_t)suballocationType; |
4949 | m_BlockAllocation.m_hPool = hPool; |
4950 | m_BlockAllocation.m_Block = block; |
4951 | m_BlockAllocation.m_Offset = offset; |
4952 | m_BlockAllocation.m_CanBecomeLost = canBecomeLost; |
4953 | } |
4954 | |
4955 | void InitLost() |
4956 | { |
4957 | VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); |
4958 | VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST); |
4959 | m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; |
4960 | m_BlockAllocation.m_hPool = VK_NULL_HANDLE; |
4961 | m_BlockAllocation.m_Block = VMA_NULL; |
4962 | m_BlockAllocation.m_Offset = 0; |
4963 | m_BlockAllocation.m_CanBecomeLost = true; |
4964 | } |
4965 | |
4966 | void ChangeBlockAllocation( |
4967 | VmaAllocator hAllocator, |
4968 | VmaDeviceMemoryBlock* block, |
4969 | VkDeviceSize offset); |
4970 | |
4971 | void ChangeSize(VkDeviceSize newSize); |
4972 | void ChangeOffset(VkDeviceSize newOffset); |
4973 | |
4974 | // pMappedData not null means allocation is created with MAPPED flag. |
4975 | void InitDedicatedAllocation( |
4976 | uint32_t memoryTypeIndex, |
4977 | VkDeviceMemory hMemory, |
4978 | VmaSuballocationType suballocationType, |
4979 | void* pMappedData, |
4980 | VkDeviceSize size) |
4981 | { |
4982 | VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); |
4983 | VMA_ASSERT(hMemory != VK_NULL_HANDLE); |
4984 | m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED; |
4985 | m_Alignment = 0; |
4986 | m_Size = size; |
4987 | m_SuballocationType = (uint8_t)suballocationType; |
4988 | m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; |
4989 | m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex; |
4990 | m_DedicatedAllocation.m_hMemory = hMemory; |
4991 | m_DedicatedAllocation.m_pMappedData = pMappedData; |
4992 | } |
4993 | |
4994 | ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } |
4995 | VkDeviceSize GetAlignment() const { return m_Alignment; } |
4996 | VkDeviceSize GetSize() const { return m_Size; } |
4997 | bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; } |
4998 | void* GetUserData() const { return m_pUserData; } |
4999 | void SetUserData(VmaAllocator hAllocator, void* pUserData); |
5000 | VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; } |
5001 | |
5002 | VmaDeviceMemoryBlock* GetBlock() const |
5003 | { |
5004 | VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); |
5005 | return m_BlockAllocation.m_Block; |
5006 | } |
5007 | VkDeviceSize GetOffset() const; |
5008 | VkDeviceMemory GetMemory() const; |
5009 | uint32_t GetMemoryTypeIndex() const; |
5010 | bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; } |
5011 | void* GetMappedData() const; |
5012 | bool CanBecomeLost() const; |
5013 | VmaPool GetPool() const; |
5014 | |
5015 | uint32_t GetLastUseFrameIndex() const |
5016 | { |
5017 | return m_LastUseFrameIndex.load(); |
5018 | } |
5019 | bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired) |
5020 | { |
5021 | return m_LastUseFrameIndex.compare_exchange_weak(i1&: expected, i2: desired); |
5022 | } |
5023 | /* |
5024 | - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex, |
5025 | makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true. |
5026 | - Else, returns false. |
5027 | |
5028 | If hAllocation is already lost, assert - you should not call it then. |
5029 | If hAllocation was not created with CAN_BECOME_LOST_BIT, assert. |
5030 | */ |
5031 | bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); |
5032 | |
5033 | void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo) |
5034 | { |
5035 | VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED); |
5036 | outInfo.blockCount = 1; |
5037 | outInfo.allocationCount = 1; |
5038 | outInfo.unusedRangeCount = 0; |
5039 | outInfo.usedBytes = m_Size; |
5040 | outInfo.unusedBytes = 0; |
5041 | outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size; |
5042 | outInfo.unusedRangeSizeMin = UINT64_MAX; |
5043 | outInfo.unusedRangeSizeMax = 0; |
5044 | } |
5045 | |
5046 | void BlockAllocMap(); |
5047 | void BlockAllocUnmap(); |
5048 | VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData); |
5049 | void DedicatedAllocUnmap(VmaAllocator hAllocator); |
5050 | |
5051 | #if VMA_STATS_STRING_ENABLED |
5052 | uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; } |
5053 | uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; } |
5054 | |
5055 | void InitBufferImageUsage(uint32_t bufferImageUsage) |
5056 | { |
5057 | VMA_ASSERT(m_BufferImageUsage == 0); |
5058 | m_BufferImageUsage = bufferImageUsage; |
5059 | } |
5060 | |
5061 | void PrintParameters(class VmaJsonWriter& json) const; |
5062 | #endif |
5063 | |
5064 | private: |
5065 | VkDeviceSize m_Alignment; |
5066 | VkDeviceSize m_Size; |
5067 | void* m_pUserData; |
5068 | VMA_ATOMIC_UINT32 m_LastUseFrameIndex; |
5069 | uint8_t m_Type; // ALLOCATION_TYPE |
5070 | uint8_t m_SuballocationType; // VmaSuballocationType |
5071 | // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT. |
5072 | // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory(). |
5073 | uint8_t m_MapCount; |
5074 | uint8_t m_Flags; // enum FLAGS |
5075 | |
5076 | // Allocation out of VmaDeviceMemoryBlock. |
5077 | struct BlockAllocation |
5078 | { |
5079 | VmaPool m_hPool; // Null if belongs to general memory. |
5080 | VmaDeviceMemoryBlock* m_Block; |
5081 | VkDeviceSize m_Offset; |
5082 | bool m_CanBecomeLost; |
5083 | }; |
5084 | |
5085 | // Allocation for an object that has its own private VkDeviceMemory. |
5086 | struct DedicatedAllocation |
5087 | { |
5088 | uint32_t m_MemoryTypeIndex; |
5089 | VkDeviceMemory m_hMemory; |
5090 | void* m_pMappedData; // Not null means memory is mapped. |
5091 | }; |
5092 | |
5093 | union |
5094 | { |
5095 | // Allocation out of VmaDeviceMemoryBlock. |
5096 | BlockAllocation m_BlockAllocation; |
5097 | // Allocation for an object that has its own private VkDeviceMemory. |
5098 | DedicatedAllocation m_DedicatedAllocation; |
5099 | }; |
5100 | |
5101 | #if VMA_STATS_STRING_ENABLED |
5102 | uint32_t m_CreationFrameIndex; |
5103 | uint32_t m_BufferImageUsage; // 0 if unknown. |
5104 | #endif |
5105 | |
5106 | void FreeUserDataString(VmaAllocator hAllocator); |
5107 | }; |
5108 | |
5109 | /* |
5110 | Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as |
5111 | allocated memory block or free. |
5112 | */ |
5113 | struct VmaSuballocation |
5114 | { |
5115 | VkDeviceSize offset; |
5116 | VkDeviceSize size; |
5117 | VmaAllocation hAllocation; |
5118 | VmaSuballocationType type; |
5119 | }; |
5120 | |
5121 | // Comparator for offsets. |
5122 | struct VmaSuballocationOffsetLess |
5123 | { |
5124 | bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const |
5125 | { |
5126 | return lhs.offset < rhs.offset; |
5127 | } |
5128 | }; |
5129 | struct VmaSuballocationOffsetGreater |
5130 | { |
5131 | bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const |
5132 | { |
5133 | return lhs.offset > rhs.offset; |
5134 | } |
5135 | }; |
5136 | |
5137 | typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList; |
5138 | |
5139 | // Cost of one additional allocation lost, as equivalent in bytes. |
5140 | static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576; |
5141 | |
5142 | /* |
5143 | Parameters of planned allocation inside a VmaDeviceMemoryBlock. |
5144 | |
5145 | If canMakeOtherLost was false: |
5146 | - item points to a FREE suballocation. |
5147 | - itemsToMakeLostCount is 0. |
5148 | |
5149 | If canMakeOtherLost was true: |
5150 | - item points to first of sequence of suballocations, which are either FREE, |
5151 | or point to VmaAllocations that can become lost. |
5152 | - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for |
5153 | the requested allocation to succeed. |
5154 | */ |
5155 | struct VmaAllocationRequest |
5156 | { |
5157 | VkDeviceSize offset; |
5158 | VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation. |
5159 | VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation. |
5160 | VmaSuballocationList::iterator item; |
5161 | size_t itemsToMakeLostCount; |
5162 | void* customData; |
5163 | |
5164 | VkDeviceSize CalcCost() const |
5165 | { |
5166 | return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST; |
5167 | } |
5168 | }; |
5169 | |
5170 | /* |
5171 | Data structure used for bookkeeping of allocations and unused ranges of memory |
5172 | in a single VkDeviceMemory block. |
5173 | */ |
5174 | class VmaBlockMetadata |
5175 | { |
5176 | public: |
5177 | VmaBlockMetadata(VmaAllocator hAllocator); |
5178 | virtual ~VmaBlockMetadata() { } |
5179 | virtual void Init(VkDeviceSize size) { m_Size = size; } |
5180 | |
5181 | // Validates all data structures inside this object. If not valid, returns false. |
5182 | virtual bool Validate() const = 0; |
5183 | VkDeviceSize GetSize() const { return m_Size; } |
5184 | virtual size_t GetAllocationCount() const = 0; |
5185 | virtual VkDeviceSize GetSumFreeSize() const = 0; |
5186 | virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0; |
5187 | // Returns true if this block is empty - contains only single free suballocation. |
5188 | virtual bool IsEmpty() const = 0; |
5189 | |
5190 | virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0; |
5191 | // Shouldn't modify blockCount. |
5192 | virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0; |
5193 | |
5194 | #if VMA_STATS_STRING_ENABLED |
5195 | virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0; |
5196 | #endif |
5197 | |
5198 | // Tries to find a place for suballocation with given parameters inside this block. |
5199 | // If succeeded, fills pAllocationRequest and returns true. |
5200 | // If failed, returns false. |
5201 | virtual bool CreateAllocationRequest( |
5202 | uint32_t currentFrameIndex, |
5203 | uint32_t frameInUseCount, |
5204 | VkDeviceSize bufferImageGranularity, |
5205 | VkDeviceSize allocSize, |
5206 | VkDeviceSize allocAlignment, |
5207 | bool upperAddress, |
5208 | VmaSuballocationType allocType, |
5209 | bool canMakeOtherLost, |
5210 | // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags. |
5211 | uint32_t strategy, |
5212 | VmaAllocationRequest* pAllocationRequest) = 0; |
5213 | |
5214 | virtual bool MakeRequestedAllocationsLost( |
5215 | uint32_t currentFrameIndex, |
5216 | uint32_t frameInUseCount, |
5217 | VmaAllocationRequest* pAllocationRequest) = 0; |
5218 | |
5219 | virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0; |
5220 | |
5221 | virtual VkResult CheckCorruption(const void* pBlockData) = 0; |
5222 | |
5223 | // Makes actual allocation based on request. Request must already be checked and valid. |
5224 | virtual void Alloc( |
5225 | const VmaAllocationRequest& request, |
5226 | VmaSuballocationType type, |
5227 | VkDeviceSize allocSize, |
5228 | bool upperAddress, |
5229 | VmaAllocation hAllocation) = 0; |
5230 | |
5231 | // Frees suballocation assigned to given memory region. |
5232 | virtual void Free(const VmaAllocation allocation) = 0; |
5233 | virtual void FreeAtOffset(VkDeviceSize offset) = 0; |
5234 | |
5235 | // Tries to resize (grow or shrink) space for given allocation, in place. |
5236 | virtual bool ResizeAllocation(const VmaAllocation /*alloc*/, VkDeviceSize /*newSize*/) { return false; } |
5237 | |
5238 | protected: |
5239 | const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; } |
5240 | |
5241 | #if VMA_STATS_STRING_ENABLED |
5242 | void PrintDetailedMap_Begin(class VmaJsonWriter& json, |
5243 | VkDeviceSize unusedBytes, |
5244 | size_t allocationCount, |
5245 | size_t unusedRangeCount) const; |
5246 | void PrintDetailedMap_Allocation(class VmaJsonWriter& json, |
5247 | VkDeviceSize offset, |
5248 | VmaAllocation hAllocation) const; |
5249 | void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, |
5250 | VkDeviceSize offset, |
5251 | VkDeviceSize size) const; |
5252 | void PrintDetailedMap_End(class VmaJsonWriter& json) const; |
5253 | #endif |
5254 | |
5255 | private: |
5256 | VkDeviceSize m_Size; |
5257 | const VkAllocationCallbacks* m_pAllocationCallbacks; |
5258 | }; |
5259 | |
5260 | #define VMA_VALIDATE(cond) do { if(!(cond)) { \ |
5261 | VMA_ASSERT(0 && "Validation failed: " #cond); \ |
5262 | return false; \ |
5263 | } } while(false) |
5264 | |
5265 | class VmaBlockMetadata_Generic : public VmaBlockMetadata |
5266 | { |
5267 | VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic) |
5268 | public: |
5269 | VmaBlockMetadata_Generic(VmaAllocator hAllocator); |
5270 | virtual ~VmaBlockMetadata_Generic(); |
5271 | virtual void Init(VkDeviceSize size); |
5272 | |
5273 | virtual bool Validate() const; |
5274 | virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; } |
5275 | virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; } |
5276 | virtual VkDeviceSize GetUnusedRangeSizeMax() const; |
5277 | virtual bool IsEmpty() const; |
5278 | |
5279 | virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const; |
5280 | virtual void AddPoolStats(VmaPoolStats& inoutStats) const; |
5281 | |
5282 | #if VMA_STATS_STRING_ENABLED |
5283 | virtual void PrintDetailedMap(class VmaJsonWriter& json) const; |
5284 | #endif |
5285 | |
5286 | virtual bool CreateAllocationRequest( |
5287 | uint32_t currentFrameIndex, |
5288 | uint32_t frameInUseCount, |
5289 | VkDeviceSize bufferImageGranularity, |
5290 | VkDeviceSize allocSize, |
5291 | VkDeviceSize allocAlignment, |
5292 | bool upperAddress, |
5293 | VmaSuballocationType allocType, |
5294 | bool canMakeOtherLost, |
5295 | uint32_t strategy, |
5296 | VmaAllocationRequest* pAllocationRequest); |
5297 | |
5298 | virtual bool MakeRequestedAllocationsLost( |
5299 | uint32_t currentFrameIndex, |
5300 | uint32_t frameInUseCount, |
5301 | VmaAllocationRequest* pAllocationRequest); |
5302 | |
5303 | virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); |
5304 | |
5305 | virtual VkResult CheckCorruption(const void* pBlockData); |
5306 | |
5307 | virtual void Alloc( |
5308 | const VmaAllocationRequest& request, |
5309 | VmaSuballocationType type, |
5310 | VkDeviceSize allocSize, |
5311 | bool upperAddress, |
5312 | VmaAllocation hAllocation); |
5313 | |
5314 | virtual void Free(const VmaAllocation allocation); |
5315 | virtual void FreeAtOffset(VkDeviceSize offset); |
5316 | |
5317 | virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize); |
5318 | |
5319 | //////////////////////////////////////////////////////////////////////////////// |
5320 | // For defragmentation |
5321 | |
5322 | bool IsBufferImageGranularityConflictPossible( |
5323 | VkDeviceSize bufferImageGranularity, |
5324 | VmaSuballocationType& inOutPrevSuballocType) const; |
5325 | |
5326 | private: |
5327 | friend class VmaDefragmentationAlgorithm_Generic; |
5328 | friend class VmaDefragmentationAlgorithm_Fast; |
5329 | |
5330 | uint32_t m_FreeCount; |
5331 | VkDeviceSize m_SumFreeSize; |
5332 | VmaSuballocationList m_Suballocations; |
5333 | // Suballocations that are free and have size greater than certain threshold. |
5334 | // Sorted by size, ascending. |
5335 | VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize; |
5336 | |
5337 | bool ValidateFreeSuballocationList() const; |
5338 | |
5339 | // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem. |
5340 | // If yes, fills pOffset and returns true. If no, returns false. |
5341 | bool CheckAllocation( |
5342 | uint32_t currentFrameIndex, |
5343 | uint32_t frameInUseCount, |
5344 | VkDeviceSize bufferImageGranularity, |
5345 | VkDeviceSize allocSize, |
5346 | VkDeviceSize allocAlignment, |
5347 | VmaSuballocationType allocType, |
5348 | VmaSuballocationList::const_iterator suballocItem, |
5349 | bool canMakeOtherLost, |
5350 | VkDeviceSize* pOffset, |
5351 | size_t* itemsToMakeLostCount, |
5352 | VkDeviceSize* pSumFreeSize, |
5353 | VkDeviceSize* pSumItemSize) const; |
5354 | // Given free suballocation, it merges it with following one, which must also be free. |
5355 | void MergeFreeWithNext(VmaSuballocationList::iterator item); |
5356 | // Releases given suballocation, making it free. |
5357 | // Merges it with adjacent free suballocations if applicable. |
5358 | // Returns iterator to new free suballocation at this place. |
5359 | VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem); |
5360 | // Given free suballocation, it inserts it into sorted list of |
5361 | // m_FreeSuballocationsBySize if it's suitable. |
5362 | void RegisterFreeSuballocation(VmaSuballocationList::iterator item); |
5363 | // Given free suballocation, it removes it from sorted list of |
5364 | // m_FreeSuballocationsBySize if it's suitable. |
5365 | void UnregisterFreeSuballocation(VmaSuballocationList::iterator item); |
5366 | }; |
5367 | |
5368 | /* |
5369 | Allocations and their references in internal data structure look like this: |
5370 | |
5371 | if(m_2ndVectorMode == SECOND_VECTOR_EMPTY): |
5372 | |
5373 | 0 +-------+ |
5374 | | | |
5375 | | | |
5376 | | | |
5377 | +-------+ |
5378 | | Alloc | 1st[m_1stNullItemsBeginCount] |
5379 | +-------+ |
5380 | | Alloc | 1st[m_1stNullItemsBeginCount + 1] |
5381 | +-------+ |
5382 | | ... | |
5383 | +-------+ |
5384 | | Alloc | 1st[1st.size() - 1] |
5385 | +-------+ |
5386 | | | |
5387 | | | |
5388 | | | |
5389 | GetSize() +-------+ |
5390 | |
5391 | if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER): |
5392 | |
5393 | 0 +-------+ |
5394 | | Alloc | 2nd[0] |
5395 | +-------+ |
5396 | | Alloc | 2nd[1] |
5397 | +-------+ |
5398 | | ... | |
5399 | +-------+ |
5400 | | Alloc | 2nd[2nd.size() - 1] |
5401 | +-------+ |
5402 | | | |
5403 | | | |
5404 | | | |
5405 | +-------+ |
5406 | | Alloc | 1st[m_1stNullItemsBeginCount] |
5407 | +-------+ |
5408 | | Alloc | 1st[m_1stNullItemsBeginCount + 1] |
5409 | +-------+ |
5410 | | ... | |
5411 | +-------+ |
5412 | | Alloc | 1st[1st.size() - 1] |
5413 | +-------+ |
5414 | | | |
5415 | GetSize() +-------+ |
5416 | |
5417 | if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK): |
5418 | |
5419 | 0 +-------+ |
5420 | | | |
5421 | | | |
5422 | | | |
5423 | +-------+ |
5424 | | Alloc | 1st[m_1stNullItemsBeginCount] |
5425 | +-------+ |
5426 | | Alloc | 1st[m_1stNullItemsBeginCount + 1] |
5427 | +-------+ |
5428 | | ... | |
5429 | +-------+ |
5430 | | Alloc | 1st[1st.size() - 1] |
5431 | +-------+ |
5432 | | | |
5433 | | | |
5434 | | | |
5435 | +-------+ |
5436 | | Alloc | 2nd[2nd.size() - 1] |
5437 | +-------+ |
5438 | | ... | |
5439 | +-------+ |
5440 | | Alloc | 2nd[1] |
5441 | +-------+ |
5442 | | Alloc | 2nd[0] |
5443 | GetSize() +-------+ |
5444 | |
5445 | */ |
5446 | class VmaBlockMetadata_Linear : public VmaBlockMetadata |
5447 | { |
5448 | VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear) |
5449 | public: |
5450 | VmaBlockMetadata_Linear(VmaAllocator hAllocator); |
5451 | virtual ~VmaBlockMetadata_Linear(); |
5452 | virtual void Init(VkDeviceSize size); |
5453 | |
5454 | virtual bool Validate() const; |
5455 | virtual size_t GetAllocationCount() const; |
5456 | virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; } |
5457 | virtual VkDeviceSize GetUnusedRangeSizeMax() const; |
5458 | virtual bool IsEmpty() const { return GetAllocationCount() == 0; } |
5459 | |
5460 | virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const; |
5461 | virtual void AddPoolStats(VmaPoolStats& inoutStats) const; |
5462 | |
5463 | #if VMA_STATS_STRING_ENABLED |
5464 | virtual void PrintDetailedMap(class VmaJsonWriter& json) const; |
5465 | #endif |
5466 | |
5467 | virtual bool CreateAllocationRequest( |
5468 | uint32_t currentFrameIndex, |
5469 | uint32_t frameInUseCount, |
5470 | VkDeviceSize bufferImageGranularity, |
5471 | VkDeviceSize allocSize, |
5472 | VkDeviceSize allocAlignment, |
5473 | bool upperAddress, |
5474 | VmaSuballocationType allocType, |
5475 | bool canMakeOtherLost, |
5476 | uint32_t strategy, |
5477 | VmaAllocationRequest* pAllocationRequest); |
5478 | |
5479 | virtual bool MakeRequestedAllocationsLost( |
5480 | uint32_t currentFrameIndex, |
5481 | uint32_t frameInUseCount, |
5482 | VmaAllocationRequest* pAllocationRequest); |
5483 | |
5484 | virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); |
5485 | |
5486 | virtual VkResult CheckCorruption(const void* pBlockData); |
5487 | |
5488 | virtual void Alloc( |
5489 | const VmaAllocationRequest& request, |
5490 | VmaSuballocationType type, |
5491 | VkDeviceSize allocSize, |
5492 | bool upperAddress, |
5493 | VmaAllocation hAllocation); |
5494 | |
5495 | virtual void Free(const VmaAllocation allocation); |
5496 | virtual void FreeAtOffset(VkDeviceSize offset); |
5497 | |
5498 | private: |
5499 | /* |
5500 | There are two suballocation vectors, used in ping-pong way. |
5501 | The one with index m_1stVectorIndex is called 1st. |
5502 | The one with index (m_1stVectorIndex ^ 1) is called 2nd. |
5503 | 2nd can be non-empty only when 1st is not empty. |
5504 | When 2nd is not empty, m_2ndVectorMode indicates its mode of operation. |
5505 | */ |
5506 | typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType; |
5507 | |
5508 | enum SECOND_VECTOR_MODE |
5509 | { |
5510 | SECOND_VECTOR_EMPTY, |
5511 | /* |
5512 | Suballocations in 2nd vector are created later than the ones in 1st, but they |
5513 | all have smaller offset. |
5514 | */ |
5515 | SECOND_VECTOR_RING_BUFFER, |
5516 | /* |
5517 | Suballocations in 2nd vector are upper side of double stack. |
5518 | They all have offsets higher than those in 1st vector. |
5519 | Top of this stack means smaller offsets, but higher indices in this vector. |
5520 | */ |
5521 | SECOND_VECTOR_DOUBLE_STACK, |
5522 | }; |
5523 | |
5524 | VkDeviceSize m_SumFreeSize; |
5525 | SuballocationVectorType m_Suballocations0, m_Suballocations1; |
5526 | uint32_t m_1stVectorIndex; |
5527 | SECOND_VECTOR_MODE m_2ndVectorMode; |
5528 | |
5529 | SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } |
5530 | SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } |
5531 | const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } |
5532 | const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } |
5533 | |
5534 | // Number of items in 1st vector with hAllocation = null at the beginning. |
5535 | size_t m_1stNullItemsBeginCount; |
5536 | // Number of other items in 1st vector with hAllocation = null somewhere in the middle. |
5537 | size_t m_1stNullItemsMiddleCount; |
5538 | // Number of items in 2nd vector with hAllocation = null. |
5539 | size_t m_2ndNullItemsCount; |
5540 | |
5541 | bool ShouldCompact1st() const; |
5542 | void CleanupAfterFree(); |
5543 | }; |
5544 | |
5545 | /* |
5546 | - GetSize() is the original size of allocated memory block. |
5547 | - m_UsableSize is this size aligned down to a power of two. |
5548 | All allocations and calculations happen relative to m_UsableSize. |
5549 | - GetUnusableSize() is the difference between them. |
5550 | It is repoted as separate, unused range, not available for allocations. |
5551 | |
5552 | Node at level 0 has size = m_UsableSize. |
5553 | Each next level contains nodes with size 2 times smaller than current level. |
5554 | m_LevelCount is the maximum number of levels to use in the current object. |
5555 | */ |
5556 | class VmaBlockMetadata_Buddy : public VmaBlockMetadata |
5557 | { |
5558 | VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy) |
5559 | public: |
5560 | VmaBlockMetadata_Buddy(VmaAllocator hAllocator); |
5561 | virtual ~VmaBlockMetadata_Buddy(); |
5562 | virtual void Init(VkDeviceSize size); |
5563 | |
5564 | virtual bool Validate() const; |
5565 | virtual size_t GetAllocationCount() const { return m_AllocationCount; } |
5566 | virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); } |
5567 | virtual VkDeviceSize GetUnusedRangeSizeMax() const; |
5568 | virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; } |
5569 | |
5570 | virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const; |
5571 | virtual void AddPoolStats(VmaPoolStats& inoutStats) const; |
5572 | |
5573 | #if VMA_STATS_STRING_ENABLED |
5574 | virtual void PrintDetailedMap(class VmaJsonWriter& json) const; |
5575 | #endif |
5576 | |
5577 | virtual bool CreateAllocationRequest( |
5578 | uint32_t currentFrameIndex, |
5579 | uint32_t frameInUseCount, |
5580 | VkDeviceSize bufferImageGranularity, |
5581 | VkDeviceSize allocSize, |
5582 | VkDeviceSize allocAlignment, |
5583 | bool upperAddress, |
5584 | VmaSuballocationType allocType, |
5585 | bool canMakeOtherLost, |
5586 | uint32_t strategy, |
5587 | VmaAllocationRequest* pAllocationRequest); |
5588 | |
5589 | virtual bool MakeRequestedAllocationsLost( |
5590 | uint32_t currentFrameIndex, |
5591 | uint32_t frameInUseCount, |
5592 | VmaAllocationRequest* pAllocationRequest); |
5593 | |
5594 | virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); |
5595 | |
5596 | virtual VkResult CheckCorruption(const void* /*pBlockData*/) { return VK_ERROR_FEATURE_NOT_PRESENT; } |
5597 | |
5598 | virtual void Alloc( |
5599 | const VmaAllocationRequest& request, |
5600 | VmaSuballocationType type, |
5601 | VkDeviceSize allocSize, |
5602 | bool upperAddress, |
5603 | VmaAllocation hAllocation); |
5604 | |
5605 | virtual void Free(const VmaAllocation allocation) { FreeAtOffset(alloc: allocation, offset: allocation->GetOffset()); } |
5606 | virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); } |
5607 | |
5608 | private: |
5609 | static const VkDeviceSize MIN_NODE_SIZE = 32; |
5610 | static const size_t MAX_LEVELS = 30; |
5611 | |
5612 | struct ValidationContext |
5613 | { |
5614 | size_t calculatedAllocationCount; |
5615 | size_t calculatedFreeCount; |
5616 | VkDeviceSize calculatedSumFreeSize; |
5617 | |
5618 | ValidationContext() : |
5619 | calculatedAllocationCount(0), |
5620 | calculatedFreeCount(0), |
5621 | calculatedSumFreeSize(0) { } |
5622 | }; |
5623 | |
5624 | struct Node |
5625 | { |
5626 | VkDeviceSize offset; |
5627 | enum TYPE |
5628 | { |
5629 | TYPE_FREE, |
5630 | TYPE_ALLOCATION, |
5631 | TYPE_SPLIT, |
5632 | TYPE_COUNT |
5633 | } type; |
5634 | Node* parent; |
5635 | Node* buddy; |
5636 | |
5637 | union |
5638 | { |
5639 | struct |
5640 | { |
5641 | Node* prev; |
5642 | Node* next; |
5643 | } free; |
5644 | struct |
5645 | { |
5646 | VmaAllocation alloc; |
5647 | } allocation; |
5648 | struct |
5649 | { |
5650 | Node* leftChild; |
5651 | } split; |
5652 | }; |
5653 | }; |
5654 | |
5655 | // Size of the memory block aligned down to a power of two. |
5656 | VkDeviceSize m_UsableSize; |
5657 | uint32_t m_LevelCount; |
5658 | |
5659 | Node* m_Root; |
5660 | struct { |
5661 | Node* front; |
5662 | Node* back; |
5663 | } m_FreeList[MAX_LEVELS]; |
5664 | // Number of nodes in the tree with type == TYPE_ALLOCATION. |
5665 | size_t m_AllocationCount; |
5666 | // Number of nodes in the tree with type == TYPE_FREE. |
5667 | size_t m_FreeCount; |
5668 | // This includes space wasted due to internal fragmentation. Doesn't include unusable size. |
5669 | VkDeviceSize m_SumFreeSize; |
5670 | |
5671 | VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; } |
5672 | void DeleteNode(Node* node); |
5673 | bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const; |
5674 | uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const; |
5675 | inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; } |
5676 | // Alloc passed just for validation. Can be null. |
5677 | void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset); |
5678 | void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const; |
5679 | // Adds node to the front of FreeList at given level. |
5680 | // node->type must be FREE. |
5681 | // node->free.prev, next can be undefined. |
5682 | void AddToFreeListFront(uint32_t level, Node* node); |
5683 | // Removes node from FreeList at given level. |
5684 | // node->type must be FREE. |
5685 | // node->free.prev, next stay untouched. |
5686 | void RemoveFromFreeList(uint32_t level, Node* node); |
5687 | |
5688 | #if VMA_STATS_STRING_ENABLED |
5689 | void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const; |
5690 | #endif |
5691 | }; |
5692 | |
5693 | /* |
5694 | Represents a single block of device memory (`VkDeviceMemory`) with all the |
5695 | data about its regions (aka suballocations, #VmaAllocation), assigned and free. |
5696 | |
5697 | Thread-safety: This class must be externally synchronized. |
5698 | */ |
5699 | class VmaDeviceMemoryBlock |
5700 | { |
5701 | VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock) |
5702 | public: |
5703 | VmaBlockMetadata* m_pMetadata; |
5704 | |
5705 | VmaDeviceMemoryBlock(VmaAllocator hAllocator); |
5706 | |
5707 | ~VmaDeviceMemoryBlock() |
5708 | { |
5709 | VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped." ); |
5710 | VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); |
5711 | } |
5712 | |
5713 | // Always call after construction. |
5714 | void Init( |
5715 | VmaAllocator hAllocator, |
5716 | uint32_t newMemoryTypeIndex, |
5717 | VkDeviceMemory newMemory, |
5718 | VkDeviceSize newSize, |
5719 | uint32_t id, |
5720 | uint32_t algorithm); |
5721 | // Always call before destruction. |
5722 | void Destroy(VmaAllocator allocator); |
5723 | |
5724 | VkDeviceMemory GetDeviceMemory() const { return m_hMemory; } |
5725 | uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } |
5726 | uint32_t GetId() const { return m_Id; } |
5727 | void* GetMappedData() const { return m_pMappedData; } |
5728 | |
5729 | // Validates all data structures inside this object. If not valid, returns false. |
5730 | bool Validate() const; |
5731 | |
5732 | VkResult CheckCorruption(VmaAllocator hAllocator); |
5733 | |
5734 | // ppData can be null. |
5735 | VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData); |
5736 | void Unmap(VmaAllocator hAllocator, uint32_t count); |
5737 | |
5738 | VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); |
5739 | VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); |
5740 | |
5741 | VkResult BindBufferMemory( |
5742 | const VmaAllocator hAllocator, |
5743 | const VmaAllocation hAllocation, |
5744 | VkBuffer hBuffer); |
5745 | VkResult BindImageMemory( |
5746 | const VmaAllocator hAllocator, |
5747 | const VmaAllocation hAllocation, |
5748 | VkImage hImage); |
5749 | |
5750 | private: |
5751 | uint32_t m_MemoryTypeIndex; |
5752 | uint32_t m_Id; |
5753 | VkDeviceMemory m_hMemory; |
5754 | |
5755 | /* |
5756 | Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory. |
5757 | Also protects m_MapCount, m_pMappedData. |
5758 | Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex. |
5759 | */ |
5760 | VMA_MUTEX m_Mutex; |
5761 | uint32_t m_MapCount; |
5762 | void* m_pMappedData; |
5763 | }; |
5764 | |
5765 | struct VmaPointerLess |
5766 | { |
5767 | bool operator()(const void* lhs, const void* rhs) const |
5768 | { |
5769 | return lhs < rhs; |
5770 | } |
5771 | }; |
5772 | |
5773 | struct VmaDefragmentationMove |
5774 | { |
5775 | size_t srcBlockIndex; |
5776 | size_t dstBlockIndex; |
5777 | VkDeviceSize srcOffset; |
5778 | VkDeviceSize dstOffset; |
5779 | VkDeviceSize size; |
5780 | }; |
5781 | |
5782 | class VmaDefragmentationAlgorithm; |
5783 | |
5784 | /* |
5785 | Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific |
5786 | Vulkan memory type. |
5787 | |
5788 | Synchronized internally with a mutex. |
5789 | */ |
5790 | struct VmaBlockVector |
5791 | { |
5792 | VMA_CLASS_NO_COPY(VmaBlockVector) |
5793 | public: |
5794 | VmaBlockVector( |
5795 | VmaAllocator hAllocator, |
5796 | uint32_t memoryTypeIndex, |
5797 | VkDeviceSize preferredBlockSize, |
5798 | size_t minBlockCount, |
5799 | size_t maxBlockCount, |
5800 | VkDeviceSize bufferImageGranularity, |
5801 | uint32_t frameInUseCount, |
5802 | bool isCustomPool, |
5803 | bool explicitBlockSize, |
5804 | uint32_t algorithm); |
5805 | ~VmaBlockVector(); |
5806 | |
5807 | VkResult CreateMinBlocks(); |
5808 | |
5809 | uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } |
5810 | VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; } |
5811 | VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } |
5812 | uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; } |
5813 | uint32_t GetAlgorithm() const { return m_Algorithm; } |
5814 | |
5815 | void GetPoolStats(VmaPoolStats* pStats); |
5816 | |
5817 | bool IsEmpty() const { return m_Blocks.empty(); } |
5818 | bool IsCorruptionDetectionEnabled() const; |
5819 | |
5820 | VkResult Allocate( |
5821 | VmaPool hCurrentPool, |
5822 | uint32_t currentFrameIndex, |
5823 | VkDeviceSize size, |
5824 | VkDeviceSize alignment, |
5825 | const VmaAllocationCreateInfo& createInfo, |
5826 | VmaSuballocationType suballocType, |
5827 | size_t allocationCount, |
5828 | VmaAllocation* pAllocations); |
5829 | |
5830 | void Free( |
5831 | VmaAllocation hAllocation); |
5832 | |
5833 | // Adds statistics of this BlockVector to pStats. |
5834 | void AddStats(VmaStats* pStats); |
5835 | |
5836 | #if VMA_STATS_STRING_ENABLED |
5837 | void PrintDetailedMap(class VmaJsonWriter& json); |
5838 | #endif |
5839 | |
5840 | void MakePoolAllocationsLost( |
5841 | uint32_t currentFrameIndex, |
5842 | size_t* pLostAllocationCount); |
5843 | VkResult CheckCorruption(); |
5844 | |
5845 | // Saves results in pCtx->res. |
5846 | void Defragment( |
5847 | class VmaBlockVectorDefragmentationContext* pCtx, |
5848 | VmaDefragmentationStats* pStats, |
5849 | VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove, |
5850 | VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove, |
5851 | VkCommandBuffer commandBuffer); |
5852 | void DefragmentationEnd( |
5853 | class VmaBlockVectorDefragmentationContext* pCtx, |
5854 | VmaDefragmentationStats* pStats); |
5855 | |
5856 | //////////////////////////////////////////////////////////////////////////////// |
5857 | // To be used only while the m_Mutex is locked. Used during defragmentation. |
5858 | |
5859 | size_t GetBlockCount() const { return m_Blocks.size(); } |
5860 | VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; } |
5861 | size_t CalcAllocationCount() const; |
5862 | bool IsBufferImageGranularityConflictPossible() const; |
5863 | |
5864 | private: |
5865 | friend class VmaDefragmentationAlgorithm_Generic; |
5866 | |
5867 | const VmaAllocator m_hAllocator; |
5868 | const uint32_t m_MemoryTypeIndex; |
5869 | const VkDeviceSize m_PreferredBlockSize; |
5870 | const size_t m_MinBlockCount; |
5871 | const size_t m_MaxBlockCount; |
5872 | const VkDeviceSize m_BufferImageGranularity; |
5873 | const uint32_t m_FrameInUseCount; |
5874 | const bool m_IsCustomPool; |
5875 | const bool m_ExplicitBlockSize; |
5876 | const uint32_t m_Algorithm; |
5877 | /* There can be at most one allocation that is completely empty - a |
5878 | hysteresis to avoid pessimistic case of alternating creation and destruction |
5879 | of a VkDeviceMemory. */ |
5880 | bool m_HasEmptyBlock; |
5881 | VMA_RW_MUTEX m_Mutex; |
5882 | // Incrementally sorted by sumFreeSize, ascending. |
5883 | VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks; |
5884 | uint32_t m_NextBlockId; |
5885 | |
5886 | VkDeviceSize CalcMaxBlockSize() const; |
5887 | |
5888 | // Finds and removes given block from vector. |
5889 | void Remove(VmaDeviceMemoryBlock* pBlock); |
5890 | |
5891 | // Performs single step in sorting m_Blocks. They may not be fully sorted |
5892 | // after this call. |
5893 | void IncrementallySortBlocks(); |
5894 | |
5895 | VkResult AllocatePage( |
5896 | VmaPool hCurrentPool, |
5897 | uint32_t currentFrameIndex, |
5898 | VkDeviceSize size, |
5899 | VkDeviceSize alignment, |
5900 | const VmaAllocationCreateInfo& createInfo, |
5901 | VmaSuballocationType suballocType, |
5902 | VmaAllocation* pAllocation); |
5903 | |
5904 | // To be used only without CAN_MAKE_OTHER_LOST flag. |
5905 | VkResult AllocateFromBlock( |
5906 | VmaDeviceMemoryBlock* pBlock, |
5907 | VmaPool hCurrentPool, |
5908 | uint32_t currentFrameIndex, |
5909 | VkDeviceSize size, |
5910 | VkDeviceSize alignment, |
5911 | VmaAllocationCreateFlags allocFlags, |
5912 | void* pUserData, |
5913 | VmaSuballocationType suballocType, |
5914 | uint32_t strategy, |
5915 | VmaAllocation* pAllocation); |
5916 | |
5917 | VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); |
5918 | |
5919 | // Saves result to pCtx->res. |
5920 | void ApplyDefragmentationMovesCpu( |
5921 | class VmaBlockVectorDefragmentationContext* pDefragCtx, |
5922 | const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves); |
5923 | // Saves result to pCtx->res. |
5924 | void ApplyDefragmentationMovesGpu( |
5925 | class VmaBlockVectorDefragmentationContext* pDefragCtx, |
5926 | const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
5927 | VkCommandBuffer commandBuffer); |
5928 | |
5929 | /* |
5930 | Used during defragmentation. pDefragmentationStats is optional. It's in/out |
5931 | - updated with new data. |
5932 | */ |
5933 | void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats); |
5934 | }; |
5935 | |
5936 | struct VmaPool_T |
5937 | { |
5938 | VMA_CLASS_NO_COPY(VmaPool_T) |
5939 | public: |
5940 | VmaBlockVector m_BlockVector; |
5941 | |
5942 | VmaPool_T( |
5943 | VmaAllocator hAllocator, |
5944 | const VmaPoolCreateInfo& createInfo, |
5945 | VkDeviceSize preferredBlockSize); |
5946 | ~VmaPool_T(); |
5947 | |
5948 | uint32_t GetId() const { return m_Id; } |
5949 | void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; } |
5950 | |
5951 | #if VMA_STATS_STRING_ENABLED |
5952 | //void PrintDetailedMap(class VmaStringBuilder& sb); |
5953 | #endif |
5954 | |
5955 | private: |
5956 | uint32_t m_Id; |
5957 | }; |
5958 | |
5959 | /* |
5960 | Performs defragmentation: |
5961 | |
5962 | - Updates `pBlockVector->m_pMetadata`. |
5963 | - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset(). |
5964 | - Does not move actual data, only returns requested moves as `moves`. |
5965 | */ |
5966 | class VmaDefragmentationAlgorithm |
5967 | { |
5968 | VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm) |
5969 | public: |
5970 | VmaDefragmentationAlgorithm( |
5971 | VmaAllocator hAllocator, |
5972 | VmaBlockVector* pBlockVector, |
5973 | uint32_t currentFrameIndex) : |
5974 | m_hAllocator(hAllocator), |
5975 | m_pBlockVector(pBlockVector), |
5976 | m_CurrentFrameIndex(currentFrameIndex) |
5977 | { |
5978 | } |
5979 | virtual ~VmaDefragmentationAlgorithm() |
5980 | { |
5981 | } |
5982 | |
5983 | virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0; |
5984 | virtual void AddAll() = 0; |
5985 | |
5986 | virtual VkResult Defragment( |
5987 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
5988 | VkDeviceSize maxBytesToMove, |
5989 | uint32_t maxAllocationsToMove) = 0; |
5990 | |
5991 | virtual VkDeviceSize GetBytesMoved() const = 0; |
5992 | virtual uint32_t GetAllocationsMoved() const = 0; |
5993 | |
5994 | protected: |
5995 | VmaAllocator const m_hAllocator; |
5996 | VmaBlockVector* const m_pBlockVector; |
5997 | const uint32_t m_CurrentFrameIndex; |
5998 | |
5999 | struct AllocationInfo |
6000 | { |
6001 | VmaAllocation m_hAllocation; |
6002 | VkBool32* m_pChanged; |
6003 | |
6004 | AllocationInfo() : |
6005 | m_hAllocation(VK_NULL_HANDLE), |
6006 | m_pChanged(VMA_NULL) |
6007 | { |
6008 | } |
6009 | AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) : |
6010 | m_hAllocation(hAlloc), |
6011 | m_pChanged(pChanged) |
6012 | { |
6013 | } |
6014 | }; |
6015 | }; |
6016 | |
6017 | class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm |
6018 | { |
6019 | VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic) |
6020 | public: |
6021 | VmaDefragmentationAlgorithm_Generic( |
6022 | VmaAllocator hAllocator, |
6023 | VmaBlockVector* pBlockVector, |
6024 | uint32_t currentFrameIndex, |
6025 | bool overlappingMoveSupported); |
6026 | virtual ~VmaDefragmentationAlgorithm_Generic(); |
6027 | |
6028 | virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); |
6029 | virtual void AddAll() { m_AllAllocations = true; } |
6030 | |
6031 | virtual VkResult Defragment( |
6032 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
6033 | VkDeviceSize maxBytesToMove, |
6034 | uint32_t maxAllocationsToMove); |
6035 | |
6036 | virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; } |
6037 | virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; } |
6038 | |
6039 | private: |
6040 | uint32_t m_AllocationCount; |
6041 | bool m_AllAllocations; |
6042 | |
6043 | VkDeviceSize m_BytesMoved; |
6044 | uint32_t m_AllocationsMoved; |
6045 | |
6046 | struct AllocationInfoSizeGreater |
6047 | { |
6048 | bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const |
6049 | { |
6050 | return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize(); |
6051 | } |
6052 | }; |
6053 | |
6054 | struct AllocationInfoOffsetGreater |
6055 | { |
6056 | bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const |
6057 | { |
6058 | return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset(); |
6059 | } |
6060 | }; |
6061 | |
6062 | struct BlockInfo |
6063 | { |
6064 | size_t m_OriginalBlockIndex; |
6065 | VmaDeviceMemoryBlock* m_pBlock; |
6066 | bool m_HasNonMovableAllocations; |
6067 | VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations; |
6068 | |
6069 | BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) : |
6070 | m_OriginalBlockIndex(SIZE_MAX), |
6071 | m_pBlock(VMA_NULL), |
6072 | m_HasNonMovableAllocations(true), |
6073 | m_Allocations(pAllocationCallbacks) |
6074 | { |
6075 | } |
6076 | |
6077 | void CalcHasNonMovableAllocations() |
6078 | { |
6079 | const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount(); |
6080 | const size_t defragmentAllocCount = m_Allocations.size(); |
6081 | m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount; |
6082 | } |
6083 | |
6084 | void SortAllocationsBySizeDescending() |
6085 | { |
6086 | VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater()); |
6087 | } |
6088 | |
6089 | void SortAllocationsByOffsetDescending() |
6090 | { |
6091 | VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater()); |
6092 | } |
6093 | }; |
6094 | |
6095 | struct BlockPointerLess |
6096 | { |
6097 | bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const |
6098 | { |
6099 | return pLhsBlockInfo->m_pBlock < pRhsBlock; |
6100 | } |
6101 | bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const |
6102 | { |
6103 | return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock; |
6104 | } |
6105 | }; |
6106 | |
6107 | // 1. Blocks with some non-movable allocations go first. |
6108 | // 2. Blocks with smaller sumFreeSize go first. |
6109 | struct BlockInfoCompareMoveDestination |
6110 | { |
6111 | bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const |
6112 | { |
6113 | if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations) |
6114 | { |
6115 | return true; |
6116 | } |
6117 | if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations) |
6118 | { |
6119 | return false; |
6120 | } |
6121 | if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize()) |
6122 | { |
6123 | return true; |
6124 | } |
6125 | return false; |
6126 | } |
6127 | }; |
6128 | |
6129 | typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector; |
6130 | BlockInfoVector m_Blocks; |
6131 | |
6132 | VkResult DefragmentRound( |
6133 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
6134 | VkDeviceSize maxBytesToMove, |
6135 | uint32_t maxAllocationsToMove); |
6136 | |
6137 | size_t CalcBlocksWithNonMovableCount() const; |
6138 | |
6139 | static bool MoveMakesSense( |
6140 | size_t dstBlockIndex, VkDeviceSize dstOffset, |
6141 | size_t srcBlockIndex, VkDeviceSize srcOffset); |
6142 | }; |
6143 | |
6144 | class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm |
6145 | { |
6146 | VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast) |
6147 | public: |
6148 | VmaDefragmentationAlgorithm_Fast( |
6149 | VmaAllocator hAllocator, |
6150 | VmaBlockVector* pBlockVector, |
6151 | uint32_t currentFrameIndex, |
6152 | bool overlappingMoveSupported); |
6153 | virtual ~VmaDefragmentationAlgorithm_Fast(); |
6154 | |
6155 | virtual void AddAllocation(VmaAllocation /*hAlloc*/, VkBool32* /*pChanged*/) { ++m_AllocationCount; } |
6156 | virtual void AddAll() { m_AllAllocations = true; } |
6157 | |
6158 | virtual VkResult Defragment( |
6159 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
6160 | VkDeviceSize maxBytesToMove, |
6161 | uint32_t maxAllocationsToMove); |
6162 | |
6163 | virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; } |
6164 | virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; } |
6165 | |
6166 | private: |
6167 | struct BlockInfo |
6168 | { |
6169 | size_t origBlockIndex; |
6170 | }; |
6171 | |
6172 | class FreeSpaceDatabase |
6173 | { |
6174 | public: |
6175 | FreeSpaceDatabase() |
6176 | { |
6177 | FreeSpace s = {}; |
6178 | s.blockInfoIndex = SIZE_MAX; |
6179 | for(size_t i = 0; i < MAX_COUNT; ++i) |
6180 | { |
6181 | m_FreeSpaces[i] = s; |
6182 | } |
6183 | } |
6184 | |
6185 | void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size) |
6186 | { |
6187 | if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) |
6188 | { |
6189 | return; |
6190 | } |
6191 | |
6192 | // Find first invalid or the smallest structure. |
6193 | size_t bestIndex = SIZE_MAX; |
6194 | for(size_t i = 0; i < MAX_COUNT; ++i) |
6195 | { |
6196 | // Empty structure. |
6197 | if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX) |
6198 | { |
6199 | bestIndex = i; |
6200 | break; |
6201 | } |
6202 | if(m_FreeSpaces[i].size < size && |
6203 | (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size)) |
6204 | { |
6205 | bestIndex = i; |
6206 | } |
6207 | } |
6208 | |
6209 | if(bestIndex != SIZE_MAX) |
6210 | { |
6211 | m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex; |
6212 | m_FreeSpaces[bestIndex].offset = offset; |
6213 | m_FreeSpaces[bestIndex].size = size; |
6214 | } |
6215 | } |
6216 | |
6217 | bool Fetch(VkDeviceSize alignment, VkDeviceSize size, |
6218 | size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset) |
6219 | { |
6220 | size_t bestIndex = SIZE_MAX; |
6221 | VkDeviceSize bestFreeSpaceAfter = 0; |
6222 | for(size_t i = 0; i < MAX_COUNT; ++i) |
6223 | { |
6224 | // Structure is valid. |
6225 | if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX) |
6226 | { |
6227 | const VkDeviceSize dstOffset = VmaAlignUp(val: m_FreeSpaces[i].offset, align: alignment); |
6228 | // Allocation fits into this structure. |
6229 | if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size) |
6230 | { |
6231 | const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) - |
6232 | (dstOffset + size); |
6233 | if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter) |
6234 | { |
6235 | bestIndex = i; |
6236 | bestFreeSpaceAfter = freeSpaceAfter; |
6237 | } |
6238 | } |
6239 | } |
6240 | } |
6241 | |
6242 | if(bestIndex != SIZE_MAX) |
6243 | { |
6244 | outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex; |
6245 | outDstOffset = VmaAlignUp(val: m_FreeSpaces[bestIndex].offset, align: alignment); |
6246 | |
6247 | if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) |
6248 | { |
6249 | // Leave this structure for remaining empty space. |
6250 | const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size; |
6251 | m_FreeSpaces[bestIndex].offset += alignmentPlusSize; |
6252 | m_FreeSpaces[bestIndex].size -= alignmentPlusSize; |
6253 | } |
6254 | else |
6255 | { |
6256 | // This structure becomes invalid. |
6257 | m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX; |
6258 | } |
6259 | |
6260 | return true; |
6261 | } |
6262 | |
6263 | return false; |
6264 | } |
6265 | |
6266 | private: |
6267 | static const size_t MAX_COUNT = 4; |
6268 | |
6269 | struct FreeSpace |
6270 | { |
6271 | size_t blockInfoIndex; // SIZE_MAX means this structure is invalid. |
6272 | VkDeviceSize offset; |
6273 | VkDeviceSize size; |
6274 | } m_FreeSpaces[MAX_COUNT]; |
6275 | }; |
6276 | |
6277 | const bool m_OverlappingMoveSupported; |
6278 | |
6279 | uint32_t m_AllocationCount; |
6280 | bool m_AllAllocations; |
6281 | |
6282 | VkDeviceSize m_BytesMoved; |
6283 | uint32_t m_AllocationsMoved; |
6284 | |
6285 | VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos; |
6286 | |
6287 | void PreprocessMetadata(); |
6288 | void PostprocessMetadata(); |
6289 | void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc); |
6290 | }; |
6291 | |
6292 | struct VmaBlockDefragmentationContext |
6293 | { |
6294 | enum BLOCK_FLAG |
6295 | { |
6296 | BLOCK_FLAG_USED = 0x00000001, |
6297 | }; |
6298 | uint32_t flags; |
6299 | VkBuffer hBuffer; |
6300 | |
6301 | VmaBlockDefragmentationContext() : |
6302 | flags(0), |
6303 | hBuffer(VK_NULL_HANDLE) |
6304 | { |
6305 | } |
6306 | }; |
6307 | |
6308 | class VmaBlockVectorDefragmentationContext |
6309 | { |
6310 | VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext) |
6311 | public: |
6312 | VkResult res; |
6313 | bool mutexLocked; |
6314 | VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts; |
6315 | |
6316 | VmaBlockVectorDefragmentationContext( |
6317 | VmaAllocator hAllocator, |
6318 | VmaPool hCustomPool, // Optional. |
6319 | VmaBlockVector* pBlockVector, |
6320 | uint32_t currFrameIndex, |
6321 | uint32_t flags); |
6322 | ~VmaBlockVectorDefragmentationContext(); |
6323 | |
6324 | VmaPool GetCustomPool() const { return m_hCustomPool; } |
6325 | VmaBlockVector* GetBlockVector() const { return m_pBlockVector; } |
6326 | VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; } |
6327 | |
6328 | void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); |
6329 | void AddAll() { m_AllAllocations = true; } |
6330 | |
6331 | void Begin(bool overlappingMoveSupported); |
6332 | |
6333 | private: |
6334 | const VmaAllocator m_hAllocator; |
6335 | // Null if not from custom pool. |
6336 | const VmaPool m_hCustomPool; |
6337 | // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors. |
6338 | VmaBlockVector* const m_pBlockVector; |
6339 | const uint32_t m_CurrFrameIndex; |
6340 | /*const uint32_t m_AlgorithmFlags;*/ |
6341 | // Owner of this object. |
6342 | VmaDefragmentationAlgorithm* m_pAlgorithm; |
6343 | |
6344 | struct AllocInfo |
6345 | { |
6346 | VmaAllocation hAlloc; |
6347 | VkBool32* pChanged; |
6348 | }; |
6349 | // Used between constructor and Begin. |
6350 | VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations; |
6351 | bool m_AllAllocations; |
6352 | }; |
6353 | |
6354 | struct VmaDefragmentationContext_T |
6355 | { |
6356 | private: |
6357 | VMA_CLASS_NO_COPY(VmaDefragmentationContext_T) |
6358 | public: |
6359 | VmaDefragmentationContext_T( |
6360 | VmaAllocator hAllocator, |
6361 | uint32_t currFrameIndex, |
6362 | uint32_t flags, |
6363 | VmaDefragmentationStats* pStats); |
6364 | ~VmaDefragmentationContext_T(); |
6365 | |
6366 | void AddPools(uint32_t poolCount, VmaPool* pPools); |
6367 | void AddAllocations( |
6368 | uint32_t allocationCount, |
6369 | VmaAllocation* pAllocations, |
6370 | VkBool32* pAllocationsChanged); |
6371 | |
6372 | /* |
6373 | Returns: |
6374 | - `VK_SUCCESS` if succeeded and object can be destroyed immediately. |
6375 | - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd(). |
6376 | - Negative value if error occured and object can be destroyed immediately. |
6377 | */ |
6378 | VkResult Defragment( |
6379 | VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove, |
6380 | VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove, |
6381 | VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats); |
6382 | |
6383 | private: |
6384 | const VmaAllocator m_hAllocator; |
6385 | const uint32_t m_CurrFrameIndex; |
6386 | const uint32_t m_Flags; |
6387 | VmaDefragmentationStats* const m_pStats; |
6388 | // Owner of these objects. |
6389 | VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES]; |
6390 | // Owner of these objects. |
6391 | VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts; |
6392 | }; |
6393 | |
6394 | #if VMA_RECORDING_ENABLED |
6395 | |
6396 | class VmaRecorder |
6397 | { |
6398 | public: |
6399 | VmaRecorder(); |
6400 | VkResult Init(const VmaRecordSettings& settings, bool useMutex); |
6401 | void WriteConfiguration( |
6402 | const VkPhysicalDeviceProperties& devProps, |
6403 | const VkPhysicalDeviceMemoryProperties& memProps, |
6404 | bool dedicatedAllocationExtensionEnabled); |
6405 | ~VmaRecorder(); |
6406 | |
6407 | void RecordCreateAllocator(uint32_t frameIndex); |
6408 | void RecordDestroyAllocator(uint32_t frameIndex); |
6409 | void RecordCreatePool(uint32_t frameIndex, |
6410 | const VmaPoolCreateInfo& createInfo, |
6411 | VmaPool pool); |
6412 | void RecordDestroyPool(uint32_t frameIndex, VmaPool pool); |
6413 | void RecordAllocateMemory(uint32_t frameIndex, |
6414 | const VkMemoryRequirements& vkMemReq, |
6415 | const VmaAllocationCreateInfo& createInfo, |
6416 | VmaAllocation allocation); |
6417 | void RecordAllocateMemoryPages(uint32_t frameIndex, |
6418 | const VkMemoryRequirements& vkMemReq, |
6419 | const VmaAllocationCreateInfo& createInfo, |
6420 | uint64_t allocationCount, |
6421 | const VmaAllocation* pAllocations); |
6422 | void RecordAllocateMemoryForBuffer(uint32_t frameIndex, |
6423 | const VkMemoryRequirements& vkMemReq, |
6424 | bool requiresDedicatedAllocation, |
6425 | bool prefersDedicatedAllocation, |
6426 | const VmaAllocationCreateInfo& createInfo, |
6427 | VmaAllocation allocation); |
6428 | void RecordAllocateMemoryForImage(uint32_t frameIndex, |
6429 | const VkMemoryRequirements& vkMemReq, |
6430 | bool requiresDedicatedAllocation, |
6431 | bool prefersDedicatedAllocation, |
6432 | const VmaAllocationCreateInfo& createInfo, |
6433 | VmaAllocation allocation); |
6434 | void RecordFreeMemory(uint32_t frameIndex, |
6435 | VmaAllocation allocation); |
6436 | void RecordFreeMemoryPages(uint32_t frameIndex, |
6437 | uint64_t allocationCount, |
6438 | const VmaAllocation* pAllocations); |
6439 | void RecordResizeAllocation( |
6440 | uint32_t frameIndex, |
6441 | VmaAllocation allocation, |
6442 | VkDeviceSize newSize); |
6443 | void RecordSetAllocationUserData(uint32_t frameIndex, |
6444 | VmaAllocation allocation, |
6445 | const void* pUserData); |
6446 | void RecordCreateLostAllocation(uint32_t frameIndex, |
6447 | VmaAllocation allocation); |
6448 | void RecordMapMemory(uint32_t frameIndex, |
6449 | VmaAllocation allocation); |
6450 | void RecordUnmapMemory(uint32_t frameIndex, |
6451 | VmaAllocation allocation); |
6452 | void RecordFlushAllocation(uint32_t frameIndex, |
6453 | VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); |
6454 | void RecordInvalidateAllocation(uint32_t frameIndex, |
6455 | VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); |
6456 | void RecordCreateBuffer(uint32_t frameIndex, |
6457 | const VkBufferCreateInfo& bufCreateInfo, |
6458 | const VmaAllocationCreateInfo& allocCreateInfo, |
6459 | VmaAllocation allocation); |
6460 | void RecordCreateImage(uint32_t frameIndex, |
6461 | const VkImageCreateInfo& imageCreateInfo, |
6462 | const VmaAllocationCreateInfo& allocCreateInfo, |
6463 | VmaAllocation allocation); |
6464 | void RecordDestroyBuffer(uint32_t frameIndex, |
6465 | VmaAllocation allocation); |
6466 | void RecordDestroyImage(uint32_t frameIndex, |
6467 | VmaAllocation allocation); |
6468 | void RecordTouchAllocation(uint32_t frameIndex, |
6469 | VmaAllocation allocation); |
6470 | void RecordGetAllocationInfo(uint32_t frameIndex, |
6471 | VmaAllocation allocation); |
6472 | void RecordMakePoolAllocationsLost(uint32_t frameIndex, |
6473 | VmaPool pool); |
6474 | void RecordDefragmentationBegin(uint32_t frameIndex, |
6475 | const VmaDefragmentationInfo2& info, |
6476 | VmaDefragmentationContext ctx); |
6477 | void RecordDefragmentationEnd(uint32_t frameIndex, |
6478 | VmaDefragmentationContext ctx); |
6479 | |
6480 | private: |
6481 | struct CallParams |
6482 | { |
6483 | uint32_t threadId; |
6484 | double time; |
6485 | }; |
6486 | |
6487 | class UserDataString |
6488 | { |
6489 | public: |
6490 | UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData); |
6491 | const char* GetString() const { return m_Str; } |
6492 | |
6493 | private: |
6494 | char m_PtrStr[17]; |
6495 | const char* m_Str; |
6496 | }; |
6497 | |
6498 | bool m_UseMutex; |
6499 | VmaRecordFlags m_Flags; |
6500 | FILE* m_File; |
6501 | VMA_MUTEX m_FileMutex; |
6502 | int64_t m_Freq; |
6503 | int64_t m_StartCounter; |
6504 | |
6505 | void GetBasicParams(CallParams& outParams); |
6506 | |
6507 | // T must be a pointer type, e.g. VmaAllocation, VmaPool. |
6508 | template<typename T> |
6509 | void PrintPointerList(uint64_t count, const T* pItems) |
6510 | { |
6511 | if(count) |
6512 | { |
6513 | fprintf(m_File, "%p" , pItems[0]); |
6514 | for(uint64_t i = 1; i < count; ++i) |
6515 | { |
6516 | fprintf(m_File, " %p" , pItems[i]); |
6517 | } |
6518 | } |
6519 | } |
6520 | |
6521 | void PrintPointerList(uint64_t count, const VmaAllocation* pItems); |
6522 | void Flush(); |
6523 | }; |
6524 | |
6525 | #endif // #if VMA_RECORDING_ENABLED |
6526 | |
6527 | // Main allocator object. |
6528 | struct VmaAllocator_T |
6529 | { |
6530 | VMA_CLASS_NO_COPY(VmaAllocator_T) |
6531 | public: |
6532 | bool m_UseMutex; |
6533 | bool m_UseKhrDedicatedAllocation; |
6534 | VkDevice m_hDevice; |
6535 | bool m_AllocationCallbacksSpecified; |
6536 | VkAllocationCallbacks m_AllocationCallbacks; |
6537 | VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks; |
6538 | |
6539 | // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap. |
6540 | VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS]; |
6541 | VMA_MUTEX m_HeapSizeLimitMutex; |
6542 | |
6543 | VkPhysicalDeviceProperties m_PhysicalDeviceProperties; |
6544 | VkPhysicalDeviceMemoryProperties m_MemProps; |
6545 | |
6546 | // Default pools. |
6547 | VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES]; |
6548 | |
6549 | // Each vector is sorted by memory (handle value). |
6550 | typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType; |
6551 | AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES]; |
6552 | VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES]; |
6553 | |
6554 | VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); |
6555 | VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo); |
6556 | ~VmaAllocator_T(); |
6557 | |
6558 | const VkAllocationCallbacks* GetAllocationCallbacks() const |
6559 | { |
6560 | return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0; |
6561 | } |
6562 | const VmaVulkanFunctions& GetVulkanFunctions() const |
6563 | { |
6564 | return m_VulkanFunctions; |
6565 | } |
6566 | |
6567 | VkDeviceSize GetBufferImageGranularity() const |
6568 | { |
6569 | return VMA_MAX( |
6570 | static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY), |
6571 | m_PhysicalDeviceProperties.limits.bufferImageGranularity); |
6572 | } |
6573 | |
6574 | uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; } |
6575 | uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; } |
6576 | |
6577 | uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const |
6578 | { |
6579 | VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount); |
6580 | return m_MemProps.memoryTypes[memTypeIndex].heapIndex; |
6581 | } |
6582 | // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT. |
6583 | bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const |
6584 | { |
6585 | return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == |
6586 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
6587 | } |
6588 | // Minimum alignment for all allocations in specific memory type. |
6589 | VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const |
6590 | { |
6591 | return IsMemoryTypeNonCoherent(memTypeIndex) ? |
6592 | VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) : |
6593 | (VkDeviceSize)VMA_DEBUG_ALIGNMENT; |
6594 | } |
6595 | |
6596 | bool IsIntegratedGpu() const |
6597 | { |
6598 | return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; |
6599 | } |
6600 | |
6601 | #if VMA_RECORDING_ENABLED |
6602 | VmaRecorder* GetRecorder() const { return m_pRecorder; } |
6603 | #endif |
6604 | |
6605 | void GetBufferMemoryRequirements( |
6606 | VkBuffer hBuffer, |
6607 | VkMemoryRequirements& memReq, |
6608 | bool& requiresDedicatedAllocation, |
6609 | bool& prefersDedicatedAllocation) const; |
6610 | void GetImageMemoryRequirements( |
6611 | VkImage hImage, |
6612 | VkMemoryRequirements& memReq, |
6613 | bool& requiresDedicatedAllocation, |
6614 | bool& prefersDedicatedAllocation) const; |
6615 | |
6616 | // Main allocation function. |
6617 | VkResult AllocateMemory( |
6618 | const VkMemoryRequirements& vkMemReq, |
6619 | bool requiresDedicatedAllocation, |
6620 | bool prefersDedicatedAllocation, |
6621 | VkBuffer dedicatedBuffer, |
6622 | VkImage dedicatedImage, |
6623 | const VmaAllocationCreateInfo& createInfo, |
6624 | VmaSuballocationType suballocType, |
6625 | size_t allocationCount, |
6626 | VmaAllocation* pAllocations); |
6627 | |
6628 | // Main deallocation function. |
6629 | void FreeMemory( |
6630 | size_t allocationCount, |
6631 | const VmaAllocation* pAllocations); |
6632 | |
6633 | VkResult ResizeAllocation( |
6634 | const VmaAllocation alloc, |
6635 | VkDeviceSize newSize); |
6636 | |
6637 | void CalculateStats(VmaStats* pStats); |
6638 | |
6639 | #if VMA_STATS_STRING_ENABLED |
6640 | void PrintDetailedMap(class VmaJsonWriter& json); |
6641 | #endif |
6642 | |
6643 | VkResult DefragmentationBegin( |
6644 | const VmaDefragmentationInfo2& info, |
6645 | VmaDefragmentationStats* pStats, |
6646 | VmaDefragmentationContext* pContext); |
6647 | VkResult DefragmentationEnd( |
6648 | VmaDefragmentationContext context); |
6649 | |
6650 | void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo); |
6651 | bool TouchAllocation(VmaAllocation hAllocation); |
6652 | |
6653 | VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool); |
6654 | void DestroyPool(VmaPool pool); |
6655 | void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats); |
6656 | |
6657 | void SetCurrentFrameIndex(uint32_t frameIndex); |
6658 | uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } |
6659 | |
6660 | void MakePoolAllocationsLost( |
6661 | VmaPool hPool, |
6662 | size_t* pLostAllocationCount); |
6663 | VkResult CheckPoolCorruption(VmaPool hPool); |
6664 | VkResult CheckCorruption(uint32_t memoryTypeBits); |
6665 | |
6666 | void CreateLostAllocation(VmaAllocation* pAllocation); |
6667 | |
6668 | VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory); |
6669 | void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory); |
6670 | |
6671 | VkResult Map(VmaAllocation hAllocation, void** ppData); |
6672 | void Unmap(VmaAllocation hAllocation); |
6673 | |
6674 | VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer); |
6675 | VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage); |
6676 | |
6677 | void FlushOrInvalidateAllocation( |
6678 | VmaAllocation hAllocation, |
6679 | VkDeviceSize offset, VkDeviceSize size, |
6680 | VMA_CACHE_OPERATION op); |
6681 | |
6682 | void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern); |
6683 | |
6684 | private: |
6685 | VkDeviceSize m_PreferredLargeHeapBlockSize; |
6686 | |
6687 | VkPhysicalDevice m_PhysicalDevice; |
6688 | VMA_ATOMIC_UINT32 m_CurrentFrameIndex; |
6689 | |
6690 | VMA_RW_MUTEX m_PoolsMutex; |
6691 | // Protected by m_PoolsMutex. Sorted by pointer value. |
6692 | VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools; |
6693 | uint32_t m_NextPoolId; |
6694 | |
6695 | VmaVulkanFunctions m_VulkanFunctions; |
6696 | |
6697 | #if VMA_RECORDING_ENABLED |
6698 | VmaRecorder* m_pRecorder; |
6699 | #endif |
6700 | |
6701 | void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); |
6702 | |
6703 | VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); |
6704 | |
6705 | VkResult AllocateMemoryOfType( |
6706 | VkDeviceSize size, |
6707 | VkDeviceSize alignment, |
6708 | bool dedicatedAllocation, |
6709 | VkBuffer dedicatedBuffer, |
6710 | VkImage dedicatedImage, |
6711 | const VmaAllocationCreateInfo& createInfo, |
6712 | uint32_t memTypeIndex, |
6713 | VmaSuballocationType suballocType, |
6714 | size_t allocationCount, |
6715 | VmaAllocation* pAllocations); |
6716 | |
6717 | // Helper function only to be used inside AllocateDedicatedMemory. |
6718 | VkResult AllocateDedicatedMemoryPage( |
6719 | VkDeviceSize size, |
6720 | VmaSuballocationType suballocType, |
6721 | uint32_t memTypeIndex, |
6722 | const VkMemoryAllocateInfo& allocInfo, |
6723 | bool map, |
6724 | bool isUserDataString, |
6725 | void* pUserData, |
6726 | VmaAllocation* pAllocation); |
6727 | |
6728 | // Allocates and registers new VkDeviceMemory specifically for dedicated allocations. |
6729 | VkResult AllocateDedicatedMemory( |
6730 | VkDeviceSize size, |
6731 | VmaSuballocationType suballocType, |
6732 | uint32_t memTypeIndex, |
6733 | bool map, |
6734 | bool isUserDataString, |
6735 | void* pUserData, |
6736 | VkBuffer dedicatedBuffer, |
6737 | VkImage dedicatedImage, |
6738 | size_t allocationCount, |
6739 | VmaAllocation* pAllocations); |
6740 | |
6741 | // Tries to free pMemory as Dedicated Memory. Returns true if found and freed. |
6742 | void FreeDedicatedMemory(VmaAllocation allocation); |
6743 | }; |
6744 | |
6745 | //////////////////////////////////////////////////////////////////////////////// |
6746 | // Memory allocation #2 after VmaAllocator_T definition |
6747 | |
6748 | static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment) |
6749 | { |
6750 | return VmaMalloc(pAllocationCallbacks: &hAllocator->m_AllocationCallbacks, size, alignment); |
6751 | } |
6752 | |
6753 | static void VmaFree(VmaAllocator hAllocator, void* ptr) |
6754 | { |
6755 | VmaFree(pAllocationCallbacks: &hAllocator->m_AllocationCallbacks, ptr); |
6756 | } |
6757 | |
6758 | template<typename T> |
6759 | static T* VmaAllocate(VmaAllocator hAllocator) |
6760 | { |
6761 | return (T*)VmaMalloc(hAllocator, size: sizeof(T), VMA_ALIGN_OF(T)); |
6762 | } |
6763 | |
6764 | template<typename T> |
6765 | static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count) |
6766 | { |
6767 | return (T*)VmaMalloc(hAllocator, size: sizeof(T) * count, VMA_ALIGN_OF(T)); |
6768 | } |
6769 | |
6770 | template<typename T> |
6771 | static void vma_delete(VmaAllocator hAllocator, T* ptr) |
6772 | { |
6773 | if(ptr != VMA_NULL) |
6774 | { |
6775 | ptr->~T(); |
6776 | VmaFree(hAllocator, ptr); |
6777 | } |
6778 | } |
6779 | |
6780 | template<typename T> |
6781 | static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count) |
6782 | { |
6783 | if(ptr != VMA_NULL) |
6784 | { |
6785 | for(size_t i = count; i--; ) |
6786 | ptr[i].~T(); |
6787 | VmaFree(hAllocator, ptr); |
6788 | } |
6789 | } |
6790 | |
6791 | //////////////////////////////////////////////////////////////////////////////// |
6792 | // VmaStringBuilder |
6793 | |
6794 | #if VMA_STATS_STRING_ENABLED |
6795 | |
6796 | class VmaStringBuilder |
6797 | { |
6798 | public: |
6799 | VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { } |
6800 | size_t GetLength() const { return m_Data.size(); } |
6801 | const char* GetData() const { return m_Data.data(); } |
6802 | |
6803 | void Add(char ch) { m_Data.push_back(src: ch); } |
6804 | void Add(const char* pStr); |
6805 | void AddNewLine() { Add(ch: '\n'); } |
6806 | void AddNumber(uint32_t num); |
6807 | void AddNumber(uint64_t num); |
6808 | void AddPointer(const void* ptr); |
6809 | |
6810 | private: |
6811 | VmaVector< char, VmaStlAllocator<char> > m_Data; |
6812 | }; |
6813 | |
6814 | void VmaStringBuilder::Add(const char* pStr) |
6815 | { |
6816 | const size_t strLen = strlen(s: pStr); |
6817 | if(strLen > 0) |
6818 | { |
6819 | const size_t oldCount = m_Data.size(); |
6820 | m_Data.resize(newCount: oldCount + strLen); |
6821 | memcpy(dest: m_Data.data() + oldCount, src: pStr, n: strLen); |
6822 | } |
6823 | } |
6824 | |
6825 | void VmaStringBuilder::AddNumber(uint32_t num) |
6826 | { |
6827 | char buf[11]; |
6828 | VmaUint32ToStr(outStr: buf, strLen: sizeof(buf), num); |
6829 | Add(pStr: buf); |
6830 | } |
6831 | |
6832 | void VmaStringBuilder::AddNumber(uint64_t num) |
6833 | { |
6834 | char buf[21]; |
6835 | VmaUint64ToStr(outStr: buf, strLen: sizeof(buf), num); |
6836 | Add(pStr: buf); |
6837 | } |
6838 | |
6839 | void VmaStringBuilder::AddPointer(const void* ptr) |
6840 | { |
6841 | char buf[21]; |
6842 | VmaPtrToStr(outStr: buf, strLen: sizeof(buf), ptr); |
6843 | Add(pStr: buf); |
6844 | } |
6845 | |
6846 | #endif // #if VMA_STATS_STRING_ENABLED |
6847 | |
6848 | //////////////////////////////////////////////////////////////////////////////// |
6849 | // VmaJsonWriter |
6850 | |
6851 | #if VMA_STATS_STRING_ENABLED |
6852 | |
6853 | class VmaJsonWriter |
6854 | { |
6855 | VMA_CLASS_NO_COPY(VmaJsonWriter) |
6856 | public: |
6857 | VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb); |
6858 | ~VmaJsonWriter(); |
6859 | |
6860 | void BeginObject(bool singleLine = false); |
6861 | void EndObject(); |
6862 | |
6863 | void BeginArray(bool singleLine = false); |
6864 | void EndArray(); |
6865 | |
6866 | void WriteString(const char* pStr); |
6867 | void BeginString(const char* pStr = VMA_NULL); |
6868 | void ContinueString(const char* pStr); |
6869 | void ContinueString(uint32_t n); |
6870 | void ContinueString(uint64_t n); |
6871 | void ContinueString_Pointer(const void* ptr); |
6872 | void EndString(const char* pStr = VMA_NULL); |
6873 | |
6874 | void WriteNumber(uint32_t n); |
6875 | void WriteNumber(uint64_t n); |
6876 | void WriteBool(bool b); |
6877 | void WriteNull(); |
6878 | |
6879 | private: |
6880 | static const char* const INDENT; |
6881 | |
6882 | enum COLLECTION_TYPE |
6883 | { |
6884 | COLLECTION_TYPE_OBJECT, |
6885 | COLLECTION_TYPE_ARRAY, |
6886 | }; |
6887 | struct StackItem |
6888 | { |
6889 | COLLECTION_TYPE type; |
6890 | uint32_t valueCount; |
6891 | bool singleLineMode; |
6892 | }; |
6893 | |
6894 | VmaStringBuilder& m_SB; |
6895 | VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack; |
6896 | bool m_InsideString; |
6897 | |
6898 | void BeginValue(bool isString); |
6899 | void WriteIndent(bool oneLess = false); |
6900 | }; |
6901 | |
6902 | const char* const VmaJsonWriter::INDENT = " " ; |
6903 | |
6904 | VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) : |
6905 | m_SB(sb), |
6906 | m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)), |
6907 | m_InsideString(false) |
6908 | { |
6909 | } |
6910 | |
6911 | VmaJsonWriter::~VmaJsonWriter() |
6912 | { |
6913 | VMA_ASSERT(!m_InsideString); |
6914 | VMA_ASSERT(m_Stack.empty()); |
6915 | } |
6916 | |
6917 | void VmaJsonWriter::BeginObject(bool singleLine) |
6918 | { |
6919 | VMA_ASSERT(!m_InsideString); |
6920 | |
6921 | BeginValue(isString: false); |
6922 | m_SB.Add(ch: '{'); |
6923 | |
6924 | StackItem item; |
6925 | item.type = COLLECTION_TYPE_OBJECT; |
6926 | item.valueCount = 0; |
6927 | item.singleLineMode = singleLine; |
6928 | m_Stack.push_back(src: item); |
6929 | } |
6930 | |
6931 | void VmaJsonWriter::EndObject() |
6932 | { |
6933 | VMA_ASSERT(!m_InsideString); |
6934 | |
6935 | WriteIndent(oneLess: true); |
6936 | m_SB.Add(ch: '}'); |
6937 | |
6938 | VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT); |
6939 | m_Stack.pop_back(); |
6940 | } |
6941 | |
6942 | void VmaJsonWriter::BeginArray(bool singleLine) |
6943 | { |
6944 | VMA_ASSERT(!m_InsideString); |
6945 | |
6946 | BeginValue(isString: false); |
6947 | m_SB.Add(ch: '['); |
6948 | |
6949 | StackItem item; |
6950 | item.type = COLLECTION_TYPE_ARRAY; |
6951 | item.valueCount = 0; |
6952 | item.singleLineMode = singleLine; |
6953 | m_Stack.push_back(src: item); |
6954 | } |
6955 | |
6956 | void VmaJsonWriter::EndArray() |
6957 | { |
6958 | VMA_ASSERT(!m_InsideString); |
6959 | |
6960 | WriteIndent(oneLess: true); |
6961 | m_SB.Add(ch: ']'); |
6962 | |
6963 | VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY); |
6964 | m_Stack.pop_back(); |
6965 | } |
6966 | |
6967 | void VmaJsonWriter::WriteString(const char* pStr) |
6968 | { |
6969 | BeginString(pStr); |
6970 | EndString(); |
6971 | } |
6972 | |
6973 | void VmaJsonWriter::BeginString(const char* pStr) |
6974 | { |
6975 | VMA_ASSERT(!m_InsideString); |
6976 | |
6977 | BeginValue(isString: true); |
6978 | m_SB.Add(ch: '"'); |
6979 | m_InsideString = true; |
6980 | if(pStr != VMA_NULL && pStr[0] != '\0') |
6981 | { |
6982 | ContinueString(pStr); |
6983 | } |
6984 | } |
6985 | |
6986 | void VmaJsonWriter::ContinueString(const char* pStr) |
6987 | { |
6988 | VMA_ASSERT(m_InsideString); |
6989 | |
6990 | const size_t strLen = strlen(s: pStr); |
6991 | for(size_t i = 0; i < strLen; ++i) |
6992 | { |
6993 | char ch = pStr[i]; |
6994 | if(ch == '\\') |
6995 | { |
6996 | m_SB.Add(pStr: "\\\\" ); |
6997 | } |
6998 | else if(ch == '"') |
6999 | { |
7000 | m_SB.Add(pStr: "\\\"" ); |
7001 | } |
7002 | else if(ch >= 32) |
7003 | { |
7004 | m_SB.Add(ch); |
7005 | } |
7006 | else switch(ch) |
7007 | { |
7008 | case '\b': |
7009 | m_SB.Add(pStr: "\\b" ); |
7010 | break; |
7011 | case '\f': |
7012 | m_SB.Add(pStr: "\\f" ); |
7013 | break; |
7014 | case '\n': |
7015 | m_SB.Add(pStr: "\\n" ); |
7016 | break; |
7017 | case '\r': |
7018 | m_SB.Add(pStr: "\\r" ); |
7019 | break; |
7020 | case '\t': |
7021 | m_SB.Add(pStr: "\\t" ); |
7022 | break; |
7023 | default: |
7024 | VMA_ASSERT(0 && "Character not currently supported." ); |
7025 | break; |
7026 | } |
7027 | } |
7028 | } |
7029 | |
7030 | void VmaJsonWriter::ContinueString(uint32_t n) |
7031 | { |
7032 | VMA_ASSERT(m_InsideString); |
7033 | m_SB.AddNumber(num: n); |
7034 | } |
7035 | |
7036 | void VmaJsonWriter::ContinueString(uint64_t n) |
7037 | { |
7038 | VMA_ASSERT(m_InsideString); |
7039 | m_SB.AddNumber(num: n); |
7040 | } |
7041 | |
7042 | void VmaJsonWriter::ContinueString_Pointer(const void* ptr) |
7043 | { |
7044 | VMA_ASSERT(m_InsideString); |
7045 | m_SB.AddPointer(ptr); |
7046 | } |
7047 | |
7048 | void VmaJsonWriter::EndString(const char* pStr) |
7049 | { |
7050 | VMA_ASSERT(m_InsideString); |
7051 | if(pStr != VMA_NULL && pStr[0] != '\0') |
7052 | { |
7053 | ContinueString(pStr); |
7054 | } |
7055 | m_SB.Add(ch: '"'); |
7056 | m_InsideString = false; |
7057 | } |
7058 | |
7059 | void VmaJsonWriter::WriteNumber(uint32_t n) |
7060 | { |
7061 | VMA_ASSERT(!m_InsideString); |
7062 | BeginValue(isString: false); |
7063 | m_SB.AddNumber(num: n); |
7064 | } |
7065 | |
7066 | void VmaJsonWriter::WriteNumber(uint64_t n) |
7067 | { |
7068 | VMA_ASSERT(!m_InsideString); |
7069 | BeginValue(isString: false); |
7070 | m_SB.AddNumber(num: n); |
7071 | } |
7072 | |
7073 | void VmaJsonWriter::WriteBool(bool b) |
7074 | { |
7075 | VMA_ASSERT(!m_InsideString); |
7076 | BeginValue(isString: false); |
7077 | m_SB.Add(pStr: b ? "true" : "false" ); |
7078 | } |
7079 | |
7080 | void VmaJsonWriter::WriteNull() |
7081 | { |
7082 | VMA_ASSERT(!m_InsideString); |
7083 | BeginValue(isString: false); |
7084 | m_SB.Add(pStr: "null" ); |
7085 | } |
7086 | |
7087 | void VmaJsonWriter::BeginValue(bool isString) |
7088 | { |
7089 | if(!m_Stack.empty()) |
7090 | { |
7091 | StackItem& currItem = m_Stack.back(); |
7092 | if(currItem.type == COLLECTION_TYPE_OBJECT && |
7093 | currItem.valueCount % 2 == 0) |
7094 | { |
7095 | (void) isString; |
7096 | VMA_ASSERT(isString); |
7097 | } |
7098 | |
7099 | if(currItem.type == COLLECTION_TYPE_OBJECT && |
7100 | currItem.valueCount % 2 != 0) |
7101 | { |
7102 | m_SB.Add(pStr: ": " ); |
7103 | } |
7104 | else if(currItem.valueCount > 0) |
7105 | { |
7106 | m_SB.Add(pStr: ", " ); |
7107 | WriteIndent(); |
7108 | } |
7109 | else |
7110 | { |
7111 | WriteIndent(); |
7112 | } |
7113 | ++currItem.valueCount; |
7114 | } |
7115 | } |
7116 | |
7117 | void VmaJsonWriter::WriteIndent(bool oneLess) |
7118 | { |
7119 | if(!m_Stack.empty() && !m_Stack.back().singleLineMode) |
7120 | { |
7121 | m_SB.AddNewLine(); |
7122 | |
7123 | size_t count = m_Stack.size(); |
7124 | if(count > 0 && oneLess) |
7125 | { |
7126 | --count; |
7127 | } |
7128 | for(size_t i = 0; i < count; ++i) |
7129 | { |
7130 | m_SB.Add(pStr: INDENT); |
7131 | } |
7132 | } |
7133 | } |
7134 | |
7135 | #endif // #if VMA_STATS_STRING_ENABLED |
7136 | |
7137 | //////////////////////////////////////////////////////////////////////////////// |
7138 | |
7139 | void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData) |
7140 | { |
7141 | if(IsUserDataString()) |
7142 | { |
7143 | VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData); |
7144 | |
7145 | FreeUserDataString(hAllocator); |
7146 | |
7147 | if(pUserData != VMA_NULL) |
7148 | { |
7149 | const char* const newStrSrc = (char*)pUserData; |
7150 | const size_t newStrLen = strlen(s: newStrSrc); |
7151 | char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1); |
7152 | memcpy(dest: newStrDst, src: newStrSrc, n: newStrLen + 1); |
7153 | m_pUserData = newStrDst; |
7154 | } |
7155 | } |
7156 | else |
7157 | { |
7158 | m_pUserData = pUserData; |
7159 | } |
7160 | } |
7161 | |
7162 | void VmaAllocation_T::ChangeBlockAllocation( |
7163 | VmaAllocator hAllocator, |
7164 | VmaDeviceMemoryBlock* block, |
7165 | VkDeviceSize offset) |
7166 | { |
7167 | VMA_ASSERT(block != VMA_NULL); |
7168 | VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); |
7169 | |
7170 | // Move mapping reference counter from old block to new block. |
7171 | if(block != m_BlockAllocation.m_Block) |
7172 | { |
7173 | uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP; |
7174 | if(IsPersistentMap()) |
7175 | ++mapRefCount; |
7176 | m_BlockAllocation.m_Block->Unmap(hAllocator, count: mapRefCount); |
7177 | block->Map(hAllocator, count: mapRefCount, VMA_NULL); |
7178 | } |
7179 | |
7180 | m_BlockAllocation.m_Block = block; |
7181 | m_BlockAllocation.m_Offset = offset; |
7182 | } |
7183 | |
7184 | void VmaAllocation_T::ChangeSize(VkDeviceSize newSize) |
7185 | { |
7186 | VMA_ASSERT(newSize > 0); |
7187 | m_Size = newSize; |
7188 | } |
7189 | |
7190 | void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset) |
7191 | { |
7192 | VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); |
7193 | m_BlockAllocation.m_Offset = newOffset; |
7194 | } |
7195 | |
7196 | VkDeviceSize VmaAllocation_T::GetOffset() const |
7197 | { |
7198 | switch(m_Type) |
7199 | { |
7200 | case ALLOCATION_TYPE_BLOCK: |
7201 | return m_BlockAllocation.m_Offset; |
7202 | case ALLOCATION_TYPE_DEDICATED: |
7203 | return 0; |
7204 | default: |
7205 | VMA_ASSERT(0); |
7206 | return 0; |
7207 | } |
7208 | } |
7209 | |
7210 | VkDeviceMemory VmaAllocation_T::GetMemory() const |
7211 | { |
7212 | switch(m_Type) |
7213 | { |
7214 | case ALLOCATION_TYPE_BLOCK: |
7215 | return m_BlockAllocation.m_Block->GetDeviceMemory(); |
7216 | case ALLOCATION_TYPE_DEDICATED: |
7217 | return m_DedicatedAllocation.m_hMemory; |
7218 | default: |
7219 | VMA_ASSERT(0); |
7220 | return VK_NULL_HANDLE; |
7221 | } |
7222 | } |
7223 | |
7224 | uint32_t VmaAllocation_T::GetMemoryTypeIndex() const |
7225 | { |
7226 | switch(m_Type) |
7227 | { |
7228 | case ALLOCATION_TYPE_BLOCK: |
7229 | return m_BlockAllocation.m_Block->GetMemoryTypeIndex(); |
7230 | case ALLOCATION_TYPE_DEDICATED: |
7231 | return m_DedicatedAllocation.m_MemoryTypeIndex; |
7232 | default: |
7233 | VMA_ASSERT(0); |
7234 | return UINT32_MAX; |
7235 | } |
7236 | } |
7237 | |
7238 | void* VmaAllocation_T::GetMappedData() const |
7239 | { |
7240 | switch(m_Type) |
7241 | { |
7242 | case ALLOCATION_TYPE_BLOCK: |
7243 | if(m_MapCount != 0) |
7244 | { |
7245 | void* pBlockData = m_BlockAllocation.m_Block->GetMappedData(); |
7246 | VMA_ASSERT(pBlockData != VMA_NULL); |
7247 | return (char*)pBlockData + m_BlockAllocation.m_Offset; |
7248 | } |
7249 | else |
7250 | { |
7251 | return VMA_NULL; |
7252 | } |
7253 | break; |
7254 | case ALLOCATION_TYPE_DEDICATED: |
7255 | VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0)); |
7256 | return m_DedicatedAllocation.m_pMappedData; |
7257 | default: |
7258 | VMA_ASSERT(0); |
7259 | return VMA_NULL; |
7260 | } |
7261 | } |
7262 | |
7263 | bool VmaAllocation_T::CanBecomeLost() const |
7264 | { |
7265 | switch(m_Type) |
7266 | { |
7267 | case ALLOCATION_TYPE_BLOCK: |
7268 | return m_BlockAllocation.m_CanBecomeLost; |
7269 | case ALLOCATION_TYPE_DEDICATED: |
7270 | return false; |
7271 | default: |
7272 | VMA_ASSERT(0); |
7273 | return false; |
7274 | } |
7275 | } |
7276 | |
7277 | VmaPool VmaAllocation_T::GetPool() const |
7278 | { |
7279 | VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); |
7280 | return m_BlockAllocation.m_hPool; |
7281 | } |
7282 | |
7283 | bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) |
7284 | { |
7285 | VMA_ASSERT(CanBecomeLost()); |
7286 | |
7287 | /* |
7288 | Warning: This is a carefully designed algorithm. |
7289 | Do not modify unless you really know what you're doing :) |
7290 | */ |
7291 | uint32_t localLastUseFrameIndex = GetLastUseFrameIndex(); |
7292 | for(;;) |
7293 | { |
7294 | if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST) |
7295 | { |
7296 | VMA_ASSERT(0); |
7297 | return false; |
7298 | } |
7299 | else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex) |
7300 | { |
7301 | return false; |
7302 | } |
7303 | else // Last use time earlier than current time. |
7304 | { |
7305 | if(CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: VMA_FRAME_INDEX_LOST)) |
7306 | { |
7307 | // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST. |
7308 | // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock. |
7309 | return true; |
7310 | } |
7311 | } |
7312 | } |
7313 | } |
7314 | |
7315 | #if VMA_STATS_STRING_ENABLED |
7316 | |
7317 | // Correspond to values of enum VmaSuballocationType. |
7318 | static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = { |
7319 | "FREE" , |
7320 | "UNKNOWN" , |
7321 | "BUFFER" , |
7322 | "IMAGE_UNKNOWN" , |
7323 | "IMAGE_LINEAR" , |
7324 | "IMAGE_OPTIMAL" , |
7325 | }; |
7326 | |
7327 | void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const |
7328 | { |
7329 | json.WriteString(pStr: "Type" ); |
7330 | json.WriteString(pStr: VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]); |
7331 | |
7332 | json.WriteString(pStr: "Size" ); |
7333 | json.WriteNumber(n: m_Size); |
7334 | |
7335 | if(m_pUserData != VMA_NULL) |
7336 | { |
7337 | json.WriteString(pStr: "UserData" ); |
7338 | if(IsUserDataString()) |
7339 | { |
7340 | json.WriteString(pStr: (const char*)m_pUserData); |
7341 | } |
7342 | else |
7343 | { |
7344 | json.BeginString(); |
7345 | json.ContinueString_Pointer(ptr: m_pUserData); |
7346 | json.EndString(); |
7347 | } |
7348 | } |
7349 | |
7350 | json.WriteString(pStr: "CreationFrameIndex" ); |
7351 | json.WriteNumber(n: m_CreationFrameIndex); |
7352 | |
7353 | json.WriteString(pStr: "LastUseFrameIndex" ); |
7354 | json.WriteNumber(n: GetLastUseFrameIndex()); |
7355 | |
7356 | if(m_BufferImageUsage != 0) |
7357 | { |
7358 | json.WriteString(pStr: "Usage" ); |
7359 | json.WriteNumber(n: m_BufferImageUsage); |
7360 | } |
7361 | } |
7362 | |
7363 | #endif |
7364 | |
7365 | void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator) |
7366 | { |
7367 | VMA_ASSERT(IsUserDataString()); |
7368 | if(m_pUserData != VMA_NULL) |
7369 | { |
7370 | char* const oldStr = (char*)m_pUserData; |
7371 | const size_t oldStrLen = strlen(s: oldStr); |
7372 | vma_delete_array(hAllocator, ptr: oldStr, count: oldStrLen + 1); |
7373 | m_pUserData = VMA_NULL; |
7374 | } |
7375 | } |
7376 | |
7377 | void VmaAllocation_T::BlockAllocMap() |
7378 | { |
7379 | VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); |
7380 | |
7381 | if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F) |
7382 | { |
7383 | ++m_MapCount; |
7384 | } |
7385 | else |
7386 | { |
7387 | VMA_ASSERT(0 && "Allocation mapped too many times simultaneously." ); |
7388 | } |
7389 | } |
7390 | |
7391 | void VmaAllocation_T::BlockAllocUnmap() |
7392 | { |
7393 | VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); |
7394 | |
7395 | if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0) |
7396 | { |
7397 | --m_MapCount; |
7398 | } |
7399 | else |
7400 | { |
7401 | VMA_ASSERT(0 && "Unmapping allocation not previously mapped." ); |
7402 | } |
7403 | } |
7404 | |
7405 | VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData) |
7406 | { |
7407 | VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); |
7408 | |
7409 | if(m_MapCount != 0) |
7410 | { |
7411 | if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F) |
7412 | { |
7413 | VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL); |
7414 | *ppData = m_DedicatedAllocation.m_pMappedData; |
7415 | ++m_MapCount; |
7416 | return VK_SUCCESS; |
7417 | } |
7418 | else |
7419 | { |
7420 | VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously." ); |
7421 | return VK_ERROR_MEMORY_MAP_FAILED; |
7422 | } |
7423 | } |
7424 | else |
7425 | { |
7426 | VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( |
7427 | hAllocator->m_hDevice, |
7428 | m_DedicatedAllocation.m_hMemory, |
7429 | 0, // offset |
7430 | VK_WHOLE_SIZE, |
7431 | 0, // flags |
7432 | ppData); |
7433 | if(result == VK_SUCCESS) |
7434 | { |
7435 | m_DedicatedAllocation.m_pMappedData = *ppData; |
7436 | m_MapCount = 1; |
7437 | } |
7438 | return result; |
7439 | } |
7440 | } |
7441 | |
7442 | void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator) |
7443 | { |
7444 | VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); |
7445 | |
7446 | if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0) |
7447 | { |
7448 | --m_MapCount; |
7449 | if(m_MapCount == 0) |
7450 | { |
7451 | m_DedicatedAllocation.m_pMappedData = VMA_NULL; |
7452 | (*hAllocator->GetVulkanFunctions().vkUnmapMemory)( |
7453 | hAllocator->m_hDevice, |
7454 | m_DedicatedAllocation.m_hMemory); |
7455 | } |
7456 | } |
7457 | else |
7458 | { |
7459 | VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped." ); |
7460 | } |
7461 | } |
7462 | |
7463 | #if VMA_STATS_STRING_ENABLED |
7464 | |
7465 | static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat) |
7466 | { |
7467 | json.BeginObject(); |
7468 | |
7469 | json.WriteString(pStr: "Blocks" ); |
7470 | json.WriteNumber(n: stat.blockCount); |
7471 | |
7472 | json.WriteString(pStr: "Allocations" ); |
7473 | json.WriteNumber(n: stat.allocationCount); |
7474 | |
7475 | json.WriteString(pStr: "UnusedRanges" ); |
7476 | json.WriteNumber(n: stat.unusedRangeCount); |
7477 | |
7478 | json.WriteString(pStr: "UsedBytes" ); |
7479 | json.WriteNumber(n: stat.usedBytes); |
7480 | |
7481 | json.WriteString(pStr: "UnusedBytes" ); |
7482 | json.WriteNumber(n: stat.unusedBytes); |
7483 | |
7484 | if(stat.allocationCount > 1) |
7485 | { |
7486 | json.WriteString(pStr: "AllocationSize" ); |
7487 | json.BeginObject(singleLine: true); |
7488 | json.WriteString(pStr: "Min" ); |
7489 | json.WriteNumber(n: stat.allocationSizeMin); |
7490 | json.WriteString(pStr: "Avg" ); |
7491 | json.WriteNumber(n: stat.allocationSizeAvg); |
7492 | json.WriteString(pStr: "Max" ); |
7493 | json.WriteNumber(n: stat.allocationSizeMax); |
7494 | json.EndObject(); |
7495 | } |
7496 | |
7497 | if(stat.unusedRangeCount > 1) |
7498 | { |
7499 | json.WriteString(pStr: "UnusedRangeSize" ); |
7500 | json.BeginObject(singleLine: true); |
7501 | json.WriteString(pStr: "Min" ); |
7502 | json.WriteNumber(n: stat.unusedRangeSizeMin); |
7503 | json.WriteString(pStr: "Avg" ); |
7504 | json.WriteNumber(n: stat.unusedRangeSizeAvg); |
7505 | json.WriteString(pStr: "Max" ); |
7506 | json.WriteNumber(n: stat.unusedRangeSizeMax); |
7507 | json.EndObject(); |
7508 | } |
7509 | |
7510 | json.EndObject(); |
7511 | } |
7512 | |
7513 | #endif // #if VMA_STATS_STRING_ENABLED |
7514 | |
7515 | struct VmaSuballocationItemSizeLess |
7516 | { |
7517 | bool operator()( |
7518 | const VmaSuballocationList::iterator lhs, |
7519 | const VmaSuballocationList::iterator rhs) const |
7520 | { |
7521 | return lhs->size < rhs->size; |
7522 | } |
7523 | bool operator()( |
7524 | const VmaSuballocationList::iterator lhs, |
7525 | VkDeviceSize rhsSize) const |
7526 | { |
7527 | return lhs->size < rhsSize; |
7528 | } |
7529 | }; |
7530 | |
7531 | |
7532 | //////////////////////////////////////////////////////////////////////////////// |
7533 | // class VmaBlockMetadata |
7534 | |
7535 | VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) : |
7536 | m_Size(0), |
7537 | m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks()) |
7538 | { |
7539 | } |
7540 | |
7541 | #if VMA_STATS_STRING_ENABLED |
7542 | |
7543 | void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json, |
7544 | VkDeviceSize unusedBytes, |
7545 | size_t allocationCount, |
7546 | size_t unusedRangeCount) const |
7547 | { |
7548 | json.BeginObject(); |
7549 | |
7550 | json.WriteString(pStr: "TotalBytes" ); |
7551 | json.WriteNumber(n: GetSize()); |
7552 | |
7553 | json.WriteString(pStr: "UnusedBytes" ); |
7554 | json.WriteNumber(n: unusedBytes); |
7555 | |
7556 | json.WriteString(pStr: "Allocations" ); |
7557 | json.WriteNumber(n: (uint64_t)allocationCount); |
7558 | |
7559 | json.WriteString(pStr: "UnusedRanges" ); |
7560 | json.WriteNumber(n: (uint64_t)unusedRangeCount); |
7561 | |
7562 | json.WriteString(pStr: "Suballocations" ); |
7563 | json.BeginArray(); |
7564 | } |
7565 | |
7566 | void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json, |
7567 | VkDeviceSize offset, |
7568 | VmaAllocation hAllocation) const |
7569 | { |
7570 | json.BeginObject(singleLine: true); |
7571 | |
7572 | json.WriteString(pStr: "Offset" ); |
7573 | json.WriteNumber(n: offset); |
7574 | |
7575 | hAllocation->PrintParameters(json); |
7576 | |
7577 | json.EndObject(); |
7578 | } |
7579 | |
7580 | void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, |
7581 | VkDeviceSize offset, |
7582 | VkDeviceSize size) const |
7583 | { |
7584 | json.BeginObject(singleLine: true); |
7585 | |
7586 | json.WriteString(pStr: "Offset" ); |
7587 | json.WriteNumber(n: offset); |
7588 | |
7589 | json.WriteString(pStr: "Type" ); |
7590 | json.WriteString(pStr: VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]); |
7591 | |
7592 | json.WriteString(pStr: "Size" ); |
7593 | json.WriteNumber(n: size); |
7594 | |
7595 | json.EndObject(); |
7596 | } |
7597 | |
7598 | void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const |
7599 | { |
7600 | json.EndArray(); |
7601 | json.EndObject(); |
7602 | } |
7603 | |
7604 | #endif // #if VMA_STATS_STRING_ENABLED |
7605 | |
7606 | //////////////////////////////////////////////////////////////////////////////// |
7607 | // class VmaBlockMetadata_Generic |
7608 | |
7609 | VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) : |
7610 | VmaBlockMetadata(hAllocator), |
7611 | m_FreeCount(0), |
7612 | m_SumFreeSize(0), |
7613 | m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())), |
7614 | m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks())) |
7615 | { |
7616 | } |
7617 | |
7618 | VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic() |
7619 | { |
7620 | } |
7621 | |
7622 | void VmaBlockMetadata_Generic::Init(VkDeviceSize size) |
7623 | { |
7624 | VmaBlockMetadata::Init(size); |
7625 | |
7626 | m_FreeCount = 1; |
7627 | m_SumFreeSize = size; |
7628 | |
7629 | VmaSuballocation suballoc = {}; |
7630 | suballoc.offset = 0; |
7631 | suballoc.size = size; |
7632 | suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
7633 | suballoc.hAllocation = VK_NULL_HANDLE; |
7634 | |
7635 | VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER); |
7636 | m_Suballocations.push_back(value: suballoc); |
7637 | VmaSuballocationList::iterator suballocItem = m_Suballocations.end(); |
7638 | --suballocItem; |
7639 | m_FreeSuballocationsBySize.push_back(src: suballocItem); |
7640 | } |
7641 | |
7642 | bool VmaBlockMetadata_Generic::Validate() const |
7643 | { |
7644 | VMA_VALIDATE(!m_Suballocations.empty()); |
7645 | |
7646 | // Expected offset of new suballocation as calculated from previous ones. |
7647 | VkDeviceSize calculatedOffset = 0; |
7648 | // Expected number of free suballocations as calculated from traversing their list. |
7649 | uint32_t calculatedFreeCount = 0; |
7650 | // Expected sum size of free suballocations as calculated from traversing their list. |
7651 | VkDeviceSize calculatedSumFreeSize = 0; |
7652 | // Expected number of free suballocations that should be registered in |
7653 | // m_FreeSuballocationsBySize calculated from traversing their list. |
7654 | size_t freeSuballocationsToRegister = 0; |
7655 | // True if previous visited suballocation was free. |
7656 | bool prevFree = false; |
7657 | |
7658 | for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); |
7659 | suballocItem != m_Suballocations.cend(); |
7660 | ++suballocItem) |
7661 | { |
7662 | const VmaSuballocation& subAlloc = *suballocItem; |
7663 | |
7664 | // Actual offset of this suballocation doesn't match expected one. |
7665 | VMA_VALIDATE(subAlloc.offset == calculatedOffset); |
7666 | |
7667 | const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE); |
7668 | // Two adjacent free suballocations are invalid. They should be merged. |
7669 | VMA_VALIDATE(!prevFree || !currFree); |
7670 | |
7671 | VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE)); |
7672 | |
7673 | if(currFree) |
7674 | { |
7675 | calculatedSumFreeSize += subAlloc.size; |
7676 | ++calculatedFreeCount; |
7677 | if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) |
7678 | { |
7679 | ++freeSuballocationsToRegister; |
7680 | } |
7681 | |
7682 | // Margin required between allocations - every free space must be at least that large. |
7683 | #if VMA_DEBUG_MARGIN |
7684 | VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN); |
7685 | #endif |
7686 | } |
7687 | else |
7688 | { |
7689 | VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset); |
7690 | VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size); |
7691 | |
7692 | // Margin required between allocations - previous allocation must be free. |
7693 | VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree); |
7694 | } |
7695 | |
7696 | calculatedOffset += subAlloc.size; |
7697 | prevFree = currFree; |
7698 | } |
7699 | |
7700 | // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't |
7701 | // match expected one. |
7702 | VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister); |
7703 | |
7704 | VkDeviceSize lastSize = 0; |
7705 | for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i) |
7706 | { |
7707 | VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i]; |
7708 | |
7709 | // Only free suballocations can be registered in m_FreeSuballocationsBySize. |
7710 | VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE); |
7711 | // They must be sorted by size ascending. |
7712 | VMA_VALIDATE(suballocItem->size >= lastSize); |
7713 | |
7714 | lastSize = suballocItem->size; |
7715 | } |
7716 | |
7717 | // Check if totals match calculacted values. |
7718 | VMA_VALIDATE(ValidateFreeSuballocationList()); |
7719 | VMA_VALIDATE(calculatedOffset == GetSize()); |
7720 | VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize); |
7721 | VMA_VALIDATE(calculatedFreeCount == m_FreeCount); |
7722 | |
7723 | return true; |
7724 | } |
7725 | |
7726 | VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const |
7727 | { |
7728 | if(!m_FreeSuballocationsBySize.empty()) |
7729 | { |
7730 | return m_FreeSuballocationsBySize.back()->size; |
7731 | } |
7732 | else |
7733 | { |
7734 | return 0; |
7735 | } |
7736 | } |
7737 | |
7738 | bool VmaBlockMetadata_Generic::IsEmpty() const |
7739 | { |
7740 | return (m_Suballocations.size() == 1) && (m_FreeCount == 1); |
7741 | } |
7742 | |
7743 | void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const |
7744 | { |
7745 | outInfo.blockCount = 1; |
7746 | |
7747 | const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); |
7748 | outInfo.allocationCount = rangeCount - m_FreeCount; |
7749 | outInfo.unusedRangeCount = m_FreeCount; |
7750 | |
7751 | outInfo.unusedBytes = m_SumFreeSize; |
7752 | outInfo.usedBytes = GetSize() - outInfo.unusedBytes; |
7753 | |
7754 | outInfo.allocationSizeMin = UINT64_MAX; |
7755 | outInfo.allocationSizeMax = 0; |
7756 | outInfo.unusedRangeSizeMin = UINT64_MAX; |
7757 | outInfo.unusedRangeSizeMax = 0; |
7758 | |
7759 | for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); |
7760 | suballocItem != m_Suballocations.cend(); |
7761 | ++suballocItem) |
7762 | { |
7763 | const VmaSuballocation& suballoc = *suballocItem; |
7764 | if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) |
7765 | { |
7766 | outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); |
7767 | outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size); |
7768 | } |
7769 | else |
7770 | { |
7771 | outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size); |
7772 | outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size); |
7773 | } |
7774 | } |
7775 | } |
7776 | |
7777 | void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const |
7778 | { |
7779 | const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); |
7780 | |
7781 | inoutStats.size += GetSize(); |
7782 | inoutStats.unusedSize += m_SumFreeSize; |
7783 | inoutStats.allocationCount += rangeCount - m_FreeCount; |
7784 | inoutStats.unusedRangeCount += m_FreeCount; |
7785 | inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax()); |
7786 | } |
7787 | |
7788 | #if VMA_STATS_STRING_ENABLED |
7789 | |
7790 | void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const |
7791 | { |
7792 | PrintDetailedMap_Begin(json, |
7793 | unusedBytes: m_SumFreeSize, // unusedBytes |
7794 | allocationCount: m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount |
7795 | unusedRangeCount: m_FreeCount); // unusedRangeCount |
7796 | |
7797 | size_t i = 0; |
7798 | for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); |
7799 | suballocItem != m_Suballocations.cend(); |
7800 | ++suballocItem, ++i) |
7801 | { |
7802 | if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE) |
7803 | { |
7804 | PrintDetailedMap_UnusedRange(json, offset: suballocItem->offset, size: suballocItem->size); |
7805 | } |
7806 | else |
7807 | { |
7808 | PrintDetailedMap_Allocation(json, offset: suballocItem->offset, hAllocation: suballocItem->hAllocation); |
7809 | } |
7810 | } |
7811 | |
7812 | PrintDetailedMap_End(json); |
7813 | } |
7814 | |
7815 | #endif // #if VMA_STATS_STRING_ENABLED |
7816 | |
7817 | bool VmaBlockMetadata_Generic::CreateAllocationRequest( |
7818 | uint32_t currentFrameIndex, |
7819 | uint32_t frameInUseCount, |
7820 | VkDeviceSize bufferImageGranularity, |
7821 | VkDeviceSize allocSize, |
7822 | VkDeviceSize allocAlignment, |
7823 | bool upperAddress, |
7824 | VmaSuballocationType allocType, |
7825 | bool canMakeOtherLost, |
7826 | uint32_t strategy, |
7827 | VmaAllocationRequest* pAllocationRequest) |
7828 | { |
7829 | VMA_ASSERT(allocSize > 0); |
7830 | VMA_ASSERT(!upperAddress); |
7831 | (void) upperAddress; |
7832 | VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); |
7833 | VMA_ASSERT(pAllocationRequest != VMA_NULL); |
7834 | VMA_HEAVY_ASSERT(Validate()); |
7835 | |
7836 | // There is not enough total free space in this block to fullfill the request: Early return. |
7837 | if(canMakeOtherLost == false && |
7838 | m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN) |
7839 | { |
7840 | return false; |
7841 | } |
7842 | |
7843 | // New algorithm, efficiently searching freeSuballocationsBySize. |
7844 | const size_t freeSuballocCount = m_FreeSuballocationsBySize.size(); |
7845 | if(freeSuballocCount > 0) |
7846 | { |
7847 | if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) |
7848 | { |
7849 | // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN. |
7850 | VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( |
7851 | beg: m_FreeSuballocationsBySize.data(), |
7852 | end: m_FreeSuballocationsBySize.data() + freeSuballocCount, |
7853 | key: allocSize + 2 * VMA_DEBUG_MARGIN, |
7854 | cmp: VmaSuballocationItemSizeLess()); |
7855 | size_t index = it - m_FreeSuballocationsBySize.data(); |
7856 | for(; index < freeSuballocCount; ++index) |
7857 | { |
7858 | if(CheckAllocation( |
7859 | currentFrameIndex, |
7860 | frameInUseCount, |
7861 | bufferImageGranularity, |
7862 | allocSize, |
7863 | allocAlignment, |
7864 | allocType, |
7865 | suballocItem: m_FreeSuballocationsBySize[index], |
7866 | canMakeOtherLost: false, // canMakeOtherLost |
7867 | pOffset: &pAllocationRequest->offset, |
7868 | itemsToMakeLostCount: &pAllocationRequest->itemsToMakeLostCount, |
7869 | pSumFreeSize: &pAllocationRequest->sumFreeSize, |
7870 | pSumItemSize: &pAllocationRequest->sumItemSize)) |
7871 | { |
7872 | pAllocationRequest->item = m_FreeSuballocationsBySize[index]; |
7873 | return true; |
7874 | } |
7875 | } |
7876 | } |
7877 | else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET) |
7878 | { |
7879 | for(VmaSuballocationList::iterator it = m_Suballocations.begin(); |
7880 | it != m_Suballocations.end(); |
7881 | ++it) |
7882 | { |
7883 | if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation( |
7884 | currentFrameIndex, |
7885 | frameInUseCount, |
7886 | bufferImageGranularity, |
7887 | allocSize, |
7888 | allocAlignment, |
7889 | allocType, |
7890 | suballocItem: it, |
7891 | canMakeOtherLost: false, // canMakeOtherLost |
7892 | pOffset: &pAllocationRequest->offset, |
7893 | itemsToMakeLostCount: &pAllocationRequest->itemsToMakeLostCount, |
7894 | pSumFreeSize: &pAllocationRequest->sumFreeSize, |
7895 | pSumItemSize: &pAllocationRequest->sumItemSize)) |
7896 | { |
7897 | pAllocationRequest->item = it; |
7898 | return true; |
7899 | } |
7900 | } |
7901 | } |
7902 | else // WORST_FIT, FIRST_FIT |
7903 | { |
7904 | // Search staring from biggest suballocations. |
7905 | for(size_t index = freeSuballocCount; index--; ) |
7906 | { |
7907 | if(CheckAllocation( |
7908 | currentFrameIndex, |
7909 | frameInUseCount, |
7910 | bufferImageGranularity, |
7911 | allocSize, |
7912 | allocAlignment, |
7913 | allocType, |
7914 | suballocItem: m_FreeSuballocationsBySize[index], |
7915 | canMakeOtherLost: false, // canMakeOtherLost |
7916 | pOffset: &pAllocationRequest->offset, |
7917 | itemsToMakeLostCount: &pAllocationRequest->itemsToMakeLostCount, |
7918 | pSumFreeSize: &pAllocationRequest->sumFreeSize, |
7919 | pSumItemSize: &pAllocationRequest->sumItemSize)) |
7920 | { |
7921 | pAllocationRequest->item = m_FreeSuballocationsBySize[index]; |
7922 | return true; |
7923 | } |
7924 | } |
7925 | } |
7926 | } |
7927 | |
7928 | if(canMakeOtherLost) |
7929 | { |
7930 | // Brute-force algorithm. TODO: Come up with something better. |
7931 | |
7932 | pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE; |
7933 | pAllocationRequest->sumItemSize = VK_WHOLE_SIZE; |
7934 | |
7935 | VmaAllocationRequest tmpAllocRequest = {}; |
7936 | for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin(); |
7937 | suballocIt != m_Suballocations.end(); |
7938 | ++suballocIt) |
7939 | { |
7940 | if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE || |
7941 | suballocIt->hAllocation->CanBecomeLost()) |
7942 | { |
7943 | if(CheckAllocation( |
7944 | currentFrameIndex, |
7945 | frameInUseCount, |
7946 | bufferImageGranularity, |
7947 | allocSize, |
7948 | allocAlignment, |
7949 | allocType, |
7950 | suballocItem: suballocIt, |
7951 | canMakeOtherLost, |
7952 | pOffset: &tmpAllocRequest.offset, |
7953 | itemsToMakeLostCount: &tmpAllocRequest.itemsToMakeLostCount, |
7954 | pSumFreeSize: &tmpAllocRequest.sumFreeSize, |
7955 | pSumItemSize: &tmpAllocRequest.sumItemSize)) |
7956 | { |
7957 | tmpAllocRequest.item = suballocIt; |
7958 | |
7959 | if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() || |
7960 | strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT) |
7961 | { |
7962 | *pAllocationRequest = tmpAllocRequest; |
7963 | } |
7964 | } |
7965 | } |
7966 | } |
7967 | |
7968 | if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE) |
7969 | { |
7970 | return true; |
7971 | } |
7972 | } |
7973 | |
7974 | return false; |
7975 | } |
7976 | |
7977 | bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost( |
7978 | uint32_t currentFrameIndex, |
7979 | uint32_t frameInUseCount, |
7980 | VmaAllocationRequest* pAllocationRequest) |
7981 | { |
7982 | while(pAllocationRequest->itemsToMakeLostCount > 0) |
7983 | { |
7984 | if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE) |
7985 | { |
7986 | ++pAllocationRequest->item; |
7987 | } |
7988 | VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end()); |
7989 | VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE); |
7990 | VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost()); |
7991 | if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) |
7992 | { |
7993 | pAllocationRequest->item = FreeSuballocation(suballocItem: pAllocationRequest->item); |
7994 | --pAllocationRequest->itemsToMakeLostCount; |
7995 | } |
7996 | else |
7997 | { |
7998 | return false; |
7999 | } |
8000 | } |
8001 | |
8002 | VMA_HEAVY_ASSERT(Validate()); |
8003 | VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end()); |
8004 | VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE); |
8005 | |
8006 | return true; |
8007 | } |
8008 | |
8009 | uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) |
8010 | { |
8011 | uint32_t lostAllocationCount = 0; |
8012 | for(VmaSuballocationList::iterator it = m_Suballocations.begin(); |
8013 | it != m_Suballocations.end(); |
8014 | ++it) |
8015 | { |
8016 | if(it->type != VMA_SUBALLOCATION_TYPE_FREE && |
8017 | it->hAllocation->CanBecomeLost() && |
8018 | it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) |
8019 | { |
8020 | it = FreeSuballocation(suballocItem: it); |
8021 | ++lostAllocationCount; |
8022 | } |
8023 | } |
8024 | return lostAllocationCount; |
8025 | } |
8026 | |
8027 | VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData) |
8028 | { |
8029 | for(VmaSuballocationList::iterator it = m_Suballocations.begin(); |
8030 | it != m_Suballocations.end(); |
8031 | ++it) |
8032 | { |
8033 | if(it->type != VMA_SUBALLOCATION_TYPE_FREE) |
8034 | { |
8035 | if(!VmaValidateMagicValue(pData: pBlockData, offset: it->offset - VMA_DEBUG_MARGIN)) |
8036 | { |
8037 | VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!" ); |
8038 | return VK_ERROR_VALIDATION_FAILED_EXT; |
8039 | } |
8040 | if(!VmaValidateMagicValue(pData: pBlockData, offset: it->offset + it->size)) |
8041 | { |
8042 | VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!" ); |
8043 | return VK_ERROR_VALIDATION_FAILED_EXT; |
8044 | } |
8045 | } |
8046 | } |
8047 | |
8048 | return VK_SUCCESS; |
8049 | } |
8050 | |
8051 | void VmaBlockMetadata_Generic::Alloc( |
8052 | const VmaAllocationRequest& request, |
8053 | VmaSuballocationType type, |
8054 | VkDeviceSize allocSize, |
8055 | bool upperAddress, |
8056 | VmaAllocation hAllocation) |
8057 | { |
8058 | VMA_ASSERT(!upperAddress); |
8059 | (void) upperAddress; |
8060 | VMA_ASSERT(request.item != m_Suballocations.end()); |
8061 | VmaSuballocation& suballoc = *request.item; |
8062 | // Given suballocation is a free block. |
8063 | VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); |
8064 | // Given offset is inside this suballocation. |
8065 | VMA_ASSERT(request.offset >= suballoc.offset); |
8066 | const VkDeviceSize paddingBegin = request.offset - suballoc.offset; |
8067 | VMA_ASSERT(suballoc.size >= paddingBegin + allocSize); |
8068 | const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize; |
8069 | |
8070 | // Unregister this free suballocation from m_FreeSuballocationsBySize and update |
8071 | // it to become used. |
8072 | UnregisterFreeSuballocation(item: request.item); |
8073 | |
8074 | suballoc.offset = request.offset; |
8075 | suballoc.size = allocSize; |
8076 | suballoc.type = type; |
8077 | suballoc.hAllocation = hAllocation; |
8078 | |
8079 | // If there are any free bytes remaining at the end, insert new free suballocation after current one. |
8080 | if(paddingEnd) |
8081 | { |
8082 | VmaSuballocation paddingSuballoc = {}; |
8083 | paddingSuballoc.offset = request.offset + allocSize; |
8084 | paddingSuballoc.size = paddingEnd; |
8085 | paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
8086 | VmaSuballocationList::iterator next = request.item; |
8087 | ++next; |
8088 | const VmaSuballocationList::iterator paddingEndItem = |
8089 | m_Suballocations.insert(it: next, value: paddingSuballoc); |
8090 | RegisterFreeSuballocation(item: paddingEndItem); |
8091 | } |
8092 | |
8093 | // If there are any free bytes remaining at the beginning, insert new free suballocation before current one. |
8094 | if(paddingBegin) |
8095 | { |
8096 | VmaSuballocation paddingSuballoc = {}; |
8097 | paddingSuballoc.offset = request.offset - paddingBegin; |
8098 | paddingSuballoc.size = paddingBegin; |
8099 | paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
8100 | const VmaSuballocationList::iterator paddingBeginItem = |
8101 | m_Suballocations.insert(it: request.item, value: paddingSuballoc); |
8102 | RegisterFreeSuballocation(item: paddingBeginItem); |
8103 | } |
8104 | |
8105 | // Update totals. |
8106 | m_FreeCount = m_FreeCount - 1; |
8107 | if(paddingBegin > 0) |
8108 | { |
8109 | ++m_FreeCount; |
8110 | } |
8111 | if(paddingEnd > 0) |
8112 | { |
8113 | ++m_FreeCount; |
8114 | } |
8115 | m_SumFreeSize -= allocSize; |
8116 | } |
8117 | |
8118 | void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation) |
8119 | { |
8120 | for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); |
8121 | suballocItem != m_Suballocations.end(); |
8122 | ++suballocItem) |
8123 | { |
8124 | VmaSuballocation& suballoc = *suballocItem; |
8125 | if(suballoc.hAllocation == allocation) |
8126 | { |
8127 | FreeSuballocation(suballocItem); |
8128 | VMA_HEAVY_ASSERT(Validate()); |
8129 | return; |
8130 | } |
8131 | } |
8132 | VMA_ASSERT(0 && "Not found!" ); |
8133 | } |
8134 | |
8135 | void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset) |
8136 | { |
8137 | for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); |
8138 | suballocItem != m_Suballocations.end(); |
8139 | ++suballocItem) |
8140 | { |
8141 | VmaSuballocation& suballoc = *suballocItem; |
8142 | if(suballoc.offset == offset) |
8143 | { |
8144 | FreeSuballocation(suballocItem); |
8145 | return; |
8146 | } |
8147 | } |
8148 | VMA_ASSERT(0 && "Not found!" ); |
8149 | } |
8150 | |
8151 | bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) |
8152 | { |
8153 | typedef VmaSuballocationList::iterator iter_type; |
8154 | for(iter_type suballocItem = m_Suballocations.begin(); |
8155 | suballocItem != m_Suballocations.end(); |
8156 | ++suballocItem) |
8157 | { |
8158 | VmaSuballocation& suballoc = *suballocItem; |
8159 | if(suballoc.hAllocation == alloc) |
8160 | { |
8161 | iter_type nextItem = suballocItem; |
8162 | ++nextItem; |
8163 | |
8164 | // Should have been ensured on higher level. |
8165 | VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0); |
8166 | |
8167 | // Shrinking. |
8168 | if(newSize < alloc->GetSize()) |
8169 | { |
8170 | const VkDeviceSize sizeDiff = suballoc.size - newSize; |
8171 | |
8172 | // There is next item. |
8173 | if(nextItem != m_Suballocations.end()) |
8174 | { |
8175 | // Next item is free. |
8176 | if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE) |
8177 | { |
8178 | // Grow this next item backward. |
8179 | UnregisterFreeSuballocation(item: nextItem); |
8180 | nextItem->offset -= sizeDiff; |
8181 | nextItem->size += sizeDiff; |
8182 | RegisterFreeSuballocation(item: nextItem); |
8183 | } |
8184 | // Next item is not free. |
8185 | else |
8186 | { |
8187 | // Create free item after current one. |
8188 | VmaSuballocation newFreeSuballoc; |
8189 | newFreeSuballoc.hAllocation = VK_NULL_HANDLE; |
8190 | newFreeSuballoc.offset = suballoc.offset + newSize; |
8191 | newFreeSuballoc.size = sizeDiff; |
8192 | newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
8193 | iter_type newFreeSuballocIt = m_Suballocations.insert(it: nextItem, value: newFreeSuballoc); |
8194 | RegisterFreeSuballocation(item: newFreeSuballocIt); |
8195 | |
8196 | ++m_FreeCount; |
8197 | } |
8198 | } |
8199 | // This is the last item. |
8200 | else |
8201 | { |
8202 | // Create free item at the end. |
8203 | VmaSuballocation newFreeSuballoc; |
8204 | newFreeSuballoc.hAllocation = VK_NULL_HANDLE; |
8205 | newFreeSuballoc.offset = suballoc.offset + newSize; |
8206 | newFreeSuballoc.size = sizeDiff; |
8207 | newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
8208 | m_Suballocations.push_back(value: newFreeSuballoc); |
8209 | |
8210 | iter_type newFreeSuballocIt = m_Suballocations.end(); |
8211 | RegisterFreeSuballocation(item: --newFreeSuballocIt); |
8212 | |
8213 | ++m_FreeCount; |
8214 | } |
8215 | |
8216 | suballoc.size = newSize; |
8217 | m_SumFreeSize += sizeDiff; |
8218 | } |
8219 | // Growing. |
8220 | else |
8221 | { |
8222 | const VkDeviceSize sizeDiff = newSize - suballoc.size; |
8223 | |
8224 | // There is next item. |
8225 | if(nextItem != m_Suballocations.end()) |
8226 | { |
8227 | // Next item is free. |
8228 | if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE) |
8229 | { |
8230 | // There is not enough free space, including margin. |
8231 | if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN) |
8232 | { |
8233 | return false; |
8234 | } |
8235 | |
8236 | // There is more free space than required. |
8237 | if(nextItem->size > sizeDiff) |
8238 | { |
8239 | // Move and shrink this next item. |
8240 | UnregisterFreeSuballocation(item: nextItem); |
8241 | nextItem->offset += sizeDiff; |
8242 | nextItem->size -= sizeDiff; |
8243 | RegisterFreeSuballocation(item: nextItem); |
8244 | } |
8245 | // There is exactly the amount of free space required. |
8246 | else |
8247 | { |
8248 | // Remove this next free item. |
8249 | UnregisterFreeSuballocation(item: nextItem); |
8250 | m_Suballocations.erase(it: nextItem); |
8251 | --m_FreeCount; |
8252 | } |
8253 | } |
8254 | // Next item is not free - there is no space to grow. |
8255 | else |
8256 | { |
8257 | return false; |
8258 | } |
8259 | } |
8260 | // This is the last item - there is no space to grow. |
8261 | else |
8262 | { |
8263 | return false; |
8264 | } |
8265 | |
8266 | suballoc.size = newSize; |
8267 | m_SumFreeSize -= sizeDiff; |
8268 | } |
8269 | |
8270 | // We cannot call Validate() here because alloc object is updated to new size outside of this call. |
8271 | return true; |
8272 | } |
8273 | } |
8274 | VMA_ASSERT(0 && "Not found!" ); |
8275 | return false; |
8276 | } |
8277 | |
8278 | bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const |
8279 | { |
8280 | VkDeviceSize lastSize = 0; |
8281 | for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i) |
8282 | { |
8283 | const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i]; |
8284 | |
8285 | VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE); |
8286 | VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER); |
8287 | VMA_VALIDATE(it->size >= lastSize); |
8288 | lastSize = it->size; |
8289 | } |
8290 | return true; |
8291 | } |
8292 | |
8293 | bool VmaBlockMetadata_Generic::CheckAllocation( |
8294 | uint32_t currentFrameIndex, |
8295 | uint32_t frameInUseCount, |
8296 | VkDeviceSize bufferImageGranularity, |
8297 | VkDeviceSize allocSize, |
8298 | VkDeviceSize allocAlignment, |
8299 | VmaSuballocationType allocType, |
8300 | VmaSuballocationList::const_iterator suballocItem, |
8301 | bool canMakeOtherLost, |
8302 | VkDeviceSize* pOffset, |
8303 | size_t* itemsToMakeLostCount, |
8304 | VkDeviceSize* pSumFreeSize, |
8305 | VkDeviceSize* pSumItemSize) const |
8306 | { |
8307 | VMA_ASSERT(allocSize > 0); |
8308 | VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); |
8309 | VMA_ASSERT(suballocItem != m_Suballocations.cend()); |
8310 | VMA_ASSERT(pOffset != VMA_NULL); |
8311 | |
8312 | *itemsToMakeLostCount = 0; |
8313 | *pSumFreeSize = 0; |
8314 | *pSumItemSize = 0; |
8315 | |
8316 | if(canMakeOtherLost) |
8317 | { |
8318 | if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE) |
8319 | { |
8320 | *pSumFreeSize = suballocItem->size; |
8321 | } |
8322 | else |
8323 | { |
8324 | if(suballocItem->hAllocation->CanBecomeLost() && |
8325 | suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) |
8326 | { |
8327 | ++*itemsToMakeLostCount; |
8328 | *pSumItemSize = suballocItem->size; |
8329 | } |
8330 | else |
8331 | { |
8332 | return false; |
8333 | } |
8334 | } |
8335 | |
8336 | // Remaining size is too small for this request: Early return. |
8337 | if(GetSize() - suballocItem->offset < allocSize) |
8338 | { |
8339 | return false; |
8340 | } |
8341 | |
8342 | // Start from offset equal to beginning of this suballocation. |
8343 | *pOffset = suballocItem->offset; |
8344 | |
8345 | // Apply VMA_DEBUG_MARGIN at the beginning. |
8346 | if(VMA_DEBUG_MARGIN > 0) |
8347 | { |
8348 | *pOffset += VMA_DEBUG_MARGIN; |
8349 | } |
8350 | |
8351 | // Apply alignment. |
8352 | *pOffset = VmaAlignUp(val: *pOffset, align: allocAlignment); |
8353 | |
8354 | // Check previous suballocations for BufferImageGranularity conflicts. |
8355 | // Make bigger alignment if necessary. |
8356 | if(bufferImageGranularity > 1) |
8357 | { |
8358 | bool bufferImageGranularityConflict = false; |
8359 | VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; |
8360 | while(prevSuballocItem != m_Suballocations.cbegin()) |
8361 | { |
8362 | --prevSuballocItem; |
8363 | const VmaSuballocation& prevSuballoc = *prevSuballocItem; |
8364 | if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: *pOffset, pageSize: bufferImageGranularity)) |
8365 | { |
8366 | if(VmaIsBufferImageGranularityConflict(suballocType1: prevSuballoc.type, suballocType2: allocType)) |
8367 | { |
8368 | bufferImageGranularityConflict = true; |
8369 | break; |
8370 | } |
8371 | } |
8372 | else |
8373 | // Already on previous page. |
8374 | break; |
8375 | } |
8376 | if(bufferImageGranularityConflict) |
8377 | { |
8378 | *pOffset = VmaAlignUp(val: *pOffset, align: bufferImageGranularity); |
8379 | } |
8380 | } |
8381 | |
8382 | // Now that we have final *pOffset, check if we are past suballocItem. |
8383 | // If yes, return false - this function should be called for another suballocItem as starting point. |
8384 | if(*pOffset >= suballocItem->offset + suballocItem->size) |
8385 | { |
8386 | return false; |
8387 | } |
8388 | |
8389 | // Calculate padding at the beginning based on current offset. |
8390 | const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset; |
8391 | |
8392 | // Calculate required margin at the end. |
8393 | const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN; |
8394 | |
8395 | const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin; |
8396 | // Another early return check. |
8397 | if(suballocItem->offset + totalSize > GetSize()) |
8398 | { |
8399 | return false; |
8400 | } |
8401 | |
8402 | // Advance lastSuballocItem until desired size is reached. |
8403 | // Update itemsToMakeLostCount. |
8404 | VmaSuballocationList::const_iterator lastSuballocItem = suballocItem; |
8405 | if(totalSize > suballocItem->size) |
8406 | { |
8407 | VkDeviceSize remainingSize = totalSize - suballocItem->size; |
8408 | while(remainingSize > 0) |
8409 | { |
8410 | ++lastSuballocItem; |
8411 | if(lastSuballocItem == m_Suballocations.cend()) |
8412 | { |
8413 | return false; |
8414 | } |
8415 | if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE) |
8416 | { |
8417 | *pSumFreeSize += lastSuballocItem->size; |
8418 | } |
8419 | else |
8420 | { |
8421 | VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE); |
8422 | if(lastSuballocItem->hAllocation->CanBecomeLost() && |
8423 | lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) |
8424 | { |
8425 | ++*itemsToMakeLostCount; |
8426 | *pSumItemSize += lastSuballocItem->size; |
8427 | } |
8428 | else |
8429 | { |
8430 | return false; |
8431 | } |
8432 | } |
8433 | remainingSize = (lastSuballocItem->size < remainingSize) ? |
8434 | remainingSize - lastSuballocItem->size : 0; |
8435 | } |
8436 | } |
8437 | |
8438 | // Check next suballocations for BufferImageGranularity conflicts. |
8439 | // If conflict exists, we must mark more allocations lost or fail. |
8440 | if(bufferImageGranularity > 1) |
8441 | { |
8442 | VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem; |
8443 | ++nextSuballocItem; |
8444 | while(nextSuballocItem != m_Suballocations.cend()) |
8445 | { |
8446 | const VmaSuballocation& nextSuballoc = *nextSuballocItem; |
8447 | if(VmaBlocksOnSamePage(resourceAOffset: *pOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity)) |
8448 | { |
8449 | if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: nextSuballoc.type)) |
8450 | { |
8451 | VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE); |
8452 | if(nextSuballoc.hAllocation->CanBecomeLost() && |
8453 | nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) |
8454 | { |
8455 | ++*itemsToMakeLostCount; |
8456 | } |
8457 | else |
8458 | { |
8459 | return false; |
8460 | } |
8461 | } |
8462 | } |
8463 | else |
8464 | { |
8465 | // Already on next page. |
8466 | break; |
8467 | } |
8468 | ++nextSuballocItem; |
8469 | } |
8470 | } |
8471 | } |
8472 | else |
8473 | { |
8474 | const VmaSuballocation& suballoc = *suballocItem; |
8475 | VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); |
8476 | |
8477 | *pSumFreeSize = suballoc.size; |
8478 | |
8479 | // Size of this suballocation is too small for this request: Early return. |
8480 | if(suballoc.size < allocSize) |
8481 | { |
8482 | return false; |
8483 | } |
8484 | |
8485 | // Start from offset equal to beginning of this suballocation. |
8486 | *pOffset = suballoc.offset; |
8487 | |
8488 | // Apply VMA_DEBUG_MARGIN at the beginning. |
8489 | if(VMA_DEBUG_MARGIN > 0) |
8490 | { |
8491 | *pOffset += VMA_DEBUG_MARGIN; |
8492 | } |
8493 | |
8494 | // Apply alignment. |
8495 | *pOffset = VmaAlignUp(val: *pOffset, align: allocAlignment); |
8496 | |
8497 | // Check previous suballocations for BufferImageGranularity conflicts. |
8498 | // Make bigger alignment if necessary. |
8499 | if(bufferImageGranularity > 1) |
8500 | { |
8501 | bool bufferImageGranularityConflict = false; |
8502 | VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; |
8503 | while(prevSuballocItem != m_Suballocations.cbegin()) |
8504 | { |
8505 | --prevSuballocItem; |
8506 | const VmaSuballocation& prevSuballoc = *prevSuballocItem; |
8507 | if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: *pOffset, pageSize: bufferImageGranularity)) |
8508 | { |
8509 | if(VmaIsBufferImageGranularityConflict(suballocType1: prevSuballoc.type, suballocType2: allocType)) |
8510 | { |
8511 | bufferImageGranularityConflict = true; |
8512 | break; |
8513 | } |
8514 | } |
8515 | else |
8516 | // Already on previous page. |
8517 | break; |
8518 | } |
8519 | if(bufferImageGranularityConflict) |
8520 | { |
8521 | *pOffset = VmaAlignUp(val: *pOffset, align: bufferImageGranularity); |
8522 | } |
8523 | } |
8524 | |
8525 | // Calculate padding at the beginning based on current offset. |
8526 | const VkDeviceSize paddingBegin = *pOffset - suballoc.offset; |
8527 | |
8528 | // Calculate required margin at the end. |
8529 | const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN; |
8530 | |
8531 | // Fail if requested size plus margin before and after is bigger than size of this suballocation. |
8532 | if(paddingBegin + allocSize + requiredEndMargin > suballoc.size) |
8533 | { |
8534 | return false; |
8535 | } |
8536 | |
8537 | // Check next suballocations for BufferImageGranularity conflicts. |
8538 | // If conflict exists, allocation cannot be made here. |
8539 | if(bufferImageGranularity > 1) |
8540 | { |
8541 | VmaSuballocationList::const_iterator nextSuballocItem = suballocItem; |
8542 | ++nextSuballocItem; |
8543 | while(nextSuballocItem != m_Suballocations.cend()) |
8544 | { |
8545 | const VmaSuballocation& nextSuballoc = *nextSuballocItem; |
8546 | if(VmaBlocksOnSamePage(resourceAOffset: *pOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity)) |
8547 | { |
8548 | if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: nextSuballoc.type)) |
8549 | { |
8550 | return false; |
8551 | } |
8552 | } |
8553 | else |
8554 | { |
8555 | // Already on next page. |
8556 | break; |
8557 | } |
8558 | ++nextSuballocItem; |
8559 | } |
8560 | } |
8561 | } |
8562 | |
8563 | // All tests passed: Success. pOffset is already filled. |
8564 | return true; |
8565 | } |
8566 | |
8567 | void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item) |
8568 | { |
8569 | VMA_ASSERT(item != m_Suballocations.end()); |
8570 | VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); |
8571 | |
8572 | VmaSuballocationList::iterator nextItem = item; |
8573 | ++nextItem; |
8574 | VMA_ASSERT(nextItem != m_Suballocations.end()); |
8575 | VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE); |
8576 | |
8577 | item->size += nextItem->size; |
8578 | --m_FreeCount; |
8579 | m_Suballocations.erase(it: nextItem); |
8580 | } |
8581 | |
8582 | VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem) |
8583 | { |
8584 | // Change this suballocation to be marked as free. |
8585 | VmaSuballocation& suballoc = *suballocItem; |
8586 | suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
8587 | suballoc.hAllocation = VK_NULL_HANDLE; |
8588 | |
8589 | // Update totals. |
8590 | ++m_FreeCount; |
8591 | m_SumFreeSize += suballoc.size; |
8592 | |
8593 | // Merge with previous and/or next suballocation if it's also free. |
8594 | bool mergeWithNext = false; |
8595 | bool mergeWithPrev = false; |
8596 | |
8597 | VmaSuballocationList::iterator nextItem = suballocItem; |
8598 | ++nextItem; |
8599 | if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)) |
8600 | { |
8601 | mergeWithNext = true; |
8602 | } |
8603 | |
8604 | VmaSuballocationList::iterator prevItem = suballocItem; |
8605 | if(suballocItem != m_Suballocations.begin()) |
8606 | { |
8607 | --prevItem; |
8608 | if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE) |
8609 | { |
8610 | mergeWithPrev = true; |
8611 | } |
8612 | } |
8613 | |
8614 | if(mergeWithNext) |
8615 | { |
8616 | UnregisterFreeSuballocation(item: nextItem); |
8617 | MergeFreeWithNext(item: suballocItem); |
8618 | } |
8619 | |
8620 | if(mergeWithPrev) |
8621 | { |
8622 | UnregisterFreeSuballocation(item: prevItem); |
8623 | MergeFreeWithNext(item: prevItem); |
8624 | RegisterFreeSuballocation(item: prevItem); |
8625 | return prevItem; |
8626 | } |
8627 | else |
8628 | { |
8629 | RegisterFreeSuballocation(item: suballocItem); |
8630 | return suballocItem; |
8631 | } |
8632 | } |
8633 | |
8634 | void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item) |
8635 | { |
8636 | VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); |
8637 | VMA_ASSERT(item->size > 0); |
8638 | |
8639 | // You may want to enable this validation at the beginning or at the end of |
8640 | // this function, depending on what do you want to check. |
8641 | VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); |
8642 | |
8643 | if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) |
8644 | { |
8645 | if(m_FreeSuballocationsBySize.empty()) |
8646 | { |
8647 | m_FreeSuballocationsBySize.push_back(src: item); |
8648 | } |
8649 | else |
8650 | { |
8651 | VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(vector&: m_FreeSuballocationsBySize, value: item); |
8652 | } |
8653 | } |
8654 | |
8655 | //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); |
8656 | } |
8657 | |
8658 | |
8659 | void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item) |
8660 | { |
8661 | VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); |
8662 | VMA_ASSERT(item->size > 0); |
8663 | |
8664 | // You may want to enable this validation at the beginning or at the end of |
8665 | // this function, depending on what do you want to check. |
8666 | VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); |
8667 | |
8668 | if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) |
8669 | { |
8670 | VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( |
8671 | beg: m_FreeSuballocationsBySize.data(), |
8672 | end: m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(), |
8673 | key: item, |
8674 | cmp: VmaSuballocationItemSizeLess()); |
8675 | for(size_t index = it - m_FreeSuballocationsBySize.data(); |
8676 | index < m_FreeSuballocationsBySize.size(); |
8677 | ++index) |
8678 | { |
8679 | if(m_FreeSuballocationsBySize[index] == item) |
8680 | { |
8681 | VmaVectorRemove(vec&: m_FreeSuballocationsBySize, index); |
8682 | return; |
8683 | } |
8684 | VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found." ); |
8685 | } |
8686 | VMA_ASSERT(0 && "Not found." ); |
8687 | } |
8688 | |
8689 | //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); |
8690 | } |
8691 | |
8692 | bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible( |
8693 | VkDeviceSize bufferImageGranularity, |
8694 | VmaSuballocationType& inOutPrevSuballocType) const |
8695 | { |
8696 | if(bufferImageGranularity == 1 || IsEmpty()) |
8697 | { |
8698 | return false; |
8699 | } |
8700 | |
8701 | VkDeviceSize minAlignment = VK_WHOLE_SIZE; |
8702 | bool typeConflictFound = false; |
8703 | for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin(); |
8704 | it != m_Suballocations.cend(); |
8705 | ++it) |
8706 | { |
8707 | const VmaSuballocationType suballocType = it->type; |
8708 | if(suballocType != VMA_SUBALLOCATION_TYPE_FREE) |
8709 | { |
8710 | minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment()); |
8711 | if(VmaIsBufferImageGranularityConflict(suballocType1: inOutPrevSuballocType, suballocType2: suballocType)) |
8712 | { |
8713 | typeConflictFound = true; |
8714 | } |
8715 | inOutPrevSuballocType = suballocType; |
8716 | } |
8717 | } |
8718 | |
8719 | return typeConflictFound || minAlignment >= bufferImageGranularity; |
8720 | } |
8721 | |
8722 | //////////////////////////////////////////////////////////////////////////////// |
8723 | // class VmaBlockMetadata_Linear |
8724 | |
8725 | VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) : |
8726 | VmaBlockMetadata(hAllocator), |
8727 | m_SumFreeSize(0), |
8728 | m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())), |
8729 | m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())), |
8730 | m_1stVectorIndex(0), |
8731 | m_2ndVectorMode(SECOND_VECTOR_EMPTY), |
8732 | m_1stNullItemsBeginCount(0), |
8733 | m_1stNullItemsMiddleCount(0), |
8734 | m_2ndNullItemsCount(0) |
8735 | { |
8736 | } |
8737 | |
8738 | VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear() |
8739 | { |
8740 | } |
8741 | |
8742 | void VmaBlockMetadata_Linear::Init(VkDeviceSize size) |
8743 | { |
8744 | VmaBlockMetadata::Init(size); |
8745 | m_SumFreeSize = size; |
8746 | } |
8747 | |
8748 | bool VmaBlockMetadata_Linear::Validate() const |
8749 | { |
8750 | const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
8751 | const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
8752 | |
8753 | VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY)); |
8754 | VMA_VALIDATE(!suballocations1st.empty() || |
8755 | suballocations2nd.empty() || |
8756 | m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER); |
8757 | |
8758 | if(!suballocations1st.empty()) |
8759 | { |
8760 | // Null item at the beginning should be accounted into m_1stNullItemsBeginCount. |
8761 | VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE); |
8762 | // Null item at the end should be just pop_back(). |
8763 | VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE); |
8764 | } |
8765 | if(!suballocations2nd.empty()) |
8766 | { |
8767 | // Null item at the end should be just pop_back(). |
8768 | VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE); |
8769 | } |
8770 | |
8771 | VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size()); |
8772 | VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size()); |
8773 | |
8774 | VkDeviceSize sumUsedSize = 0; |
8775 | const size_t suballoc1stCount = suballocations1st.size(); |
8776 | VkDeviceSize offset = VMA_DEBUG_MARGIN; |
8777 | |
8778 | if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) |
8779 | { |
8780 | const size_t suballoc2ndCount = suballocations2nd.size(); |
8781 | size_t nullItem2ndCount = 0; |
8782 | for(size_t i = 0; i < suballoc2ndCount; ++i) |
8783 | { |
8784 | const VmaSuballocation& suballoc = suballocations2nd[i]; |
8785 | const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); |
8786 | |
8787 | VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE)); |
8788 | VMA_VALIDATE(suballoc.offset >= offset); |
8789 | |
8790 | if(!currFree) |
8791 | { |
8792 | VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset); |
8793 | VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size); |
8794 | sumUsedSize += suballoc.size; |
8795 | } |
8796 | else |
8797 | { |
8798 | ++nullItem2ndCount; |
8799 | } |
8800 | |
8801 | offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN; |
8802 | } |
8803 | |
8804 | VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); |
8805 | } |
8806 | |
8807 | for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i) |
8808 | { |
8809 | const VmaSuballocation& suballoc = suballocations1st[i]; |
8810 | VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE && |
8811 | suballoc.hAllocation == VK_NULL_HANDLE); |
8812 | } |
8813 | |
8814 | size_t nullItem1stCount = m_1stNullItemsBeginCount; |
8815 | |
8816 | for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i) |
8817 | { |
8818 | const VmaSuballocation& suballoc = suballocations1st[i]; |
8819 | const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); |
8820 | |
8821 | VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE)); |
8822 | VMA_VALIDATE(suballoc.offset >= offset); |
8823 | VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree); |
8824 | |
8825 | if(!currFree) |
8826 | { |
8827 | VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset); |
8828 | VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size); |
8829 | sumUsedSize += suballoc.size; |
8830 | } |
8831 | else |
8832 | { |
8833 | ++nullItem1stCount; |
8834 | } |
8835 | |
8836 | offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN; |
8837 | } |
8838 | VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount); |
8839 | |
8840 | if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) |
8841 | { |
8842 | const size_t suballoc2ndCount = suballocations2nd.size(); |
8843 | size_t nullItem2ndCount = 0; |
8844 | for(size_t i = suballoc2ndCount; i--; ) |
8845 | { |
8846 | const VmaSuballocation& suballoc = suballocations2nd[i]; |
8847 | const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); |
8848 | |
8849 | VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE)); |
8850 | VMA_VALIDATE(suballoc.offset >= offset); |
8851 | |
8852 | if(!currFree) |
8853 | { |
8854 | VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset); |
8855 | VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size); |
8856 | sumUsedSize += suballoc.size; |
8857 | } |
8858 | else |
8859 | { |
8860 | ++nullItem2ndCount; |
8861 | } |
8862 | |
8863 | offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN; |
8864 | } |
8865 | |
8866 | VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); |
8867 | } |
8868 | |
8869 | VMA_VALIDATE(offset <= GetSize()); |
8870 | VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize); |
8871 | |
8872 | return true; |
8873 | } |
8874 | |
8875 | size_t VmaBlockMetadata_Linear::GetAllocationCount() const |
8876 | { |
8877 | return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) + |
8878 | AccessSuballocations2nd().size() - m_2ndNullItemsCount; |
8879 | } |
8880 | |
8881 | VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const |
8882 | { |
8883 | const VkDeviceSize size = GetSize(); |
8884 | |
8885 | /* |
8886 | We don't consider gaps inside allocation vectors with freed allocations because |
8887 | they are not suitable for reuse in linear allocator. We consider only space that |
8888 | is available for new allocations. |
8889 | */ |
8890 | if(IsEmpty()) |
8891 | { |
8892 | return size; |
8893 | } |
8894 | |
8895 | const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
8896 | |
8897 | switch(m_2ndVectorMode) |
8898 | { |
8899 | case SECOND_VECTOR_EMPTY: |
8900 | /* |
8901 | Available space is after end of 1st, as well as before beginning of 1st (which |
8902 | whould make it a ring buffer). |
8903 | */ |
8904 | { |
8905 | const size_t suballocations1stCount = suballocations1st.size(); |
8906 | VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount); |
8907 | const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; |
8908 | const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1]; |
8909 | return VMA_MAX( |
8910 | firstSuballoc.offset, |
8911 | size - (lastSuballoc.offset + lastSuballoc.size)); |
8912 | } |
8913 | break; |
8914 | |
8915 | case SECOND_VECTOR_RING_BUFFER: |
8916 | /* |
8917 | Available space is only between end of 2nd and beginning of 1st. |
8918 | */ |
8919 | { |
8920 | const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
8921 | const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back(); |
8922 | const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount]; |
8923 | return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size); |
8924 | } |
8925 | break; |
8926 | |
8927 | case SECOND_VECTOR_DOUBLE_STACK: |
8928 | /* |
8929 | Available space is only between end of 1st and top of 2nd. |
8930 | */ |
8931 | { |
8932 | const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
8933 | const VmaSuballocation& topSuballoc2nd = suballocations2nd.back(); |
8934 | const VmaSuballocation& lastSuballoc1st = suballocations1st.back(); |
8935 | return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size); |
8936 | } |
8937 | break; |
8938 | |
8939 | default: |
8940 | VMA_ASSERT(0); |
8941 | return 0; |
8942 | } |
8943 | } |
8944 | |
8945 | void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const |
8946 | { |
8947 | const VkDeviceSize size = GetSize(); |
8948 | const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
8949 | const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
8950 | const size_t suballoc1stCount = suballocations1st.size(); |
8951 | const size_t suballoc2ndCount = suballocations2nd.size(); |
8952 | |
8953 | outInfo.blockCount = 1; |
8954 | outInfo.allocationCount = (uint32_t)GetAllocationCount(); |
8955 | outInfo.unusedRangeCount = 0; |
8956 | outInfo.usedBytes = 0; |
8957 | outInfo.allocationSizeMin = UINT64_MAX; |
8958 | outInfo.allocationSizeMax = 0; |
8959 | outInfo.unusedRangeSizeMin = UINT64_MAX; |
8960 | outInfo.unusedRangeSizeMax = 0; |
8961 | |
8962 | VkDeviceSize lastOffset = 0; |
8963 | |
8964 | if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) |
8965 | { |
8966 | const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; |
8967 | size_t nextAlloc2ndIndex = 0; |
8968 | while(lastOffset < freeSpace2ndTo1stEnd) |
8969 | { |
8970 | // Find next non-null allocation or move nextAllocIndex to the end. |
8971 | while(nextAlloc2ndIndex < suballoc2ndCount && |
8972 | suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) |
8973 | { |
8974 | ++nextAlloc2ndIndex; |
8975 | } |
8976 | |
8977 | // Found non-null allocation. |
8978 | if(nextAlloc2ndIndex < suballoc2ndCount) |
8979 | { |
8980 | const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; |
8981 | |
8982 | // 1. Process free space before this allocation. |
8983 | if(lastOffset < suballoc.offset) |
8984 | { |
8985 | // There is free space from lastOffset to suballoc.offset. |
8986 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
8987 | ++outInfo.unusedRangeCount; |
8988 | outInfo.unusedBytes += unusedRangeSize; |
8989 | outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); |
8990 | outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); |
8991 | } |
8992 | |
8993 | // 2. Process this allocation. |
8994 | // There is allocation with suballoc.offset, suballoc.size. |
8995 | outInfo.usedBytes += suballoc.size; |
8996 | outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); |
8997 | outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size); |
8998 | |
8999 | // 3. Prepare for next iteration. |
9000 | lastOffset = suballoc.offset + suballoc.size; |
9001 | ++nextAlloc2ndIndex; |
9002 | } |
9003 | // We are at the end. |
9004 | else |
9005 | { |
9006 | // There is free space from lastOffset to freeSpace2ndTo1stEnd. |
9007 | if(lastOffset < freeSpace2ndTo1stEnd) |
9008 | { |
9009 | const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; |
9010 | ++outInfo.unusedRangeCount; |
9011 | outInfo.unusedBytes += unusedRangeSize; |
9012 | outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); |
9013 | outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); |
9014 | } |
9015 | |
9016 | // End of loop. |
9017 | lastOffset = freeSpace2ndTo1stEnd; |
9018 | } |
9019 | } |
9020 | } |
9021 | |
9022 | size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; |
9023 | const VkDeviceSize freeSpace1stTo2ndEnd = |
9024 | m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; |
9025 | while(lastOffset < freeSpace1stTo2ndEnd) |
9026 | { |
9027 | // Find next non-null allocation or move nextAllocIndex to the end. |
9028 | while(nextAlloc1stIndex < suballoc1stCount && |
9029 | suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE) |
9030 | { |
9031 | ++nextAlloc1stIndex; |
9032 | } |
9033 | |
9034 | // Found non-null allocation. |
9035 | if(nextAlloc1stIndex < suballoc1stCount) |
9036 | { |
9037 | const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; |
9038 | |
9039 | // 1. Process free space before this allocation. |
9040 | if(lastOffset < suballoc.offset) |
9041 | { |
9042 | // There is free space from lastOffset to suballoc.offset. |
9043 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
9044 | ++outInfo.unusedRangeCount; |
9045 | outInfo.unusedBytes += unusedRangeSize; |
9046 | outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); |
9047 | outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); |
9048 | } |
9049 | |
9050 | // 2. Process this allocation. |
9051 | // There is allocation with suballoc.offset, suballoc.size. |
9052 | outInfo.usedBytes += suballoc.size; |
9053 | outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); |
9054 | outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size); |
9055 | |
9056 | // 3. Prepare for next iteration. |
9057 | lastOffset = suballoc.offset + suballoc.size; |
9058 | ++nextAlloc1stIndex; |
9059 | } |
9060 | // We are at the end. |
9061 | else |
9062 | { |
9063 | // There is free space from lastOffset to freeSpace1stTo2ndEnd. |
9064 | if(lastOffset < freeSpace1stTo2ndEnd) |
9065 | { |
9066 | const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; |
9067 | ++outInfo.unusedRangeCount; |
9068 | outInfo.unusedBytes += unusedRangeSize; |
9069 | outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); |
9070 | outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); |
9071 | } |
9072 | |
9073 | // End of loop. |
9074 | lastOffset = freeSpace1stTo2ndEnd; |
9075 | } |
9076 | } |
9077 | |
9078 | if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) |
9079 | { |
9080 | size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; |
9081 | while(lastOffset < size) |
9082 | { |
9083 | // Find next non-null allocation or move nextAllocIndex to the end. |
9084 | while(nextAlloc2ndIndex != SIZE_MAX && |
9085 | suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) |
9086 | { |
9087 | --nextAlloc2ndIndex; |
9088 | } |
9089 | |
9090 | // Found non-null allocation. |
9091 | if(nextAlloc2ndIndex != SIZE_MAX) |
9092 | { |
9093 | const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; |
9094 | |
9095 | // 1. Process free space before this allocation. |
9096 | if(lastOffset < suballoc.offset) |
9097 | { |
9098 | // There is free space from lastOffset to suballoc.offset. |
9099 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
9100 | ++outInfo.unusedRangeCount; |
9101 | outInfo.unusedBytes += unusedRangeSize; |
9102 | outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); |
9103 | outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); |
9104 | } |
9105 | |
9106 | // 2. Process this allocation. |
9107 | // There is allocation with suballoc.offset, suballoc.size. |
9108 | outInfo.usedBytes += suballoc.size; |
9109 | outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); |
9110 | outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size); |
9111 | |
9112 | // 3. Prepare for next iteration. |
9113 | lastOffset = suballoc.offset + suballoc.size; |
9114 | --nextAlloc2ndIndex; |
9115 | } |
9116 | // We are at the end. |
9117 | else |
9118 | { |
9119 | // There is free space from lastOffset to size. |
9120 | if(lastOffset < size) |
9121 | { |
9122 | const VkDeviceSize unusedRangeSize = size - lastOffset; |
9123 | ++outInfo.unusedRangeCount; |
9124 | outInfo.unusedBytes += unusedRangeSize; |
9125 | outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); |
9126 | outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); |
9127 | } |
9128 | |
9129 | // End of loop. |
9130 | lastOffset = size; |
9131 | } |
9132 | } |
9133 | } |
9134 | |
9135 | outInfo.unusedBytes = size - outInfo.usedBytes; |
9136 | } |
9137 | |
9138 | void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const |
9139 | { |
9140 | const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
9141 | const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
9142 | const VkDeviceSize size = GetSize(); |
9143 | const size_t suballoc1stCount = suballocations1st.size(); |
9144 | const size_t suballoc2ndCount = suballocations2nd.size(); |
9145 | |
9146 | inoutStats.size += size; |
9147 | |
9148 | VkDeviceSize lastOffset = 0; |
9149 | |
9150 | if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) |
9151 | { |
9152 | const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; |
9153 | size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount; |
9154 | while(lastOffset < freeSpace2ndTo1stEnd) |
9155 | { |
9156 | // Find next non-null allocation or move nextAlloc2ndIndex to the end. |
9157 | while(nextAlloc2ndIndex < suballoc2ndCount && |
9158 | suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) |
9159 | { |
9160 | ++nextAlloc2ndIndex; |
9161 | } |
9162 | |
9163 | // Found non-null allocation. |
9164 | if(nextAlloc2ndIndex < suballoc2ndCount) |
9165 | { |
9166 | const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; |
9167 | |
9168 | // 1. Process free space before this allocation. |
9169 | if(lastOffset < suballoc.offset) |
9170 | { |
9171 | // There is free space from lastOffset to suballoc.offset. |
9172 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
9173 | inoutStats.unusedSize += unusedRangeSize; |
9174 | ++inoutStats.unusedRangeCount; |
9175 | inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); |
9176 | } |
9177 | |
9178 | // 2. Process this allocation. |
9179 | // There is allocation with suballoc.offset, suballoc.size. |
9180 | ++inoutStats.allocationCount; |
9181 | |
9182 | // 3. Prepare for next iteration. |
9183 | lastOffset = suballoc.offset + suballoc.size; |
9184 | ++nextAlloc2ndIndex; |
9185 | } |
9186 | // We are at the end. |
9187 | else |
9188 | { |
9189 | if(lastOffset < freeSpace2ndTo1stEnd) |
9190 | { |
9191 | // There is free space from lastOffset to freeSpace2ndTo1stEnd. |
9192 | const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; |
9193 | inoutStats.unusedSize += unusedRangeSize; |
9194 | ++inoutStats.unusedRangeCount; |
9195 | inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); |
9196 | } |
9197 | |
9198 | // End of loop. |
9199 | lastOffset = freeSpace2ndTo1stEnd; |
9200 | } |
9201 | } |
9202 | } |
9203 | |
9204 | size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; |
9205 | const VkDeviceSize freeSpace1stTo2ndEnd = |
9206 | m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; |
9207 | while(lastOffset < freeSpace1stTo2ndEnd) |
9208 | { |
9209 | // Find next non-null allocation or move nextAllocIndex to the end. |
9210 | while(nextAlloc1stIndex < suballoc1stCount && |
9211 | suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE) |
9212 | { |
9213 | ++nextAlloc1stIndex; |
9214 | } |
9215 | |
9216 | // Found non-null allocation. |
9217 | if(nextAlloc1stIndex < suballoc1stCount) |
9218 | { |
9219 | const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; |
9220 | |
9221 | // 1. Process free space before this allocation. |
9222 | if(lastOffset < suballoc.offset) |
9223 | { |
9224 | // There is free space from lastOffset to suballoc.offset. |
9225 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
9226 | inoutStats.unusedSize += unusedRangeSize; |
9227 | ++inoutStats.unusedRangeCount; |
9228 | inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); |
9229 | } |
9230 | |
9231 | // 2. Process this allocation. |
9232 | // There is allocation with suballoc.offset, suballoc.size. |
9233 | ++inoutStats.allocationCount; |
9234 | |
9235 | // 3. Prepare for next iteration. |
9236 | lastOffset = suballoc.offset + suballoc.size; |
9237 | ++nextAlloc1stIndex; |
9238 | } |
9239 | // We are at the end. |
9240 | else |
9241 | { |
9242 | if(lastOffset < freeSpace1stTo2ndEnd) |
9243 | { |
9244 | // There is free space from lastOffset to freeSpace1stTo2ndEnd. |
9245 | const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; |
9246 | inoutStats.unusedSize += unusedRangeSize; |
9247 | ++inoutStats.unusedRangeCount; |
9248 | inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); |
9249 | } |
9250 | |
9251 | // End of loop. |
9252 | lastOffset = freeSpace1stTo2ndEnd; |
9253 | } |
9254 | } |
9255 | |
9256 | if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) |
9257 | { |
9258 | size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; |
9259 | while(lastOffset < size) |
9260 | { |
9261 | // Find next non-null allocation or move nextAlloc2ndIndex to the end. |
9262 | while(nextAlloc2ndIndex != SIZE_MAX && |
9263 | suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) |
9264 | { |
9265 | --nextAlloc2ndIndex; |
9266 | } |
9267 | |
9268 | // Found non-null allocation. |
9269 | if(nextAlloc2ndIndex != SIZE_MAX) |
9270 | { |
9271 | const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; |
9272 | |
9273 | // 1. Process free space before this allocation. |
9274 | if(lastOffset < suballoc.offset) |
9275 | { |
9276 | // There is free space from lastOffset to suballoc.offset. |
9277 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
9278 | inoutStats.unusedSize += unusedRangeSize; |
9279 | ++inoutStats.unusedRangeCount; |
9280 | inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); |
9281 | } |
9282 | |
9283 | // 2. Process this allocation. |
9284 | // There is allocation with suballoc.offset, suballoc.size. |
9285 | ++inoutStats.allocationCount; |
9286 | |
9287 | // 3. Prepare for next iteration. |
9288 | lastOffset = suballoc.offset + suballoc.size; |
9289 | --nextAlloc2ndIndex; |
9290 | } |
9291 | // We are at the end. |
9292 | else |
9293 | { |
9294 | if(lastOffset < size) |
9295 | { |
9296 | // There is free space from lastOffset to size. |
9297 | const VkDeviceSize unusedRangeSize = size - lastOffset; |
9298 | inoutStats.unusedSize += unusedRangeSize; |
9299 | ++inoutStats.unusedRangeCount; |
9300 | inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); |
9301 | } |
9302 | |
9303 | // End of loop. |
9304 | lastOffset = size; |
9305 | } |
9306 | } |
9307 | } |
9308 | } |
9309 | |
9310 | #if VMA_STATS_STRING_ENABLED |
9311 | void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const |
9312 | { |
9313 | const VkDeviceSize size = GetSize(); |
9314 | const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
9315 | const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
9316 | const size_t suballoc1stCount = suballocations1st.size(); |
9317 | const size_t suballoc2ndCount = suballocations2nd.size(); |
9318 | |
9319 | // FIRST PASS |
9320 | |
9321 | size_t unusedRangeCount = 0; |
9322 | VkDeviceSize usedBytes = 0; |
9323 | |
9324 | VkDeviceSize lastOffset = 0; |
9325 | |
9326 | size_t alloc2ndCount = 0; |
9327 | if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) |
9328 | { |
9329 | const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; |
9330 | size_t nextAlloc2ndIndex = 0; |
9331 | while(lastOffset < freeSpace2ndTo1stEnd) |
9332 | { |
9333 | // Find next non-null allocation or move nextAlloc2ndIndex to the end. |
9334 | while(nextAlloc2ndIndex < suballoc2ndCount && |
9335 | suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) |
9336 | { |
9337 | ++nextAlloc2ndIndex; |
9338 | } |
9339 | |
9340 | // Found non-null allocation. |
9341 | if(nextAlloc2ndIndex < suballoc2ndCount) |
9342 | { |
9343 | const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; |
9344 | |
9345 | // 1. Process free space before this allocation. |
9346 | if(lastOffset < suballoc.offset) |
9347 | { |
9348 | // There is free space from lastOffset to suballoc.offset. |
9349 | ++unusedRangeCount; |
9350 | } |
9351 | |
9352 | // 2. Process this allocation. |
9353 | // There is allocation with suballoc.offset, suballoc.size. |
9354 | ++alloc2ndCount; |
9355 | usedBytes += suballoc.size; |
9356 | |
9357 | // 3. Prepare for next iteration. |
9358 | lastOffset = suballoc.offset + suballoc.size; |
9359 | ++nextAlloc2ndIndex; |
9360 | } |
9361 | // We are at the end. |
9362 | else |
9363 | { |
9364 | if(lastOffset < freeSpace2ndTo1stEnd) |
9365 | { |
9366 | // There is free space from lastOffset to freeSpace2ndTo1stEnd. |
9367 | ++unusedRangeCount; |
9368 | } |
9369 | |
9370 | // End of loop. |
9371 | lastOffset = freeSpace2ndTo1stEnd; |
9372 | } |
9373 | } |
9374 | } |
9375 | |
9376 | size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; |
9377 | size_t alloc1stCount = 0; |
9378 | const VkDeviceSize freeSpace1stTo2ndEnd = |
9379 | m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; |
9380 | while(lastOffset < freeSpace1stTo2ndEnd) |
9381 | { |
9382 | // Find next non-null allocation or move nextAllocIndex to the end. |
9383 | while(nextAlloc1stIndex < suballoc1stCount && |
9384 | suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE) |
9385 | { |
9386 | ++nextAlloc1stIndex; |
9387 | } |
9388 | |
9389 | // Found non-null allocation. |
9390 | if(nextAlloc1stIndex < suballoc1stCount) |
9391 | { |
9392 | const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; |
9393 | |
9394 | // 1. Process free space before this allocation. |
9395 | if(lastOffset < suballoc.offset) |
9396 | { |
9397 | // There is free space from lastOffset to suballoc.offset. |
9398 | ++unusedRangeCount; |
9399 | } |
9400 | |
9401 | // 2. Process this allocation. |
9402 | // There is allocation with suballoc.offset, suballoc.size. |
9403 | ++alloc1stCount; |
9404 | usedBytes += suballoc.size; |
9405 | |
9406 | // 3. Prepare for next iteration. |
9407 | lastOffset = suballoc.offset + suballoc.size; |
9408 | ++nextAlloc1stIndex; |
9409 | } |
9410 | // We are at the end. |
9411 | else |
9412 | { |
9413 | if(lastOffset < size) |
9414 | { |
9415 | // There is free space from lastOffset to freeSpace1stTo2ndEnd. |
9416 | ++unusedRangeCount; |
9417 | } |
9418 | |
9419 | // End of loop. |
9420 | lastOffset = freeSpace1stTo2ndEnd; |
9421 | } |
9422 | } |
9423 | |
9424 | if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) |
9425 | { |
9426 | size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; |
9427 | while(lastOffset < size) |
9428 | { |
9429 | // Find next non-null allocation or move nextAlloc2ndIndex to the end. |
9430 | while(nextAlloc2ndIndex != SIZE_MAX && |
9431 | suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) |
9432 | { |
9433 | --nextAlloc2ndIndex; |
9434 | } |
9435 | |
9436 | // Found non-null allocation. |
9437 | if(nextAlloc2ndIndex != SIZE_MAX) |
9438 | { |
9439 | const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; |
9440 | |
9441 | // 1. Process free space before this allocation. |
9442 | if(lastOffset < suballoc.offset) |
9443 | { |
9444 | // There is free space from lastOffset to suballoc.offset. |
9445 | ++unusedRangeCount; |
9446 | } |
9447 | |
9448 | // 2. Process this allocation. |
9449 | // There is allocation with suballoc.offset, suballoc.size. |
9450 | ++alloc2ndCount; |
9451 | usedBytes += suballoc.size; |
9452 | |
9453 | // 3. Prepare for next iteration. |
9454 | lastOffset = suballoc.offset + suballoc.size; |
9455 | --nextAlloc2ndIndex; |
9456 | } |
9457 | // We are at the end. |
9458 | else |
9459 | { |
9460 | if(lastOffset < size) |
9461 | { |
9462 | // There is free space from lastOffset to size. |
9463 | ++unusedRangeCount; |
9464 | } |
9465 | |
9466 | // End of loop. |
9467 | lastOffset = size; |
9468 | } |
9469 | } |
9470 | } |
9471 | |
9472 | const VkDeviceSize unusedBytes = size - usedBytes; |
9473 | PrintDetailedMap_Begin(json, unusedBytes, allocationCount: alloc1stCount + alloc2ndCount, unusedRangeCount); |
9474 | |
9475 | // SECOND PASS |
9476 | lastOffset = 0; |
9477 | |
9478 | if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) |
9479 | { |
9480 | const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; |
9481 | size_t nextAlloc2ndIndex = 0; |
9482 | while(lastOffset < freeSpace2ndTo1stEnd) |
9483 | { |
9484 | // Find next non-null allocation or move nextAlloc2ndIndex to the end. |
9485 | while(nextAlloc2ndIndex < suballoc2ndCount && |
9486 | suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) |
9487 | { |
9488 | ++nextAlloc2ndIndex; |
9489 | } |
9490 | |
9491 | // Found non-null allocation. |
9492 | if(nextAlloc2ndIndex < suballoc2ndCount) |
9493 | { |
9494 | const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; |
9495 | |
9496 | // 1. Process free space before this allocation. |
9497 | if(lastOffset < suballoc.offset) |
9498 | { |
9499 | // There is free space from lastOffset to suballoc.offset. |
9500 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
9501 | PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize); |
9502 | } |
9503 | |
9504 | // 2. Process this allocation. |
9505 | // There is allocation with suballoc.offset, suballoc.size. |
9506 | PrintDetailedMap_Allocation(json, offset: suballoc.offset, hAllocation: suballoc.hAllocation); |
9507 | |
9508 | // 3. Prepare for next iteration. |
9509 | lastOffset = suballoc.offset + suballoc.size; |
9510 | ++nextAlloc2ndIndex; |
9511 | } |
9512 | // We are at the end. |
9513 | else |
9514 | { |
9515 | if(lastOffset < freeSpace2ndTo1stEnd) |
9516 | { |
9517 | // There is free space from lastOffset to freeSpace2ndTo1stEnd. |
9518 | const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; |
9519 | PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize); |
9520 | } |
9521 | |
9522 | // End of loop. |
9523 | lastOffset = freeSpace2ndTo1stEnd; |
9524 | } |
9525 | } |
9526 | } |
9527 | |
9528 | nextAlloc1stIndex = m_1stNullItemsBeginCount; |
9529 | while(lastOffset < freeSpace1stTo2ndEnd) |
9530 | { |
9531 | // Find next non-null allocation or move nextAllocIndex to the end. |
9532 | while(nextAlloc1stIndex < suballoc1stCount && |
9533 | suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE) |
9534 | { |
9535 | ++nextAlloc1stIndex; |
9536 | } |
9537 | |
9538 | // Found non-null allocation. |
9539 | if(nextAlloc1stIndex < suballoc1stCount) |
9540 | { |
9541 | const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; |
9542 | |
9543 | // 1. Process free space before this allocation. |
9544 | if(lastOffset < suballoc.offset) |
9545 | { |
9546 | // There is free space from lastOffset to suballoc.offset. |
9547 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
9548 | PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize); |
9549 | } |
9550 | |
9551 | // 2. Process this allocation. |
9552 | // There is allocation with suballoc.offset, suballoc.size. |
9553 | PrintDetailedMap_Allocation(json, offset: suballoc.offset, hAllocation: suballoc.hAllocation); |
9554 | |
9555 | // 3. Prepare for next iteration. |
9556 | lastOffset = suballoc.offset + suballoc.size; |
9557 | ++nextAlloc1stIndex; |
9558 | } |
9559 | // We are at the end. |
9560 | else |
9561 | { |
9562 | if(lastOffset < freeSpace1stTo2ndEnd) |
9563 | { |
9564 | // There is free space from lastOffset to freeSpace1stTo2ndEnd. |
9565 | const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; |
9566 | PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize); |
9567 | } |
9568 | |
9569 | // End of loop. |
9570 | lastOffset = freeSpace1stTo2ndEnd; |
9571 | } |
9572 | } |
9573 | |
9574 | if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) |
9575 | { |
9576 | size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; |
9577 | while(lastOffset < size) |
9578 | { |
9579 | // Find next non-null allocation or move nextAlloc2ndIndex to the end. |
9580 | while(nextAlloc2ndIndex != SIZE_MAX && |
9581 | suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) |
9582 | { |
9583 | --nextAlloc2ndIndex; |
9584 | } |
9585 | |
9586 | // Found non-null allocation. |
9587 | if(nextAlloc2ndIndex != SIZE_MAX) |
9588 | { |
9589 | const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; |
9590 | |
9591 | // 1. Process free space before this allocation. |
9592 | if(lastOffset < suballoc.offset) |
9593 | { |
9594 | // There is free space from lastOffset to suballoc.offset. |
9595 | const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; |
9596 | PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize); |
9597 | } |
9598 | |
9599 | // 2. Process this allocation. |
9600 | // There is allocation with suballoc.offset, suballoc.size. |
9601 | PrintDetailedMap_Allocation(json, offset: suballoc.offset, hAllocation: suballoc.hAllocation); |
9602 | |
9603 | // 3. Prepare for next iteration. |
9604 | lastOffset = suballoc.offset + suballoc.size; |
9605 | --nextAlloc2ndIndex; |
9606 | } |
9607 | // We are at the end. |
9608 | else |
9609 | { |
9610 | if(lastOffset < size) |
9611 | { |
9612 | // There is free space from lastOffset to size. |
9613 | const VkDeviceSize unusedRangeSize = size - lastOffset; |
9614 | PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize); |
9615 | } |
9616 | |
9617 | // End of loop. |
9618 | lastOffset = size; |
9619 | } |
9620 | } |
9621 | } |
9622 | |
9623 | PrintDetailedMap_End(json); |
9624 | } |
9625 | #endif // #if VMA_STATS_STRING_ENABLED |
9626 | |
9627 | bool VmaBlockMetadata_Linear::CreateAllocationRequest( |
9628 | uint32_t currentFrameIndex, |
9629 | uint32_t frameInUseCount, |
9630 | VkDeviceSize bufferImageGranularity, |
9631 | VkDeviceSize allocSize, |
9632 | VkDeviceSize allocAlignment, |
9633 | bool upperAddress, |
9634 | VmaSuballocationType allocType, |
9635 | bool canMakeOtherLost, |
9636 | uint32_t /*strategy*/, |
9637 | VmaAllocationRequest* pAllocationRequest) |
9638 | { |
9639 | VMA_ASSERT(allocSize > 0); |
9640 | VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); |
9641 | VMA_ASSERT(pAllocationRequest != VMA_NULL); |
9642 | VMA_HEAVY_ASSERT(Validate()); |
9643 | |
9644 | const VkDeviceSize size = GetSize(); |
9645 | SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
9646 | SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
9647 | |
9648 | if(upperAddress) |
9649 | { |
9650 | if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) |
9651 | { |
9652 | VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer." ); |
9653 | return false; |
9654 | } |
9655 | |
9656 | // Try to allocate before 2nd.back(), or end of block if 2nd.empty(). |
9657 | if(allocSize > size) |
9658 | { |
9659 | return false; |
9660 | } |
9661 | VkDeviceSize resultBaseOffset = size - allocSize; |
9662 | if(!suballocations2nd.empty()) |
9663 | { |
9664 | const VmaSuballocation& lastSuballoc = suballocations2nd.back(); |
9665 | resultBaseOffset = lastSuballoc.offset - allocSize; |
9666 | if(allocSize > lastSuballoc.offset) |
9667 | { |
9668 | return false; |
9669 | } |
9670 | } |
9671 | |
9672 | // Start from offset equal to end of free space. |
9673 | VkDeviceSize resultOffset = resultBaseOffset; |
9674 | |
9675 | // Apply VMA_DEBUG_MARGIN at the end. |
9676 | if(VMA_DEBUG_MARGIN > 0) |
9677 | { |
9678 | #if VMA_DEBUG_MARGIN |
9679 | if(resultOffset < VMA_DEBUG_MARGIN) |
9680 | { |
9681 | return false; |
9682 | } |
9683 | #endif |
9684 | resultOffset -= VMA_DEBUG_MARGIN; |
9685 | } |
9686 | |
9687 | // Apply alignment. |
9688 | resultOffset = VmaAlignDown(val: resultOffset, align: allocAlignment); |
9689 | |
9690 | // Check next suballocations from 2nd for BufferImageGranularity conflicts. |
9691 | // Make bigger alignment if necessary. |
9692 | if(bufferImageGranularity > 1 && !suballocations2nd.empty()) |
9693 | { |
9694 | bool bufferImageGranularityConflict = false; |
9695 | for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) |
9696 | { |
9697 | const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; |
9698 | if(VmaBlocksOnSamePage(resourceAOffset: resultOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity)) |
9699 | { |
9700 | if(VmaIsBufferImageGranularityConflict(suballocType1: nextSuballoc.type, suballocType2: allocType)) |
9701 | { |
9702 | bufferImageGranularityConflict = true; |
9703 | break; |
9704 | } |
9705 | } |
9706 | else |
9707 | // Already on previous page. |
9708 | break; |
9709 | } |
9710 | if(bufferImageGranularityConflict) |
9711 | { |
9712 | resultOffset = VmaAlignDown(val: resultOffset, align: bufferImageGranularity); |
9713 | } |
9714 | } |
9715 | |
9716 | // There is enough free space. |
9717 | const VkDeviceSize endOf1st = !suballocations1st.empty() ? |
9718 | suballocations1st.back().offset + suballocations1st.back().size : |
9719 | 0; |
9720 | if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset) |
9721 | { |
9722 | // Check previous suballocations for BufferImageGranularity conflicts. |
9723 | // If conflict exists, allocation cannot be made here. |
9724 | if(bufferImageGranularity > 1) |
9725 | { |
9726 | for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) |
9727 | { |
9728 | const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; |
9729 | if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: resultOffset, pageSize: bufferImageGranularity)) |
9730 | { |
9731 | if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: prevSuballoc.type)) |
9732 | { |
9733 | return false; |
9734 | } |
9735 | } |
9736 | else |
9737 | { |
9738 | // Already on next page. |
9739 | break; |
9740 | } |
9741 | } |
9742 | } |
9743 | |
9744 | // All tests passed: Success. |
9745 | pAllocationRequest->offset = resultOffset; |
9746 | pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st; |
9747 | pAllocationRequest->sumItemSize = 0; |
9748 | // pAllocationRequest->item unused. |
9749 | pAllocationRequest->itemsToMakeLostCount = 0; |
9750 | return true; |
9751 | } |
9752 | } |
9753 | else // !upperAddress |
9754 | { |
9755 | if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) |
9756 | { |
9757 | // Try to allocate at the end of 1st vector. |
9758 | |
9759 | VkDeviceSize resultBaseOffset = 0; |
9760 | if(!suballocations1st.empty()) |
9761 | { |
9762 | const VmaSuballocation& lastSuballoc = suballocations1st.back(); |
9763 | resultBaseOffset = lastSuballoc.offset + lastSuballoc.size; |
9764 | } |
9765 | |
9766 | // Start from offset equal to beginning of free space. |
9767 | VkDeviceSize resultOffset = resultBaseOffset; |
9768 | |
9769 | // Apply VMA_DEBUG_MARGIN at the beginning. |
9770 | if(VMA_DEBUG_MARGIN > 0) |
9771 | { |
9772 | resultOffset += VMA_DEBUG_MARGIN; |
9773 | } |
9774 | |
9775 | // Apply alignment. |
9776 | resultOffset = VmaAlignUp(val: resultOffset, align: allocAlignment); |
9777 | |
9778 | // Check previous suballocations for BufferImageGranularity conflicts. |
9779 | // Make bigger alignment if necessary. |
9780 | if(bufferImageGranularity > 1 && !suballocations1st.empty()) |
9781 | { |
9782 | bool bufferImageGranularityConflict = false; |
9783 | for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) |
9784 | { |
9785 | const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; |
9786 | if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: resultOffset, pageSize: bufferImageGranularity)) |
9787 | { |
9788 | if(VmaIsBufferImageGranularityConflict(suballocType1: prevSuballoc.type, suballocType2: allocType)) |
9789 | { |
9790 | bufferImageGranularityConflict = true; |
9791 | break; |
9792 | } |
9793 | } |
9794 | else |
9795 | // Already on previous page. |
9796 | break; |
9797 | } |
9798 | if(bufferImageGranularityConflict) |
9799 | { |
9800 | resultOffset = VmaAlignUp(val: resultOffset, align: bufferImageGranularity); |
9801 | } |
9802 | } |
9803 | |
9804 | const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? |
9805 | suballocations2nd.back().offset : size; |
9806 | |
9807 | // There is enough free space at the end after alignment. |
9808 | if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd) |
9809 | { |
9810 | // Check next suballocations for BufferImageGranularity conflicts. |
9811 | // If conflict exists, allocation cannot be made here. |
9812 | if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) |
9813 | { |
9814 | for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) |
9815 | { |
9816 | const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; |
9817 | if(VmaBlocksOnSamePage(resourceAOffset: resultOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity)) |
9818 | { |
9819 | if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: nextSuballoc.type)) |
9820 | { |
9821 | return false; |
9822 | } |
9823 | } |
9824 | else |
9825 | { |
9826 | // Already on previous page. |
9827 | break; |
9828 | } |
9829 | } |
9830 | } |
9831 | |
9832 | // All tests passed: Success. |
9833 | pAllocationRequest->offset = resultOffset; |
9834 | pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset; |
9835 | pAllocationRequest->sumItemSize = 0; |
9836 | // pAllocationRequest->item unused. |
9837 | pAllocationRequest->itemsToMakeLostCount = 0; |
9838 | return true; |
9839 | } |
9840 | } |
9841 | |
9842 | // Wrap-around to end of 2nd vector. Try to allocate there, watching for the |
9843 | // beginning of 1st vector as the end of free space. |
9844 | if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) |
9845 | { |
9846 | VMA_ASSERT(!suballocations1st.empty()); |
9847 | |
9848 | VkDeviceSize resultBaseOffset = 0; |
9849 | if(!suballocations2nd.empty()) |
9850 | { |
9851 | const VmaSuballocation& lastSuballoc = suballocations2nd.back(); |
9852 | resultBaseOffset = lastSuballoc.offset + lastSuballoc.size; |
9853 | } |
9854 | |
9855 | // Start from offset equal to beginning of free space. |
9856 | VkDeviceSize resultOffset = resultBaseOffset; |
9857 | |
9858 | // Apply VMA_DEBUG_MARGIN at the beginning. |
9859 | if(VMA_DEBUG_MARGIN > 0) |
9860 | { |
9861 | resultOffset += VMA_DEBUG_MARGIN; |
9862 | } |
9863 | |
9864 | // Apply alignment. |
9865 | resultOffset = VmaAlignUp(val: resultOffset, align: allocAlignment); |
9866 | |
9867 | // Check previous suballocations for BufferImageGranularity conflicts. |
9868 | // Make bigger alignment if necessary. |
9869 | if(bufferImageGranularity > 1 && !suballocations2nd.empty()) |
9870 | { |
9871 | bool bufferImageGranularityConflict = false; |
9872 | for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; ) |
9873 | { |
9874 | const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex]; |
9875 | if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: resultOffset, pageSize: bufferImageGranularity)) |
9876 | { |
9877 | if(VmaIsBufferImageGranularityConflict(suballocType1: prevSuballoc.type, suballocType2: allocType)) |
9878 | { |
9879 | bufferImageGranularityConflict = true; |
9880 | break; |
9881 | } |
9882 | } |
9883 | else |
9884 | // Already on previous page. |
9885 | break; |
9886 | } |
9887 | if(bufferImageGranularityConflict) |
9888 | { |
9889 | resultOffset = VmaAlignUp(val: resultOffset, align: bufferImageGranularity); |
9890 | } |
9891 | } |
9892 | |
9893 | pAllocationRequest->itemsToMakeLostCount = 0; |
9894 | pAllocationRequest->sumItemSize = 0; |
9895 | size_t index1st = m_1stNullItemsBeginCount; |
9896 | |
9897 | if(canMakeOtherLost) |
9898 | { |
9899 | while(index1st < suballocations1st.size() && |
9900 | resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset) |
9901 | { |
9902 | // Next colliding allocation at the beginning of 1st vector found. Try to make it lost. |
9903 | const VmaSuballocation& suballoc = suballocations1st[index1st]; |
9904 | if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE) |
9905 | { |
9906 | // No problem. |
9907 | } |
9908 | else |
9909 | { |
9910 | VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE); |
9911 | if(suballoc.hAllocation->CanBecomeLost() && |
9912 | suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) |
9913 | { |
9914 | ++pAllocationRequest->itemsToMakeLostCount; |
9915 | pAllocationRequest->sumItemSize += suballoc.size; |
9916 | } |
9917 | else |
9918 | { |
9919 | return false; |
9920 | } |
9921 | } |
9922 | ++index1st; |
9923 | } |
9924 | |
9925 | // Check next suballocations for BufferImageGranularity conflicts. |
9926 | // If conflict exists, we must mark more allocations lost or fail. |
9927 | if(bufferImageGranularity > 1) |
9928 | { |
9929 | while(index1st < suballocations1st.size()) |
9930 | { |
9931 | const VmaSuballocation& suballoc = suballocations1st[index1st]; |
9932 | if(VmaBlocksOnSamePage(resourceAOffset: resultOffset, resourceASize: allocSize, resourceBOffset: suballoc.offset, pageSize: bufferImageGranularity)) |
9933 | { |
9934 | if(suballoc.hAllocation != VK_NULL_HANDLE) |
9935 | { |
9936 | // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type). |
9937 | if(suballoc.hAllocation->CanBecomeLost() && |
9938 | suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) |
9939 | { |
9940 | ++pAllocationRequest->itemsToMakeLostCount; |
9941 | pAllocationRequest->sumItemSize += suballoc.size; |
9942 | } |
9943 | else |
9944 | { |
9945 | return false; |
9946 | } |
9947 | } |
9948 | } |
9949 | else |
9950 | { |
9951 | // Already on next page. |
9952 | break; |
9953 | } |
9954 | ++index1st; |
9955 | } |
9956 | } |
9957 | } |
9958 | |
9959 | // There is enough free space at the end after alignment. |
9960 | if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) || |
9961 | (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset)) |
9962 | { |
9963 | // Check next suballocations for BufferImageGranularity conflicts. |
9964 | // If conflict exists, allocation cannot be made here. |
9965 | if(bufferImageGranularity > 1) |
9966 | { |
9967 | for(size_t nextSuballocIndex = index1st; |
9968 | nextSuballocIndex < suballocations1st.size(); |
9969 | nextSuballocIndex++) |
9970 | { |
9971 | const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex]; |
9972 | if(VmaBlocksOnSamePage(resourceAOffset: resultOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity)) |
9973 | { |
9974 | if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: nextSuballoc.type)) |
9975 | { |
9976 | return false; |
9977 | } |
9978 | } |
9979 | else |
9980 | { |
9981 | // Already on next page. |
9982 | break; |
9983 | } |
9984 | } |
9985 | } |
9986 | |
9987 | // All tests passed: Success. |
9988 | pAllocationRequest->offset = resultOffset; |
9989 | pAllocationRequest->sumFreeSize = |
9990 | (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size) |
9991 | - resultBaseOffset |
9992 | - pAllocationRequest->sumItemSize; |
9993 | // pAllocationRequest->item unused. |
9994 | return true; |
9995 | } |
9996 | } |
9997 | } |
9998 | |
9999 | return false; |
10000 | } |
10001 | |
10002 | bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost( |
10003 | uint32_t currentFrameIndex, |
10004 | uint32_t frameInUseCount, |
10005 | VmaAllocationRequest* pAllocationRequest) |
10006 | { |
10007 | if(pAllocationRequest->itemsToMakeLostCount == 0) |
10008 | { |
10009 | return true; |
10010 | } |
10011 | |
10012 | VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER); |
10013 | |
10014 | SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
10015 | size_t index1st = m_1stNullItemsBeginCount; |
10016 | size_t madeLostCount = 0; |
10017 | while(madeLostCount < pAllocationRequest->itemsToMakeLostCount) |
10018 | { |
10019 | VMA_ASSERT(index1st < suballocations1st.size()); |
10020 | VmaSuballocation& suballoc = suballocations1st[index1st]; |
10021 | if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) |
10022 | { |
10023 | VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE); |
10024 | VMA_ASSERT(suballoc.hAllocation->CanBecomeLost()); |
10025 | if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) |
10026 | { |
10027 | suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
10028 | suballoc.hAllocation = VK_NULL_HANDLE; |
10029 | m_SumFreeSize += suballoc.size; |
10030 | ++m_1stNullItemsMiddleCount; |
10031 | ++madeLostCount; |
10032 | } |
10033 | else |
10034 | { |
10035 | return false; |
10036 | } |
10037 | } |
10038 | ++index1st; |
10039 | } |
10040 | |
10041 | CleanupAfterFree(); |
10042 | //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree(). |
10043 | |
10044 | return true; |
10045 | } |
10046 | |
10047 | uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) |
10048 | { |
10049 | uint32_t lostAllocationCount = 0; |
10050 | |
10051 | SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
10052 | for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i) |
10053 | { |
10054 | VmaSuballocation& suballoc = suballocations1st[i]; |
10055 | if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE && |
10056 | suballoc.hAllocation->CanBecomeLost() && |
10057 | suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) |
10058 | { |
10059 | suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
10060 | suballoc.hAllocation = VK_NULL_HANDLE; |
10061 | ++m_1stNullItemsMiddleCount; |
10062 | m_SumFreeSize += suballoc.size; |
10063 | ++lostAllocationCount; |
10064 | } |
10065 | } |
10066 | |
10067 | SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
10068 | for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i) |
10069 | { |
10070 | VmaSuballocation& suballoc = suballocations2nd[i]; |
10071 | if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE && |
10072 | suballoc.hAllocation->CanBecomeLost() && |
10073 | suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) |
10074 | { |
10075 | suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
10076 | suballoc.hAllocation = VK_NULL_HANDLE; |
10077 | ++m_2ndNullItemsCount; |
10078 | ++lostAllocationCount; |
10079 | } |
10080 | } |
10081 | |
10082 | if(lostAllocationCount) |
10083 | { |
10084 | CleanupAfterFree(); |
10085 | } |
10086 | |
10087 | return lostAllocationCount; |
10088 | } |
10089 | |
10090 | VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData) |
10091 | { |
10092 | SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
10093 | for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i) |
10094 | { |
10095 | const VmaSuballocation& suballoc = suballocations1st[i]; |
10096 | if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) |
10097 | { |
10098 | if(!VmaValidateMagicValue(pData: pBlockData, offset: suballoc.offset - VMA_DEBUG_MARGIN)) |
10099 | { |
10100 | VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!" ); |
10101 | return VK_ERROR_VALIDATION_FAILED_EXT; |
10102 | } |
10103 | if(!VmaValidateMagicValue(pData: pBlockData, offset: suballoc.offset + suballoc.size)) |
10104 | { |
10105 | VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!" ); |
10106 | return VK_ERROR_VALIDATION_FAILED_EXT; |
10107 | } |
10108 | } |
10109 | } |
10110 | |
10111 | SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
10112 | for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i) |
10113 | { |
10114 | const VmaSuballocation& suballoc = suballocations2nd[i]; |
10115 | if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) |
10116 | { |
10117 | if(!VmaValidateMagicValue(pData: pBlockData, offset: suballoc.offset - VMA_DEBUG_MARGIN)) |
10118 | { |
10119 | VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!" ); |
10120 | return VK_ERROR_VALIDATION_FAILED_EXT; |
10121 | } |
10122 | if(!VmaValidateMagicValue(pData: pBlockData, offset: suballoc.offset + suballoc.size)) |
10123 | { |
10124 | VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!" ); |
10125 | return VK_ERROR_VALIDATION_FAILED_EXT; |
10126 | } |
10127 | } |
10128 | } |
10129 | |
10130 | return VK_SUCCESS; |
10131 | } |
10132 | |
10133 | void VmaBlockMetadata_Linear::Alloc( |
10134 | const VmaAllocationRequest& request, |
10135 | VmaSuballocationType type, |
10136 | VkDeviceSize allocSize, |
10137 | bool upperAddress, |
10138 | VmaAllocation hAllocation) |
10139 | { |
10140 | const VmaSuballocation newSuballoc = { .offset: request.offset, .size: allocSize, .hAllocation: hAllocation, .type: type }; |
10141 | |
10142 | if(upperAddress) |
10143 | { |
10144 | VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER && |
10145 | "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer." ); |
10146 | SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
10147 | suballocations2nd.push_back(src: newSuballoc); |
10148 | m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK; |
10149 | } |
10150 | else |
10151 | { |
10152 | SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
10153 | |
10154 | // First allocation. |
10155 | if(suballocations1st.empty()) |
10156 | { |
10157 | suballocations1st.push_back(src: newSuballoc); |
10158 | } |
10159 | else |
10160 | { |
10161 | // New allocation at the end of 1st vector. |
10162 | if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size) |
10163 | { |
10164 | // Check if it fits before the end of the block. |
10165 | VMA_ASSERT(request.offset + allocSize <= GetSize()); |
10166 | suballocations1st.push_back(src: newSuballoc); |
10167 | } |
10168 | // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector. |
10169 | else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset) |
10170 | { |
10171 | SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
10172 | |
10173 | switch(m_2ndVectorMode) |
10174 | { |
10175 | case SECOND_VECTOR_EMPTY: |
10176 | // First allocation from second part ring buffer. |
10177 | VMA_ASSERT(suballocations2nd.empty()); |
10178 | m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER; |
10179 | break; |
10180 | case SECOND_VECTOR_RING_BUFFER: |
10181 | // 2-part ring buffer is already started. |
10182 | VMA_ASSERT(!suballocations2nd.empty()); |
10183 | break; |
10184 | case SECOND_VECTOR_DOUBLE_STACK: |
10185 | VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack." ); |
10186 | break; |
10187 | default: |
10188 | VMA_ASSERT(0); |
10189 | } |
10190 | |
10191 | suballocations2nd.push_back(src: newSuballoc); |
10192 | } |
10193 | else |
10194 | { |
10195 | VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR." ); |
10196 | } |
10197 | } |
10198 | } |
10199 | |
10200 | m_SumFreeSize -= newSuballoc.size; |
10201 | } |
10202 | |
10203 | void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation) |
10204 | { |
10205 | FreeAtOffset(offset: allocation->GetOffset()); |
10206 | } |
10207 | |
10208 | void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset) |
10209 | { |
10210 | SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
10211 | SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
10212 | |
10213 | if(!suballocations1st.empty()) |
10214 | { |
10215 | // First allocation: Mark it as next empty at the beginning. |
10216 | VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; |
10217 | if(firstSuballoc.offset == offset) |
10218 | { |
10219 | firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; |
10220 | firstSuballoc.hAllocation = VK_NULL_HANDLE; |
10221 | m_SumFreeSize += firstSuballoc.size; |
10222 | ++m_1stNullItemsBeginCount; |
10223 | CleanupAfterFree(); |
10224 | return; |
10225 | } |
10226 | } |
10227 | |
10228 | // Last allocation in 2-part ring buffer or top of upper stack (same logic). |
10229 | if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER || |
10230 | m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) |
10231 | { |
10232 | VmaSuballocation& lastSuballoc = suballocations2nd.back(); |
10233 | if(lastSuballoc.offset == offset) |
10234 | { |
10235 | m_SumFreeSize += lastSuballoc.size; |
10236 | suballocations2nd.pop_back(); |
10237 | CleanupAfterFree(); |
10238 | return; |
10239 | } |
10240 | } |
10241 | // Last allocation in 1st vector. |
10242 | else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY) |
10243 | { |
10244 | VmaSuballocation& lastSuballoc = suballocations1st.back(); |
10245 | if(lastSuballoc.offset == offset) |
10246 | { |
10247 | m_SumFreeSize += lastSuballoc.size; |
10248 | suballocations1st.pop_back(); |
10249 | CleanupAfterFree(); |
10250 | return; |
10251 | } |
10252 | } |
10253 | |
10254 | // Item from the middle of 1st vector. |
10255 | { |
10256 | VmaSuballocation refSuballoc; |
10257 | refSuballoc.offset = offset; |
10258 | // Rest of members stays uninitialized intentionally for better performance. |
10259 | SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>( |
10260 | beg: suballocations1st.begin() + m_1stNullItemsBeginCount, |
10261 | end: suballocations1st.end(), |
10262 | value: refSuballoc); |
10263 | if(it != suballocations1st.end()) |
10264 | { |
10265 | it->type = VMA_SUBALLOCATION_TYPE_FREE; |
10266 | it->hAllocation = VK_NULL_HANDLE; |
10267 | ++m_1stNullItemsMiddleCount; |
10268 | m_SumFreeSize += it->size; |
10269 | CleanupAfterFree(); |
10270 | return; |
10271 | } |
10272 | } |
10273 | |
10274 | if(m_2ndVectorMode != SECOND_VECTOR_EMPTY) |
10275 | { |
10276 | // Item from the middle of 2nd vector. |
10277 | VmaSuballocation refSuballoc; |
10278 | refSuballoc.offset = offset; |
10279 | // Rest of members stays uninitialized intentionally for better performance. |
10280 | SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? |
10281 | VmaVectorFindSorted<VmaSuballocationOffsetLess>(beg: suballocations2nd.begin(), end: suballocations2nd.end(), value: refSuballoc) : |
10282 | VmaVectorFindSorted<VmaSuballocationOffsetGreater>(beg: suballocations2nd.begin(), end: suballocations2nd.end(), value: refSuballoc); |
10283 | if(it != suballocations2nd.end()) |
10284 | { |
10285 | it->type = VMA_SUBALLOCATION_TYPE_FREE; |
10286 | it->hAllocation = VK_NULL_HANDLE; |
10287 | ++m_2ndNullItemsCount; |
10288 | m_SumFreeSize += it->size; |
10289 | CleanupAfterFree(); |
10290 | return; |
10291 | } |
10292 | } |
10293 | |
10294 | VMA_ASSERT(0 && "Allocation to free not found in linear allocator!" ); |
10295 | } |
10296 | |
10297 | bool VmaBlockMetadata_Linear::ShouldCompact1st() const |
10298 | { |
10299 | const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; |
10300 | const size_t suballocCount = AccessSuballocations1st().size(); |
10301 | return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3; |
10302 | } |
10303 | |
10304 | void VmaBlockMetadata_Linear::CleanupAfterFree() |
10305 | { |
10306 | SuballocationVectorType& suballocations1st = AccessSuballocations1st(); |
10307 | SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); |
10308 | |
10309 | if(IsEmpty()) |
10310 | { |
10311 | suballocations1st.clear(); |
10312 | suballocations2nd.clear(); |
10313 | m_1stNullItemsBeginCount = 0; |
10314 | m_1stNullItemsMiddleCount = 0; |
10315 | m_2ndNullItemsCount = 0; |
10316 | m_2ndVectorMode = SECOND_VECTOR_EMPTY; |
10317 | } |
10318 | else |
10319 | { |
10320 | const size_t suballoc1stCount = suballocations1st.size(); |
10321 | const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; |
10322 | VMA_ASSERT(nullItem1stCount <= suballoc1stCount); |
10323 | |
10324 | // Find more null items at the beginning of 1st vector. |
10325 | while(m_1stNullItemsBeginCount < suballoc1stCount && |
10326 | suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE) |
10327 | { |
10328 | ++m_1stNullItemsBeginCount; |
10329 | --m_1stNullItemsMiddleCount; |
10330 | } |
10331 | |
10332 | // Find more null items at the end of 1st vector. |
10333 | while(m_1stNullItemsMiddleCount > 0 && |
10334 | suballocations1st.back().hAllocation == VK_NULL_HANDLE) |
10335 | { |
10336 | --m_1stNullItemsMiddleCount; |
10337 | suballocations1st.pop_back(); |
10338 | } |
10339 | |
10340 | // Find more null items at the end of 2nd vector. |
10341 | while(m_2ndNullItemsCount > 0 && |
10342 | suballocations2nd.back().hAllocation == VK_NULL_HANDLE) |
10343 | { |
10344 | --m_2ndNullItemsCount; |
10345 | suballocations2nd.pop_back(); |
10346 | } |
10347 | |
10348 | if(ShouldCompact1st()) |
10349 | { |
10350 | const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount; |
10351 | size_t srcIndex = m_1stNullItemsBeginCount; |
10352 | for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex) |
10353 | { |
10354 | while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE) |
10355 | { |
10356 | ++srcIndex; |
10357 | } |
10358 | if(dstIndex != srcIndex) |
10359 | { |
10360 | suballocations1st[dstIndex] = suballocations1st[srcIndex]; |
10361 | } |
10362 | ++srcIndex; |
10363 | } |
10364 | suballocations1st.resize(newCount: nonNullItemCount); |
10365 | m_1stNullItemsBeginCount = 0; |
10366 | m_1stNullItemsMiddleCount = 0; |
10367 | } |
10368 | |
10369 | // 2nd vector became empty. |
10370 | if(suballocations2nd.empty()) |
10371 | { |
10372 | m_2ndVectorMode = SECOND_VECTOR_EMPTY; |
10373 | } |
10374 | |
10375 | // 1st vector became empty. |
10376 | if(suballocations1st.size() - m_1stNullItemsBeginCount == 0) |
10377 | { |
10378 | suballocations1st.clear(); |
10379 | m_1stNullItemsBeginCount = 0; |
10380 | |
10381 | if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) |
10382 | { |
10383 | // Swap 1st with 2nd. Now 2nd is empty. |
10384 | m_2ndVectorMode = SECOND_VECTOR_EMPTY; |
10385 | m_1stNullItemsMiddleCount = m_2ndNullItemsCount; |
10386 | while(m_1stNullItemsBeginCount < suballocations2nd.size() && |
10387 | suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE) |
10388 | { |
10389 | ++m_1stNullItemsBeginCount; |
10390 | --m_1stNullItemsMiddleCount; |
10391 | } |
10392 | m_2ndNullItemsCount = 0; |
10393 | m_1stVectorIndex ^= 1; |
10394 | } |
10395 | } |
10396 | } |
10397 | |
10398 | VMA_HEAVY_ASSERT(Validate()); |
10399 | } |
10400 | |
10401 | |
10402 | //////////////////////////////////////////////////////////////////////////////// |
10403 | // class VmaBlockMetadata_Buddy |
10404 | |
10405 | VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) : |
10406 | VmaBlockMetadata(hAllocator), |
10407 | m_Root(VMA_NULL), |
10408 | m_AllocationCount(0), |
10409 | m_FreeCount(1), |
10410 | m_SumFreeSize(0) |
10411 | { |
10412 | memset(s: m_FreeList, c: 0, n: sizeof(m_FreeList)); |
10413 | } |
10414 | |
10415 | VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy() |
10416 | { |
10417 | DeleteNode(node: m_Root); |
10418 | } |
10419 | |
10420 | void VmaBlockMetadata_Buddy::Init(VkDeviceSize size) |
10421 | { |
10422 | VmaBlockMetadata::Init(size); |
10423 | |
10424 | m_UsableSize = VmaPrevPow2(v: size); |
10425 | m_SumFreeSize = m_UsableSize; |
10426 | |
10427 | // Calculate m_LevelCount. |
10428 | m_LevelCount = 1; |
10429 | while(m_LevelCount < MAX_LEVELS && |
10430 | LevelToNodeSize(level: m_LevelCount) >= MIN_NODE_SIZE) |
10431 | { |
10432 | ++m_LevelCount; |
10433 | } |
10434 | |
10435 | Node* rootNode = vma_new(GetAllocationCallbacks(), Node)(); |
10436 | rootNode->offset = 0; |
10437 | rootNode->type = Node::TYPE_FREE; |
10438 | rootNode->parent = VMA_NULL; |
10439 | rootNode->buddy = VMA_NULL; |
10440 | |
10441 | m_Root = rootNode; |
10442 | AddToFreeListFront(level: 0, node: rootNode); |
10443 | } |
10444 | |
10445 | bool VmaBlockMetadata_Buddy::Validate() const |
10446 | { |
10447 | // Validate tree. |
10448 | ValidationContext ctx; |
10449 | if(!ValidateNode(ctx, VMA_NULL, curr: m_Root, level: 0, levelNodeSize: LevelToNodeSize(level: 0))) |
10450 | { |
10451 | VMA_VALIDATE(false && "ValidateNode failed." ); |
10452 | } |
10453 | VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount); |
10454 | VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize); |
10455 | |
10456 | // Validate free node lists. |
10457 | for(uint32_t level = 0; level < m_LevelCount; ++level) |
10458 | { |
10459 | VMA_VALIDATE(m_FreeList[level].front == VMA_NULL || |
10460 | m_FreeList[level].front->free.prev == VMA_NULL); |
10461 | |
10462 | for(Node* node = m_FreeList[level].front; |
10463 | node != VMA_NULL; |
10464 | node = node->free.next) |
10465 | { |
10466 | VMA_VALIDATE(node->type == Node::TYPE_FREE); |
10467 | |
10468 | if(node->free.next == VMA_NULL) |
10469 | { |
10470 | VMA_VALIDATE(m_FreeList[level].back == node); |
10471 | } |
10472 | else |
10473 | { |
10474 | VMA_VALIDATE(node->free.next->free.prev == node); |
10475 | } |
10476 | } |
10477 | } |
10478 | |
10479 | // Validate that free lists ar higher levels are empty. |
10480 | for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level) |
10481 | { |
10482 | VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL); |
10483 | } |
10484 | |
10485 | return true; |
10486 | } |
10487 | |
10488 | VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const |
10489 | { |
10490 | for(uint32_t level = 0; level < m_LevelCount; ++level) |
10491 | { |
10492 | if(m_FreeList[level].front != VMA_NULL) |
10493 | { |
10494 | return LevelToNodeSize(level); |
10495 | } |
10496 | } |
10497 | return 0; |
10498 | } |
10499 | |
10500 | void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const |
10501 | { |
10502 | const VkDeviceSize unusableSize = GetUnusableSize(); |
10503 | |
10504 | outInfo.blockCount = 1; |
10505 | |
10506 | outInfo.allocationCount = outInfo.unusedRangeCount = 0; |
10507 | outInfo.usedBytes = outInfo.unusedBytes = 0; |
10508 | |
10509 | outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0; |
10510 | outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX; |
10511 | outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused. |
10512 | |
10513 | CalcAllocationStatInfoNode(outInfo, node: m_Root, levelNodeSize: LevelToNodeSize(level: 0)); |
10514 | |
10515 | if(unusableSize > 0) |
10516 | { |
10517 | ++outInfo.unusedRangeCount; |
10518 | outInfo.unusedBytes += unusableSize; |
10519 | outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize); |
10520 | outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize); |
10521 | } |
10522 | } |
10523 | |
10524 | void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const |
10525 | { |
10526 | const VkDeviceSize unusableSize = GetUnusableSize(); |
10527 | |
10528 | inoutStats.size += GetSize(); |
10529 | inoutStats.unusedSize += m_SumFreeSize + unusableSize; |
10530 | inoutStats.allocationCount += m_AllocationCount; |
10531 | inoutStats.unusedRangeCount += m_FreeCount; |
10532 | inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax()); |
10533 | |
10534 | if(unusableSize > 0) |
10535 | { |
10536 | ++inoutStats.unusedRangeCount; |
10537 | // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations. |
10538 | } |
10539 | } |
10540 | |
10541 | #if VMA_STATS_STRING_ENABLED |
10542 | |
10543 | void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const |
10544 | { |
10545 | // TODO optimize |
10546 | VmaStatInfo stat; |
10547 | CalcAllocationStatInfo(outInfo&: stat); |
10548 | |
10549 | PrintDetailedMap_Begin( |
10550 | json, |
10551 | unusedBytes: stat.unusedBytes, |
10552 | allocationCount: stat.allocationCount, |
10553 | unusedRangeCount: stat.unusedRangeCount); |
10554 | |
10555 | PrintDetailedMapNode(json, node: m_Root, levelNodeSize: LevelToNodeSize(level: 0)); |
10556 | |
10557 | const VkDeviceSize unusableSize = GetUnusableSize(); |
10558 | if(unusableSize > 0) |
10559 | { |
10560 | PrintDetailedMap_UnusedRange(json, |
10561 | offset: m_UsableSize, // offset |
10562 | size: unusableSize); // size |
10563 | } |
10564 | |
10565 | PrintDetailedMap_End(json); |
10566 | } |
10567 | |
10568 | #endif // #if VMA_STATS_STRING_ENABLED |
10569 | |
10570 | bool VmaBlockMetadata_Buddy::CreateAllocationRequest( |
10571 | uint32_t /*currentFrameIndex*/, |
10572 | uint32_t /*frameInUseCount*/, |
10573 | VkDeviceSize bufferImageGranularity, |
10574 | VkDeviceSize allocSize, |
10575 | VkDeviceSize allocAlignment, |
10576 | bool upperAddress, |
10577 | VmaSuballocationType allocType, |
10578 | bool /*canMakeOtherLost*/, |
10579 | uint32_t /*strategy*/, |
10580 | VmaAllocationRequest* pAllocationRequest) |
10581 | { |
10582 | VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm." ); |
10583 | (void) upperAddress; |
10584 | |
10585 | // Simple way to respect bufferImageGranularity. May be optimized some day. |
10586 | // Whenever it might be an OPTIMAL image... |
10587 | if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || |
10588 | allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || |
10589 | allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) |
10590 | { |
10591 | allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity); |
10592 | allocSize = VMA_MAX(allocSize, bufferImageGranularity); |
10593 | } |
10594 | |
10595 | if(allocSize > m_UsableSize) |
10596 | { |
10597 | return false; |
10598 | } |
10599 | |
10600 | const uint32_t targetLevel = AllocSizeToLevel(allocSize); |
10601 | for(uint32_t level = targetLevel + 1; level--; ) |
10602 | { |
10603 | for(Node* freeNode = m_FreeList[level].front; |
10604 | freeNode != VMA_NULL; |
10605 | freeNode = freeNode->free.next) |
10606 | { |
10607 | if(freeNode->offset % allocAlignment == 0) |
10608 | { |
10609 | pAllocationRequest->offset = freeNode->offset; |
10610 | pAllocationRequest->sumFreeSize = LevelToNodeSize(level); |
10611 | pAllocationRequest->sumItemSize = 0; |
10612 | pAllocationRequest->itemsToMakeLostCount = 0; |
10613 | pAllocationRequest->customData = (void*)(uintptr_t)level; |
10614 | return true; |
10615 | } |
10616 | } |
10617 | } |
10618 | |
10619 | return false; |
10620 | } |
10621 | |
10622 | bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost( |
10623 | uint32_t /*currentFrameIndex*/, |
10624 | uint32_t /*frameInUseCount*/, |
10625 | VmaAllocationRequest* pAllocationRequest) |
10626 | { |
10627 | /* |
10628 | Lost allocations are not supported in buddy allocator at the moment. |
10629 | Support might be added in the future. |
10630 | */ |
10631 | return pAllocationRequest->itemsToMakeLostCount == 0; |
10632 | } |
10633 | |
10634 | uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t /*currentFrameIndex*/, uint32_t /*frameInUseCount*/) |
10635 | { |
10636 | /* |
10637 | Lost allocations are not supported in buddy allocator at the moment. |
10638 | Support might be added in the future. |
10639 | */ |
10640 | return 0; |
10641 | } |
10642 | |
10643 | void VmaBlockMetadata_Buddy::Alloc( |
10644 | const VmaAllocationRequest& request, |
10645 | VmaSuballocationType /*type*/, |
10646 | VkDeviceSize allocSize, |
10647 | bool /*upperAddress*/, |
10648 | VmaAllocation hAllocation) |
10649 | { |
10650 | const uint32_t targetLevel = AllocSizeToLevel(allocSize); |
10651 | uint32_t currLevel = (uint32_t)(uintptr_t)request.customData; |
10652 | |
10653 | Node* currNode = m_FreeList[currLevel].front; |
10654 | VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); |
10655 | while(currNode->offset != request.offset) |
10656 | { |
10657 | currNode = currNode->free.next; |
10658 | VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); |
10659 | } |
10660 | |
10661 | // Go down, splitting free nodes. |
10662 | while(currLevel < targetLevel) |
10663 | { |
10664 | // currNode is already first free node at currLevel. |
10665 | // Remove it from list of free nodes at this currLevel. |
10666 | RemoveFromFreeList(level: currLevel, node: currNode); |
10667 | |
10668 | const uint32_t childrenLevel = currLevel + 1; |
10669 | |
10670 | // Create two free sub-nodes. |
10671 | Node* leftChild = vma_new(GetAllocationCallbacks(), Node)(); |
10672 | Node* rightChild = vma_new(GetAllocationCallbacks(), Node)(); |
10673 | |
10674 | leftChild->offset = currNode->offset; |
10675 | leftChild->type = Node::TYPE_FREE; |
10676 | leftChild->parent = currNode; |
10677 | leftChild->buddy = rightChild; |
10678 | |
10679 | rightChild->offset = currNode->offset + LevelToNodeSize(level: childrenLevel); |
10680 | rightChild->type = Node::TYPE_FREE; |
10681 | rightChild->parent = currNode; |
10682 | rightChild->buddy = leftChild; |
10683 | |
10684 | // Convert current currNode to split type. |
10685 | currNode->type = Node::TYPE_SPLIT; |
10686 | currNode->split.leftChild = leftChild; |
10687 | |
10688 | // Add child nodes to free list. Order is important! |
10689 | AddToFreeListFront(level: childrenLevel, node: rightChild); |
10690 | AddToFreeListFront(level: childrenLevel, node: leftChild); |
10691 | |
10692 | ++m_FreeCount; |
10693 | //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2. |
10694 | ++currLevel; |
10695 | currNode = m_FreeList[currLevel].front; |
10696 | |
10697 | /* |
10698 | We can be sure that currNode, as left child of node previously split, |
10699 | also fullfills the alignment requirement. |
10700 | */ |
10701 | } |
10702 | |
10703 | // Remove from free list. |
10704 | VMA_ASSERT(currLevel == targetLevel && |
10705 | currNode != VMA_NULL && |
10706 | currNode->type == Node::TYPE_FREE); |
10707 | RemoveFromFreeList(level: currLevel, node: currNode); |
10708 | |
10709 | // Convert to allocation node. |
10710 | currNode->type = Node::TYPE_ALLOCATION; |
10711 | currNode->allocation.alloc = hAllocation; |
10712 | |
10713 | ++m_AllocationCount; |
10714 | --m_FreeCount; |
10715 | m_SumFreeSize -= allocSize; |
10716 | } |
10717 | |
10718 | void VmaBlockMetadata_Buddy::DeleteNode(Node* node) |
10719 | { |
10720 | if(node->type == Node::TYPE_SPLIT) |
10721 | { |
10722 | DeleteNode(node: node->split.leftChild->buddy); |
10723 | DeleteNode(node: node->split.leftChild); |
10724 | } |
10725 | |
10726 | vma_delete(pAllocationCallbacks: GetAllocationCallbacks(), ptr: node); |
10727 | } |
10728 | |
10729 | bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const |
10730 | { |
10731 | VMA_VALIDATE(level < m_LevelCount); |
10732 | VMA_VALIDATE(curr->parent == parent); |
10733 | VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL)); |
10734 | VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr); |
10735 | switch(curr->type) |
10736 | { |
10737 | case Node::TYPE_FREE: |
10738 | // curr->free.prev, next are validated separately. |
10739 | ctx.calculatedSumFreeSize += levelNodeSize; |
10740 | ++ctx.calculatedFreeCount; |
10741 | break; |
10742 | case Node::TYPE_ALLOCATION: |
10743 | ++ctx.calculatedAllocationCount; |
10744 | ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize(); |
10745 | VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE); |
10746 | break; |
10747 | case Node::TYPE_SPLIT: |
10748 | { |
10749 | const uint32_t childrenLevel = level + 1; |
10750 | const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2; |
10751 | const Node* const leftChild = curr->split.leftChild; |
10752 | VMA_VALIDATE(leftChild != VMA_NULL); |
10753 | VMA_VALIDATE(leftChild->offset == curr->offset); |
10754 | if(!ValidateNode(ctx, parent: curr, curr: leftChild, level: childrenLevel, levelNodeSize: childrenLevelNodeSize)) |
10755 | { |
10756 | VMA_VALIDATE(false && "ValidateNode for left child failed." ); |
10757 | } |
10758 | const Node* const rightChild = leftChild->buddy; |
10759 | VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize); |
10760 | if(!ValidateNode(ctx, parent: curr, curr: rightChild, level: childrenLevel, levelNodeSize: childrenLevelNodeSize)) |
10761 | { |
10762 | VMA_VALIDATE(false && "ValidateNode for right child failed." ); |
10763 | } |
10764 | } |
10765 | break; |
10766 | default: |
10767 | return false; |
10768 | } |
10769 | |
10770 | return true; |
10771 | } |
10772 | |
10773 | uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const |
10774 | { |
10775 | // I know this could be optimized somehow e.g. by using std::log2p1 from C++20. |
10776 | uint32_t level = 0; |
10777 | VkDeviceSize currLevelNodeSize = m_UsableSize; |
10778 | VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1; |
10779 | while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount) |
10780 | { |
10781 | ++level; |
10782 | currLevelNodeSize = nextLevelNodeSize; |
10783 | nextLevelNodeSize = currLevelNodeSize >> 1; |
10784 | } |
10785 | return level; |
10786 | } |
10787 | |
10788 | void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset) |
10789 | { |
10790 | // Find node and level. |
10791 | Node* node = m_Root; |
10792 | VkDeviceSize nodeOffset = 0; |
10793 | uint32_t level = 0; |
10794 | VkDeviceSize levelNodeSize = LevelToNodeSize(level: 0); |
10795 | while(node->type == Node::TYPE_SPLIT) |
10796 | { |
10797 | const VkDeviceSize nextLevelSize = levelNodeSize >> 1; |
10798 | if(offset < nodeOffset + nextLevelSize) |
10799 | { |
10800 | node = node->split.leftChild; |
10801 | } |
10802 | else |
10803 | { |
10804 | node = node->split.leftChild->buddy; |
10805 | nodeOffset += nextLevelSize; |
10806 | } |
10807 | ++level; |
10808 | levelNodeSize = nextLevelSize; |
10809 | } |
10810 | |
10811 | VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION); |
10812 | VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc); |
10813 | |
10814 | ++m_FreeCount; |
10815 | --m_AllocationCount; |
10816 | m_SumFreeSize += alloc->GetSize(); |
10817 | |
10818 | node->type = Node::TYPE_FREE; |
10819 | |
10820 | // Join free nodes if possible. |
10821 | while(level > 0 && node->buddy->type == Node::TYPE_FREE) |
10822 | { |
10823 | RemoveFromFreeList(level, node: node->buddy); |
10824 | Node* const parent = node->parent; |
10825 | |
10826 | vma_delete(pAllocationCallbacks: GetAllocationCallbacks(), ptr: node->buddy); |
10827 | vma_delete(pAllocationCallbacks: GetAllocationCallbacks(), ptr: node); |
10828 | parent->type = Node::TYPE_FREE; |
10829 | |
10830 | node = parent; |
10831 | --level; |
10832 | //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2. |
10833 | --m_FreeCount; |
10834 | } |
10835 | |
10836 | AddToFreeListFront(level, node); |
10837 | } |
10838 | |
10839 | void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const |
10840 | { |
10841 | switch(node->type) |
10842 | { |
10843 | case Node::TYPE_FREE: |
10844 | ++outInfo.unusedRangeCount; |
10845 | outInfo.unusedBytes += levelNodeSize; |
10846 | outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize); |
10847 | outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize); |
10848 | break; |
10849 | case Node::TYPE_ALLOCATION: |
10850 | { |
10851 | const VkDeviceSize allocSize = node->allocation.alloc->GetSize(); |
10852 | ++outInfo.allocationCount; |
10853 | outInfo.usedBytes += allocSize; |
10854 | outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize); |
10855 | outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize); |
10856 | |
10857 | const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize; |
10858 | if(unusedRangeSize > 0) |
10859 | { |
10860 | ++outInfo.unusedRangeCount; |
10861 | outInfo.unusedBytes += unusedRangeSize; |
10862 | outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize); |
10863 | outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize); |
10864 | } |
10865 | } |
10866 | break; |
10867 | case Node::TYPE_SPLIT: |
10868 | { |
10869 | const VkDeviceSize childrenNodeSize = levelNodeSize / 2; |
10870 | const Node* const leftChild = node->split.leftChild; |
10871 | CalcAllocationStatInfoNode(outInfo, node: leftChild, levelNodeSize: childrenNodeSize); |
10872 | const Node* const rightChild = leftChild->buddy; |
10873 | CalcAllocationStatInfoNode(outInfo, node: rightChild, levelNodeSize: childrenNodeSize); |
10874 | } |
10875 | break; |
10876 | default: |
10877 | VMA_ASSERT(0); |
10878 | } |
10879 | } |
10880 | |
10881 | void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node) |
10882 | { |
10883 | VMA_ASSERT(node->type == Node::TYPE_FREE); |
10884 | |
10885 | // List is empty. |
10886 | Node* const frontNode = m_FreeList[level].front; |
10887 | if(frontNode == VMA_NULL) |
10888 | { |
10889 | VMA_ASSERT(m_FreeList[level].back == VMA_NULL); |
10890 | node->free.prev = node->free.next = VMA_NULL; |
10891 | m_FreeList[level].front = m_FreeList[level].back = node; |
10892 | } |
10893 | else |
10894 | { |
10895 | VMA_ASSERT(frontNode->free.prev == VMA_NULL); |
10896 | node->free.prev = VMA_NULL; |
10897 | node->free.next = frontNode; |
10898 | frontNode->free.prev = node; |
10899 | m_FreeList[level].front = node; |
10900 | } |
10901 | } |
10902 | |
10903 | void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node) |
10904 | { |
10905 | VMA_ASSERT(m_FreeList[level].front != VMA_NULL); |
10906 | |
10907 | // It is at the front. |
10908 | if(node->free.prev == VMA_NULL) |
10909 | { |
10910 | VMA_ASSERT(m_FreeList[level].front == node); |
10911 | m_FreeList[level].front = node->free.next; |
10912 | } |
10913 | else |
10914 | { |
10915 | Node* const prevFreeNode = node->free.prev; |
10916 | VMA_ASSERT(prevFreeNode->free.next == node); |
10917 | prevFreeNode->free.next = node->free.next; |
10918 | } |
10919 | |
10920 | // It is at the back. |
10921 | if(node->free.next == VMA_NULL) |
10922 | { |
10923 | VMA_ASSERT(m_FreeList[level].back == node); |
10924 | m_FreeList[level].back = node->free.prev; |
10925 | } |
10926 | else |
10927 | { |
10928 | Node* const nextFreeNode = node->free.next; |
10929 | VMA_ASSERT(nextFreeNode->free.prev == node); |
10930 | nextFreeNode->free.prev = node->free.prev; |
10931 | } |
10932 | } |
10933 | |
10934 | #if VMA_STATS_STRING_ENABLED |
10935 | void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const |
10936 | { |
10937 | switch(node->type) |
10938 | { |
10939 | case Node::TYPE_FREE: |
10940 | PrintDetailedMap_UnusedRange(json, offset: node->offset, size: levelNodeSize); |
10941 | break; |
10942 | case Node::TYPE_ALLOCATION: |
10943 | { |
10944 | PrintDetailedMap_Allocation(json, offset: node->offset, hAllocation: node->allocation.alloc); |
10945 | const VkDeviceSize allocSize = node->allocation.alloc->GetSize(); |
10946 | if(allocSize < levelNodeSize) |
10947 | { |
10948 | PrintDetailedMap_UnusedRange(json, offset: node->offset + allocSize, size: levelNodeSize - allocSize); |
10949 | } |
10950 | } |
10951 | break; |
10952 | case Node::TYPE_SPLIT: |
10953 | { |
10954 | const VkDeviceSize childrenNodeSize = levelNodeSize / 2; |
10955 | const Node* const leftChild = node->split.leftChild; |
10956 | PrintDetailedMapNode(json, node: leftChild, levelNodeSize: childrenNodeSize); |
10957 | const Node* const rightChild = leftChild->buddy; |
10958 | PrintDetailedMapNode(json, node: rightChild, levelNodeSize: childrenNodeSize); |
10959 | } |
10960 | break; |
10961 | default: |
10962 | VMA_ASSERT(0); |
10963 | } |
10964 | } |
10965 | #endif // #if VMA_STATS_STRING_ENABLED |
10966 | |
10967 | |
10968 | //////////////////////////////////////////////////////////////////////////////// |
10969 | // class VmaDeviceMemoryBlock |
10970 | |
10971 | VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator /*hAllocator*/) : |
10972 | m_pMetadata(VMA_NULL), |
10973 | m_MemoryTypeIndex(UINT32_MAX), |
10974 | m_Id(0), |
10975 | m_hMemory(VK_NULL_HANDLE), |
10976 | m_MapCount(0), |
10977 | m_pMappedData(VMA_NULL) |
10978 | { |
10979 | } |
10980 | |
10981 | void VmaDeviceMemoryBlock::Init( |
10982 | VmaAllocator hAllocator, |
10983 | uint32_t newMemoryTypeIndex, |
10984 | VkDeviceMemory newMemory, |
10985 | VkDeviceSize newSize, |
10986 | uint32_t id, |
10987 | uint32_t algorithm) |
10988 | { |
10989 | VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); |
10990 | |
10991 | m_MemoryTypeIndex = newMemoryTypeIndex; |
10992 | m_Id = id; |
10993 | m_hMemory = newMemory; |
10994 | |
10995 | switch(algorithm) |
10996 | { |
10997 | case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: |
10998 | m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator); |
10999 | break; |
11000 | case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT: |
11001 | m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator); |
11002 | break; |
11003 | default: |
11004 | VMA_ASSERT(0); |
11005 | // Fall-through. |
11006 | case 0: |
11007 | m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator); |
11008 | } |
11009 | m_pMetadata->Init(size: newSize); |
11010 | } |
11011 | |
11012 | void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator) |
11013 | { |
11014 | // This is the most important assert in the entire library. |
11015 | // Hitting it means you have some memory leak - unreleased VmaAllocation objects. |
11016 | VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!" ); |
11017 | |
11018 | VMA_ASSERT(m_hMemory != VK_NULL_HANDLE); |
11019 | allocator->FreeVulkanMemory(memoryType: m_MemoryTypeIndex, size: m_pMetadata->GetSize(), hMemory: m_hMemory); |
11020 | m_hMemory = VK_NULL_HANDLE; |
11021 | |
11022 | vma_delete(hAllocator: allocator, ptr: m_pMetadata); |
11023 | m_pMetadata = VMA_NULL; |
11024 | } |
11025 | |
11026 | bool VmaDeviceMemoryBlock::Validate() const |
11027 | { |
11028 | VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) && |
11029 | (m_pMetadata->GetSize() != 0)); |
11030 | |
11031 | return m_pMetadata->Validate(); |
11032 | } |
11033 | |
11034 | VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator) |
11035 | { |
11036 | void* pData = nullptr; |
11037 | VkResult res = Map(hAllocator, count: 1, ppData: &pData); |
11038 | if(res != VK_SUCCESS) |
11039 | { |
11040 | return res; |
11041 | } |
11042 | |
11043 | res = m_pMetadata->CheckCorruption(pBlockData: pData); |
11044 | |
11045 | Unmap(hAllocator, count: 1); |
11046 | |
11047 | return res; |
11048 | } |
11049 | |
11050 | VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData) |
11051 | { |
11052 | if(count == 0) |
11053 | { |
11054 | return VK_SUCCESS; |
11055 | } |
11056 | |
11057 | VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); |
11058 | if(m_MapCount != 0) |
11059 | { |
11060 | m_MapCount += count; |
11061 | VMA_ASSERT(m_pMappedData != VMA_NULL); |
11062 | if(ppData != VMA_NULL) |
11063 | { |
11064 | *ppData = m_pMappedData; |
11065 | } |
11066 | return VK_SUCCESS; |
11067 | } |
11068 | else |
11069 | { |
11070 | VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( |
11071 | hAllocator->m_hDevice, |
11072 | m_hMemory, |
11073 | 0, // offset |
11074 | VK_WHOLE_SIZE, |
11075 | 0, // flags |
11076 | &m_pMappedData); |
11077 | if(result == VK_SUCCESS) |
11078 | { |
11079 | if(ppData != VMA_NULL) |
11080 | { |
11081 | *ppData = m_pMappedData; |
11082 | } |
11083 | m_MapCount = count; |
11084 | } |
11085 | return result; |
11086 | } |
11087 | } |
11088 | |
11089 | void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count) |
11090 | { |
11091 | if(count == 0) |
11092 | { |
11093 | return; |
11094 | } |
11095 | |
11096 | VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); |
11097 | if(m_MapCount >= count) |
11098 | { |
11099 | m_MapCount -= count; |
11100 | if(m_MapCount == 0) |
11101 | { |
11102 | m_pMappedData = VMA_NULL; |
11103 | (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); |
11104 | } |
11105 | } |
11106 | else |
11107 | { |
11108 | VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped." ); |
11109 | } |
11110 | } |
11111 | |
11112 | VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) |
11113 | { |
11114 | VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); |
11115 | VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN); |
11116 | |
11117 | void* pData; |
11118 | VkResult res = Map(hAllocator, count: 1, ppData: &pData); |
11119 | if(res != VK_SUCCESS) |
11120 | { |
11121 | return res; |
11122 | } |
11123 | |
11124 | VmaWriteMagicValue(pData, offset: allocOffset - VMA_DEBUG_MARGIN); |
11125 | VmaWriteMagicValue(pData, offset: allocOffset + allocSize); |
11126 | |
11127 | Unmap(hAllocator, count: 1); |
11128 | |
11129 | return VK_SUCCESS; |
11130 | } |
11131 | |
11132 | VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) |
11133 | { |
11134 | VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); |
11135 | VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN); |
11136 | |
11137 | void* pData; |
11138 | VkResult res = Map(hAllocator, count: 1, ppData: &pData); |
11139 | if(res != VK_SUCCESS) |
11140 | { |
11141 | return res; |
11142 | } |
11143 | |
11144 | if(!VmaValidateMagicValue(pData, offset: allocOffset - VMA_DEBUG_MARGIN)) |
11145 | { |
11146 | VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!" ); |
11147 | } |
11148 | else if(!VmaValidateMagicValue(pData, offset: allocOffset + allocSize)) |
11149 | { |
11150 | VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!" ); |
11151 | } |
11152 | |
11153 | Unmap(hAllocator, count: 1); |
11154 | |
11155 | return VK_SUCCESS; |
11156 | } |
11157 | |
11158 | VkResult VmaDeviceMemoryBlock::BindBufferMemory( |
11159 | const VmaAllocator hAllocator, |
11160 | const VmaAllocation hAllocation, |
11161 | VkBuffer hBuffer) |
11162 | { |
11163 | VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && |
11164 | hAllocation->GetBlock() == this); |
11165 | // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. |
11166 | VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); |
11167 | return hAllocator->GetVulkanFunctions().vkBindBufferMemory( |
11168 | hAllocator->m_hDevice, |
11169 | hBuffer, |
11170 | m_hMemory, |
11171 | hAllocation->GetOffset()); |
11172 | } |
11173 | |
11174 | VkResult VmaDeviceMemoryBlock::BindImageMemory( |
11175 | const VmaAllocator hAllocator, |
11176 | const VmaAllocation hAllocation, |
11177 | VkImage hImage) |
11178 | { |
11179 | VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && |
11180 | hAllocation->GetBlock() == this); |
11181 | // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. |
11182 | VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); |
11183 | return hAllocator->GetVulkanFunctions().vkBindImageMemory( |
11184 | hAllocator->m_hDevice, |
11185 | hImage, |
11186 | m_hMemory, |
11187 | hAllocation->GetOffset()); |
11188 | } |
11189 | |
11190 | static void InitStatInfo(VmaStatInfo& outInfo) |
11191 | { |
11192 | memset(s: &outInfo, c: 0, n: sizeof(outInfo)); |
11193 | outInfo.allocationSizeMin = UINT64_MAX; |
11194 | outInfo.unusedRangeSizeMin = UINT64_MAX; |
11195 | } |
11196 | |
11197 | // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo. |
11198 | static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo) |
11199 | { |
11200 | inoutInfo.blockCount += srcInfo.blockCount; |
11201 | inoutInfo.allocationCount += srcInfo.allocationCount; |
11202 | inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount; |
11203 | inoutInfo.usedBytes += srcInfo.usedBytes; |
11204 | inoutInfo.unusedBytes += srcInfo.unusedBytes; |
11205 | inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin); |
11206 | inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax); |
11207 | inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin); |
11208 | inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax); |
11209 | } |
11210 | |
11211 | static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo) |
11212 | { |
11213 | inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ? |
11214 | VmaRoundDiv<VkDeviceSize>(x: inoutInfo.usedBytes, y: inoutInfo.allocationCount) : 0; |
11215 | inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ? |
11216 | VmaRoundDiv<VkDeviceSize>(x: inoutInfo.unusedBytes, y: inoutInfo.unusedRangeCount) : 0; |
11217 | } |
11218 | |
11219 | VmaPool_T::VmaPool_T( |
11220 | VmaAllocator hAllocator, |
11221 | const VmaPoolCreateInfo& createInfo, |
11222 | VkDeviceSize preferredBlockSize) : |
11223 | m_BlockVector( |
11224 | hAllocator, |
11225 | createInfo.memoryTypeIndex, |
11226 | createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize, |
11227 | createInfo.minBlockCount, |
11228 | createInfo.maxBlockCount, |
11229 | (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(), |
11230 | createInfo.frameInUseCount, |
11231 | true, // isCustomPool |
11232 | createInfo.blockSize != 0, // explicitBlockSize |
11233 | createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm |
11234 | m_Id(0) |
11235 | { |
11236 | } |
11237 | |
11238 | VmaPool_T::~VmaPool_T() |
11239 | { |
11240 | } |
11241 | |
11242 | #if VMA_STATS_STRING_ENABLED |
11243 | |
11244 | #endif // #if VMA_STATS_STRING_ENABLED |
11245 | |
11246 | VmaBlockVector::VmaBlockVector( |
11247 | VmaAllocator hAllocator, |
11248 | uint32_t memoryTypeIndex, |
11249 | VkDeviceSize preferredBlockSize, |
11250 | size_t minBlockCount, |
11251 | size_t maxBlockCount, |
11252 | VkDeviceSize bufferImageGranularity, |
11253 | uint32_t frameInUseCount, |
11254 | bool isCustomPool, |
11255 | bool explicitBlockSize, |
11256 | uint32_t algorithm) : |
11257 | m_hAllocator(hAllocator), |
11258 | m_MemoryTypeIndex(memoryTypeIndex), |
11259 | m_PreferredBlockSize(preferredBlockSize), |
11260 | m_MinBlockCount(minBlockCount), |
11261 | m_MaxBlockCount(maxBlockCount), |
11262 | m_BufferImageGranularity(bufferImageGranularity), |
11263 | m_FrameInUseCount(frameInUseCount), |
11264 | m_IsCustomPool(isCustomPool), |
11265 | m_ExplicitBlockSize(explicitBlockSize), |
11266 | m_Algorithm(algorithm), |
11267 | m_HasEmptyBlock(false), |
11268 | m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())), |
11269 | m_NextBlockId(0) |
11270 | { |
11271 | } |
11272 | |
11273 | VmaBlockVector::~VmaBlockVector() |
11274 | { |
11275 | for(size_t i = m_Blocks.size(); i--; ) |
11276 | { |
11277 | m_Blocks[i]->Destroy(allocator: m_hAllocator); |
11278 | vma_delete(hAllocator: m_hAllocator, ptr: m_Blocks[i]); |
11279 | } |
11280 | } |
11281 | |
11282 | VkResult VmaBlockVector::CreateMinBlocks() |
11283 | { |
11284 | for(size_t i = 0; i < m_MinBlockCount; ++i) |
11285 | { |
11286 | VkResult res = CreateBlock(blockSize: m_PreferredBlockSize, VMA_NULL); |
11287 | if(res != VK_SUCCESS) |
11288 | { |
11289 | return res; |
11290 | } |
11291 | } |
11292 | return VK_SUCCESS; |
11293 | } |
11294 | |
11295 | void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats) |
11296 | { |
11297 | VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); |
11298 | |
11299 | const size_t blockCount = m_Blocks.size(); |
11300 | |
11301 | pStats->size = 0; |
11302 | pStats->unusedSize = 0; |
11303 | pStats->allocationCount = 0; |
11304 | pStats->unusedRangeCount = 0; |
11305 | pStats->unusedRangeSizeMax = 0; |
11306 | pStats->blockCount = blockCount; |
11307 | |
11308 | for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) |
11309 | { |
11310 | const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; |
11311 | VMA_ASSERT(pBlock); |
11312 | VMA_HEAVY_ASSERT(pBlock->Validate()); |
11313 | pBlock->m_pMetadata->AddPoolStats(inoutStats&: *pStats); |
11314 | } |
11315 | } |
11316 | |
11317 | bool VmaBlockVector::IsCorruptionDetectionEnabled() const |
11318 | { |
11319 | const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
11320 | return (VMA_DEBUG_DETECT_CORRUPTION != 0) && |
11321 | (VMA_DEBUG_MARGIN > 0) && |
11322 | (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags; |
11323 | } |
11324 | |
11325 | static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32; |
11326 | |
11327 | VkResult VmaBlockVector::Allocate( |
11328 | VmaPool hCurrentPool, |
11329 | uint32_t currentFrameIndex, |
11330 | VkDeviceSize size, |
11331 | VkDeviceSize alignment, |
11332 | const VmaAllocationCreateInfo& createInfo, |
11333 | VmaSuballocationType suballocType, |
11334 | size_t allocationCount, |
11335 | VmaAllocation* pAllocations) |
11336 | { |
11337 | size_t allocIndex; |
11338 | VkResult res = VK_SUCCESS; |
11339 | |
11340 | { |
11341 | VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); |
11342 | for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) |
11343 | { |
11344 | res = AllocatePage( |
11345 | hCurrentPool, |
11346 | currentFrameIndex, |
11347 | size, |
11348 | alignment, |
11349 | createInfo, |
11350 | suballocType, |
11351 | pAllocation: pAllocations + allocIndex); |
11352 | if(res != VK_SUCCESS) |
11353 | { |
11354 | break; |
11355 | } |
11356 | } |
11357 | } |
11358 | |
11359 | if(res != VK_SUCCESS) |
11360 | { |
11361 | // Free all already created allocations. |
11362 | while(allocIndex--) |
11363 | { |
11364 | Free(hAllocation: pAllocations[allocIndex]); |
11365 | } |
11366 | memset(s: pAllocations, c: 0, n: sizeof(VmaAllocation) * allocationCount); |
11367 | } |
11368 | |
11369 | return res; |
11370 | } |
11371 | |
11372 | VkResult VmaBlockVector::AllocatePage( |
11373 | VmaPool hCurrentPool, |
11374 | uint32_t currentFrameIndex, |
11375 | VkDeviceSize size, |
11376 | VkDeviceSize alignment, |
11377 | const VmaAllocationCreateInfo& createInfo, |
11378 | VmaSuballocationType suballocType, |
11379 | VmaAllocation* pAllocation) |
11380 | { |
11381 | const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; |
11382 | bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0; |
11383 | const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; |
11384 | const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; |
11385 | const bool canCreateNewBlock = |
11386 | ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) && |
11387 | (m_Blocks.size() < m_MaxBlockCount); |
11388 | uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK; |
11389 | |
11390 | // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer. |
11391 | // Which in turn is available only when maxBlockCount = 1. |
11392 | if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1) |
11393 | { |
11394 | canMakeOtherLost = false; |
11395 | } |
11396 | |
11397 | // Upper address can only be used with linear allocator and within single memory block. |
11398 | if(isUpperAddress && |
11399 | (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1)) |
11400 | { |
11401 | return VK_ERROR_FEATURE_NOT_PRESENT; |
11402 | } |
11403 | |
11404 | // Validate strategy. |
11405 | switch(strategy) |
11406 | { |
11407 | case 0: |
11408 | strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; |
11409 | break; |
11410 | case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT: |
11411 | case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT: |
11412 | case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT: |
11413 | break; |
11414 | default: |
11415 | return VK_ERROR_FEATURE_NOT_PRESENT; |
11416 | } |
11417 | |
11418 | // Early reject: requested allocation size is larger that maximum block size for this block vector. |
11419 | if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize) |
11420 | { |
11421 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
11422 | } |
11423 | |
11424 | /* |
11425 | Under certain condition, this whole section can be skipped for optimization, so |
11426 | we move on directly to trying to allocate with canMakeOtherLost. That's the case |
11427 | e.g. for custom pools with linear algorithm. |
11428 | */ |
11429 | if(!canMakeOtherLost || canCreateNewBlock) |
11430 | { |
11431 | // 1. Search existing allocations. Try to allocate without making other allocations lost. |
11432 | VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags; |
11433 | allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT; |
11434 | |
11435 | if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) |
11436 | { |
11437 | // Use only last block. |
11438 | if(!m_Blocks.empty()) |
11439 | { |
11440 | VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back(); |
11441 | VMA_ASSERT(pCurrBlock); |
11442 | VkResult res = AllocateFromBlock( |
11443 | pBlock: pCurrBlock, |
11444 | hCurrentPool, |
11445 | currentFrameIndex, |
11446 | size, |
11447 | alignment, |
11448 | allocFlags: allocFlagsCopy, |
11449 | pUserData: createInfo.pUserData, |
11450 | suballocType, |
11451 | strategy, |
11452 | pAllocation); |
11453 | if(res == VK_SUCCESS) |
11454 | { |
11455 | VMA_DEBUG_LOG(" Returned from last block #%u" , (uint32_t)(m_Blocks.size() - 1)); |
11456 | return VK_SUCCESS; |
11457 | } |
11458 | } |
11459 | } |
11460 | else |
11461 | { |
11462 | if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) |
11463 | { |
11464 | // Forward order in m_Blocks - prefer blocks with smallest amount of free space. |
11465 | for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex ) |
11466 | { |
11467 | VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; |
11468 | VMA_ASSERT(pCurrBlock); |
11469 | VkResult res = AllocateFromBlock( |
11470 | pBlock: pCurrBlock, |
11471 | hCurrentPool, |
11472 | currentFrameIndex, |
11473 | size, |
11474 | alignment, |
11475 | allocFlags: allocFlagsCopy, |
11476 | pUserData: createInfo.pUserData, |
11477 | suballocType, |
11478 | strategy, |
11479 | pAllocation); |
11480 | if(res == VK_SUCCESS) |
11481 | { |
11482 | VMA_DEBUG_LOG(" Returned from existing block #%u" , (uint32_t)blockIndex); |
11483 | return VK_SUCCESS; |
11484 | } |
11485 | } |
11486 | } |
11487 | else // WORST_FIT, FIRST_FIT |
11488 | { |
11489 | // Backward order in m_Blocks - prefer blocks with largest amount of free space. |
11490 | for(size_t blockIndex = m_Blocks.size(); blockIndex--; ) |
11491 | { |
11492 | VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; |
11493 | VMA_ASSERT(pCurrBlock); |
11494 | VkResult res = AllocateFromBlock( |
11495 | pBlock: pCurrBlock, |
11496 | hCurrentPool, |
11497 | currentFrameIndex, |
11498 | size, |
11499 | alignment, |
11500 | allocFlags: allocFlagsCopy, |
11501 | pUserData: createInfo.pUserData, |
11502 | suballocType, |
11503 | strategy, |
11504 | pAllocation); |
11505 | if(res == VK_SUCCESS) |
11506 | { |
11507 | VMA_DEBUG_LOG(" Returned from existing block #%u" , (uint32_t)blockIndex); |
11508 | return VK_SUCCESS; |
11509 | } |
11510 | } |
11511 | } |
11512 | } |
11513 | |
11514 | // 2. Try to create new block. |
11515 | if(canCreateNewBlock) |
11516 | { |
11517 | // Calculate optimal size for new block. |
11518 | VkDeviceSize newBlockSize = m_PreferredBlockSize; |
11519 | uint32_t newBlockSizeShift = 0; |
11520 | const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3; |
11521 | |
11522 | if(!m_ExplicitBlockSize) |
11523 | { |
11524 | // Allocate 1/8, 1/4, 1/2 as first blocks. |
11525 | const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize(); |
11526 | for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i) |
11527 | { |
11528 | const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; |
11529 | if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2) |
11530 | { |
11531 | newBlockSize = smallerNewBlockSize; |
11532 | ++newBlockSizeShift; |
11533 | } |
11534 | else |
11535 | { |
11536 | break; |
11537 | } |
11538 | } |
11539 | } |
11540 | |
11541 | size_t newBlockIndex = 0; |
11542 | VkResult res = CreateBlock(blockSize: newBlockSize, pNewBlockIndex: &newBlockIndex); |
11543 | // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize. |
11544 | if(!m_ExplicitBlockSize) |
11545 | { |
11546 | while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX) |
11547 | { |
11548 | const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; |
11549 | if(smallerNewBlockSize >= size) |
11550 | { |
11551 | newBlockSize = smallerNewBlockSize; |
11552 | ++newBlockSizeShift; |
11553 | res = CreateBlock(blockSize: newBlockSize, pNewBlockIndex: &newBlockIndex); |
11554 | } |
11555 | else |
11556 | { |
11557 | break; |
11558 | } |
11559 | } |
11560 | } |
11561 | |
11562 | if(res == VK_SUCCESS) |
11563 | { |
11564 | VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex]; |
11565 | VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size); |
11566 | |
11567 | res = AllocateFromBlock( |
11568 | pBlock, |
11569 | hCurrentPool, |
11570 | currentFrameIndex, |
11571 | size, |
11572 | alignment, |
11573 | allocFlags: allocFlagsCopy, |
11574 | pUserData: createInfo.pUserData, |
11575 | suballocType, |
11576 | strategy, |
11577 | pAllocation); |
11578 | if(res == VK_SUCCESS) |
11579 | { |
11580 | VMA_DEBUG_LOG(" Created new block Size=%llu" , newBlockSize); |
11581 | return VK_SUCCESS; |
11582 | } |
11583 | else |
11584 | { |
11585 | // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment. |
11586 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
11587 | } |
11588 | } |
11589 | } |
11590 | } |
11591 | |
11592 | // 3. Try to allocate from existing blocks with making other allocations lost. |
11593 | if(canMakeOtherLost) |
11594 | { |
11595 | uint32_t tryIndex = 0; |
11596 | for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex) |
11597 | { |
11598 | VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL; |
11599 | VmaAllocationRequest bestRequest = {}; |
11600 | VkDeviceSize bestRequestCost = VK_WHOLE_SIZE; |
11601 | |
11602 | // 1. Search existing allocations. |
11603 | if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) |
11604 | { |
11605 | // Forward order in m_Blocks - prefer blocks with smallest amount of free space. |
11606 | for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex ) |
11607 | { |
11608 | VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; |
11609 | VMA_ASSERT(pCurrBlock); |
11610 | VmaAllocationRequest currRequest = {}; |
11611 | if(pCurrBlock->m_pMetadata->CreateAllocationRequest( |
11612 | currentFrameIndex, |
11613 | frameInUseCount: m_FrameInUseCount, |
11614 | bufferImageGranularity: m_BufferImageGranularity, |
11615 | allocSize: size, |
11616 | allocAlignment: alignment, |
11617 | upperAddress: (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, |
11618 | allocType: suballocType, |
11619 | canMakeOtherLost, |
11620 | strategy, |
11621 | pAllocationRequest: &currRequest)) |
11622 | { |
11623 | const VkDeviceSize currRequestCost = currRequest.CalcCost(); |
11624 | if(pBestRequestBlock == VMA_NULL || |
11625 | currRequestCost < bestRequestCost) |
11626 | { |
11627 | pBestRequestBlock = pCurrBlock; |
11628 | bestRequest = currRequest; |
11629 | bestRequestCost = currRequestCost; |
11630 | |
11631 | if(bestRequestCost == 0) |
11632 | { |
11633 | break; |
11634 | } |
11635 | } |
11636 | } |
11637 | } |
11638 | } |
11639 | else // WORST_FIT, FIRST_FIT |
11640 | { |
11641 | // Backward order in m_Blocks - prefer blocks with largest amount of free space. |
11642 | for(size_t blockIndex = m_Blocks.size(); blockIndex--; ) |
11643 | { |
11644 | VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; |
11645 | VMA_ASSERT(pCurrBlock); |
11646 | VmaAllocationRequest currRequest = {}; |
11647 | if(pCurrBlock->m_pMetadata->CreateAllocationRequest( |
11648 | currentFrameIndex, |
11649 | frameInUseCount: m_FrameInUseCount, |
11650 | bufferImageGranularity: m_BufferImageGranularity, |
11651 | allocSize: size, |
11652 | allocAlignment: alignment, |
11653 | upperAddress: (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, |
11654 | allocType: suballocType, |
11655 | canMakeOtherLost, |
11656 | strategy, |
11657 | pAllocationRequest: &currRequest)) |
11658 | { |
11659 | const VkDeviceSize currRequestCost = currRequest.CalcCost(); |
11660 | if(pBestRequestBlock == VMA_NULL || |
11661 | currRequestCost < bestRequestCost || |
11662 | strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT) |
11663 | { |
11664 | pBestRequestBlock = pCurrBlock; |
11665 | bestRequest = currRequest; |
11666 | bestRequestCost = currRequestCost; |
11667 | |
11668 | if(bestRequestCost == 0 || |
11669 | strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT) |
11670 | { |
11671 | break; |
11672 | } |
11673 | } |
11674 | } |
11675 | } |
11676 | } |
11677 | |
11678 | if(pBestRequestBlock != VMA_NULL) |
11679 | { |
11680 | if(mapped) |
11681 | { |
11682 | VkResult res = pBestRequestBlock->Map(hAllocator: m_hAllocator, count: 1, VMA_NULL); |
11683 | if(res != VK_SUCCESS) |
11684 | { |
11685 | return res; |
11686 | } |
11687 | } |
11688 | |
11689 | if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost( |
11690 | currentFrameIndex, |
11691 | frameInUseCount: m_FrameInUseCount, |
11692 | pAllocationRequest: &bestRequest)) |
11693 | { |
11694 | // We no longer have an empty Allocation. |
11695 | if(pBestRequestBlock->m_pMetadata->IsEmpty()) |
11696 | { |
11697 | m_HasEmptyBlock = false; |
11698 | } |
11699 | // Allocate from this pBlock. |
11700 | *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString); |
11701 | pBestRequestBlock->m_pMetadata->Alloc(request: bestRequest, type: suballocType, allocSize: size, upperAddress: isUpperAddress, hAllocation: *pAllocation); |
11702 | (*pAllocation)->InitBlockAllocation( |
11703 | hPool: hCurrentPool, |
11704 | block: pBestRequestBlock, |
11705 | offset: bestRequest.offset, |
11706 | alignment, |
11707 | size, |
11708 | suballocationType: suballocType, |
11709 | mapped, |
11710 | canBecomeLost: (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0); |
11711 | VMA_HEAVY_ASSERT(pBestRequestBlock->Validate()); |
11712 | VMA_DEBUG_LOG(" Returned from existing allocation #%u" , (uint32_t)blockIndex); |
11713 | (*pAllocation)->SetUserData(hAllocator: m_hAllocator, pUserData: createInfo.pUserData); |
11714 | if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) |
11715 | { |
11716 | m_hAllocator->FillAllocation(hAllocation: *pAllocation, pattern: VMA_ALLOCATION_FILL_PATTERN_CREATED); |
11717 | } |
11718 | if(IsCorruptionDetectionEnabled()) |
11719 | { |
11720 | VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(hAllocator: m_hAllocator, allocOffset: bestRequest.offset, allocSize: size); |
11721 | (void) res; |
11722 | VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value." ); |
11723 | } |
11724 | return VK_SUCCESS; |
11725 | } |
11726 | // else: Some allocations must have been touched while we are here. Next try. |
11727 | } |
11728 | else |
11729 | { |
11730 | // Could not find place in any of the blocks - break outer loop. |
11731 | break; |
11732 | } |
11733 | } |
11734 | /* Maximum number of tries exceeded - a very unlike event when many other |
11735 | threads are simultaneously touching allocations making it impossible to make |
11736 | lost at the same time as we try to allocate. */ |
11737 | if(tryIndex == VMA_ALLOCATION_TRY_COUNT) |
11738 | { |
11739 | return VK_ERROR_TOO_MANY_OBJECTS; |
11740 | } |
11741 | } |
11742 | |
11743 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
11744 | } |
11745 | |
11746 | void VmaBlockVector::Free( |
11747 | VmaAllocation hAllocation) |
11748 | { |
11749 | VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL; |
11750 | |
11751 | // Scope for lock. |
11752 | { |
11753 | VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); |
11754 | |
11755 | VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); |
11756 | |
11757 | if(IsCorruptionDetectionEnabled()) |
11758 | { |
11759 | VkResult res = pBlock->ValidateMagicValueAroundAllocation(hAllocator: m_hAllocator, allocOffset: hAllocation->GetOffset(), allocSize: hAllocation->GetSize()); |
11760 | (void) res; |
11761 | VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value." ); |
11762 | } |
11763 | |
11764 | if(hAllocation->IsPersistentMap()) |
11765 | { |
11766 | pBlock->Unmap(hAllocator: m_hAllocator, count: 1); |
11767 | } |
11768 | |
11769 | pBlock->m_pMetadata->Free(allocation: hAllocation); |
11770 | VMA_HEAVY_ASSERT(pBlock->Validate()); |
11771 | |
11772 | VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u" , memTypeIndex); |
11773 | |
11774 | // pBlock became empty after this deallocation. |
11775 | if(pBlock->m_pMetadata->IsEmpty()) |
11776 | { |
11777 | // Already has empty Allocation. We don't want to have two, so delete this one. |
11778 | if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount) |
11779 | { |
11780 | pBlockToDelete = pBlock; |
11781 | Remove(pBlock); |
11782 | } |
11783 | // We now have first empty block. |
11784 | else |
11785 | { |
11786 | m_HasEmptyBlock = true; |
11787 | } |
11788 | } |
11789 | // pBlock didn't become empty, but we have another empty block - find and free that one. |
11790 | // (This is optional, heuristics.) |
11791 | else if(m_HasEmptyBlock) |
11792 | { |
11793 | VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back(); |
11794 | if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount) |
11795 | { |
11796 | pBlockToDelete = pLastBlock; |
11797 | m_Blocks.pop_back(); |
11798 | m_HasEmptyBlock = false; |
11799 | } |
11800 | } |
11801 | |
11802 | IncrementallySortBlocks(); |
11803 | } |
11804 | |
11805 | // Destruction of a free Allocation. Deferred until this point, outside of mutex |
11806 | // lock, for performance reason. |
11807 | if(pBlockToDelete != VMA_NULL) |
11808 | { |
11809 | VMA_DEBUG_LOG(" Deleted empty allocation" ); |
11810 | pBlockToDelete->Destroy(allocator: m_hAllocator); |
11811 | vma_delete(hAllocator: m_hAllocator, ptr: pBlockToDelete); |
11812 | } |
11813 | } |
11814 | |
11815 | VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const |
11816 | { |
11817 | VkDeviceSize result = 0; |
11818 | for(size_t i = m_Blocks.size(); i--; ) |
11819 | { |
11820 | result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize()); |
11821 | if(result >= m_PreferredBlockSize) |
11822 | { |
11823 | break; |
11824 | } |
11825 | } |
11826 | return result; |
11827 | } |
11828 | |
11829 | void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock) |
11830 | { |
11831 | for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) |
11832 | { |
11833 | if(m_Blocks[blockIndex] == pBlock) |
11834 | { |
11835 | VmaVectorRemove(vec&: m_Blocks, index: blockIndex); |
11836 | return; |
11837 | } |
11838 | } |
11839 | VMA_ASSERT(0); |
11840 | } |
11841 | |
11842 | void VmaBlockVector::IncrementallySortBlocks() |
11843 | { |
11844 | if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) |
11845 | { |
11846 | // Bubble sort only until first swap. |
11847 | for(size_t i = 1; i < m_Blocks.size(); ++i) |
11848 | { |
11849 | if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize()) |
11850 | { |
11851 | VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]); |
11852 | return; |
11853 | } |
11854 | } |
11855 | } |
11856 | } |
11857 | |
11858 | VkResult VmaBlockVector::AllocateFromBlock( |
11859 | VmaDeviceMemoryBlock* pBlock, |
11860 | VmaPool hCurrentPool, |
11861 | uint32_t currentFrameIndex, |
11862 | VkDeviceSize size, |
11863 | VkDeviceSize alignment, |
11864 | VmaAllocationCreateFlags allocFlags, |
11865 | void* pUserData, |
11866 | VmaSuballocationType suballocType, |
11867 | uint32_t strategy, |
11868 | VmaAllocation* pAllocation) |
11869 | { |
11870 | VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0); |
11871 | const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; |
11872 | const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; |
11873 | const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; |
11874 | |
11875 | VmaAllocationRequest currRequest = {}; |
11876 | if(pBlock->m_pMetadata->CreateAllocationRequest( |
11877 | currentFrameIndex, |
11878 | frameInUseCount: m_FrameInUseCount, |
11879 | bufferImageGranularity: m_BufferImageGranularity, |
11880 | allocSize: size, |
11881 | allocAlignment: alignment, |
11882 | upperAddress: isUpperAddress, |
11883 | allocType: suballocType, |
11884 | canMakeOtherLost: false, // canMakeOtherLost |
11885 | strategy, |
11886 | pAllocationRequest: &currRequest)) |
11887 | { |
11888 | // Allocate from pCurrBlock. |
11889 | VMA_ASSERT(currRequest.itemsToMakeLostCount == 0); |
11890 | |
11891 | if(mapped) |
11892 | { |
11893 | VkResult res = pBlock->Map(hAllocator: m_hAllocator, count: 1, VMA_NULL); |
11894 | if(res != VK_SUCCESS) |
11895 | { |
11896 | return res; |
11897 | } |
11898 | } |
11899 | |
11900 | // We no longer have an empty Allocation. |
11901 | if(pBlock->m_pMetadata->IsEmpty()) |
11902 | { |
11903 | m_HasEmptyBlock = false; |
11904 | } |
11905 | |
11906 | *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString); |
11907 | pBlock->m_pMetadata->Alloc(request: currRequest, type: suballocType, allocSize: size, upperAddress: isUpperAddress, hAllocation: *pAllocation); |
11908 | (*pAllocation)->InitBlockAllocation( |
11909 | hPool: hCurrentPool, |
11910 | block: pBlock, |
11911 | offset: currRequest.offset, |
11912 | alignment, |
11913 | size, |
11914 | suballocationType: suballocType, |
11915 | mapped, |
11916 | canBecomeLost: (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0); |
11917 | VMA_HEAVY_ASSERT(pBlock->Validate()); |
11918 | (*pAllocation)->SetUserData(hAllocator: m_hAllocator, pUserData); |
11919 | if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) |
11920 | { |
11921 | m_hAllocator->FillAllocation(hAllocation: *pAllocation, pattern: VMA_ALLOCATION_FILL_PATTERN_CREATED); |
11922 | } |
11923 | if(IsCorruptionDetectionEnabled()) |
11924 | { |
11925 | VkResult res = pBlock->WriteMagicValueAroundAllocation(hAllocator: m_hAllocator, allocOffset: currRequest.offset, allocSize: size); |
11926 | (void) res; |
11927 | VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value." ); |
11928 | } |
11929 | return VK_SUCCESS; |
11930 | } |
11931 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
11932 | } |
11933 | |
11934 | VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex) |
11935 | { |
11936 | VkMemoryAllocateInfo allocInfo = {}; |
11937 | allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
11938 | allocInfo.memoryTypeIndex = m_MemoryTypeIndex; |
11939 | allocInfo.allocationSize = blockSize; |
11940 | VkDeviceMemory mem = VK_NULL_HANDLE; |
11941 | VkResult res = m_hAllocator->AllocateVulkanMemory(pAllocateInfo: &allocInfo, pMemory: &mem); |
11942 | if(res < 0) |
11943 | { |
11944 | return res; |
11945 | } |
11946 | |
11947 | // New VkDeviceMemory successfully created. |
11948 | |
11949 | // Create new Allocation for it. |
11950 | VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator); |
11951 | pBlock->Init( |
11952 | hAllocator: m_hAllocator, |
11953 | newMemoryTypeIndex: m_MemoryTypeIndex, |
11954 | newMemory: mem, |
11955 | newSize: allocInfo.allocationSize, |
11956 | id: m_NextBlockId++, |
11957 | algorithm: m_Algorithm); |
11958 | |
11959 | m_Blocks.push_back(src: pBlock); |
11960 | if(pNewBlockIndex != VMA_NULL) |
11961 | { |
11962 | *pNewBlockIndex = m_Blocks.size() - 1; |
11963 | } |
11964 | |
11965 | return VK_SUCCESS; |
11966 | } |
11967 | |
11968 | void VmaBlockVector::ApplyDefragmentationMovesCpu( |
11969 | class VmaBlockVectorDefragmentationContext* pDefragCtx, |
11970 | const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves) |
11971 | { |
11972 | const size_t blockCount = m_Blocks.size(); |
11973 | const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(memTypeIndex: m_MemoryTypeIndex); |
11974 | |
11975 | enum BLOCK_FLAG |
11976 | { |
11977 | BLOCK_FLAG_USED = 0x00000001, |
11978 | BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002, |
11979 | }; |
11980 | |
11981 | struct BlockInfo |
11982 | { |
11983 | uint32_t flags; |
11984 | void* pMappedData; |
11985 | }; |
11986 | VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > |
11987 | blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks())); |
11988 | memset(s: blockInfo.data(), c: 0, n: blockCount * sizeof(BlockInfo)); |
11989 | |
11990 | // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED. |
11991 | const size_t moveCount = moves.size(); |
11992 | for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) |
11993 | { |
11994 | const VmaDefragmentationMove& move = moves[moveIndex]; |
11995 | blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED; |
11996 | blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED; |
11997 | } |
11998 | |
11999 | VMA_ASSERT(pDefragCtx->res == VK_SUCCESS); |
12000 | |
12001 | // Go over all blocks. Get mapped pointer or map if necessary. |
12002 | for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex) |
12003 | { |
12004 | BlockInfo& currBlockInfo = blockInfo[blockIndex]; |
12005 | VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; |
12006 | if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0) |
12007 | { |
12008 | currBlockInfo.pMappedData = pBlock->GetMappedData(); |
12009 | // It is not originally mapped - map it. |
12010 | if(currBlockInfo.pMappedData == VMA_NULL) |
12011 | { |
12012 | pDefragCtx->res = pBlock->Map(hAllocator: m_hAllocator, count: 1, ppData: &currBlockInfo.pMappedData); |
12013 | if(pDefragCtx->res == VK_SUCCESS) |
12014 | { |
12015 | currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION; |
12016 | } |
12017 | } |
12018 | } |
12019 | } |
12020 | |
12021 | // Go over all moves. Do actual data transfer. |
12022 | if(pDefragCtx->res == VK_SUCCESS) |
12023 | { |
12024 | const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; |
12025 | VkMappedMemoryRange memRange = {}; |
12026 | memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; |
12027 | |
12028 | for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) |
12029 | { |
12030 | const VmaDefragmentationMove& move = moves[moveIndex]; |
12031 | |
12032 | const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex]; |
12033 | const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex]; |
12034 | |
12035 | VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData); |
12036 | |
12037 | // Invalidate source. |
12038 | if(isNonCoherent) |
12039 | { |
12040 | VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex]; |
12041 | memRange.memory = pSrcBlock->GetDeviceMemory(); |
12042 | memRange.offset = VmaAlignDown(val: move.srcOffset, align: nonCoherentAtomSize); |
12043 | memRange.size = VMA_MIN( |
12044 | VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize), |
12045 | pSrcBlock->m_pMetadata->GetSize() - memRange.offset); |
12046 | (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange); |
12047 | } |
12048 | |
12049 | // THE PLACE WHERE ACTUAL DATA COPY HAPPENS. |
12050 | memmove( |
12051 | dest: reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset, |
12052 | src: reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset, |
12053 | n: static_cast<size_t>(move.size)); |
12054 | |
12055 | if(IsCorruptionDetectionEnabled()) |
12056 | { |
12057 | VmaWriteMagicValue(pData: dstBlockInfo.pMappedData, offset: move.dstOffset - VMA_DEBUG_MARGIN); |
12058 | VmaWriteMagicValue(pData: dstBlockInfo.pMappedData, offset: move.dstOffset + move.size); |
12059 | } |
12060 | |
12061 | // Flush destination. |
12062 | if(isNonCoherent) |
12063 | { |
12064 | VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex]; |
12065 | memRange.memory = pDstBlock->GetDeviceMemory(); |
12066 | memRange.offset = VmaAlignDown(val: move.dstOffset, align: nonCoherentAtomSize); |
12067 | memRange.size = VMA_MIN( |
12068 | VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize), |
12069 | pDstBlock->m_pMetadata->GetSize() - memRange.offset); |
12070 | (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange); |
12071 | } |
12072 | } |
12073 | } |
12074 | |
12075 | // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation. |
12076 | // Regardless of pCtx->res == VK_SUCCESS. |
12077 | for(size_t blockIndex = blockCount; blockIndex--; ) |
12078 | { |
12079 | const BlockInfo& currBlockInfo = blockInfo[blockIndex]; |
12080 | if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0) |
12081 | { |
12082 | VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; |
12083 | pBlock->Unmap(hAllocator: m_hAllocator, count: 1); |
12084 | } |
12085 | } |
12086 | } |
12087 | |
12088 | void VmaBlockVector::ApplyDefragmentationMovesGpu( |
12089 | class VmaBlockVectorDefragmentationContext* pDefragCtx, |
12090 | const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
12091 | VkCommandBuffer commandBuffer) |
12092 | { |
12093 | const size_t blockCount = m_Blocks.size(); |
12094 | |
12095 | pDefragCtx->blockContexts.resize(newCount: blockCount); |
12096 | for (size_t i = 0; i < blockCount; ++i) |
12097 | pDefragCtx->blockContexts[i] = VmaBlockDefragmentationContext(); |
12098 | |
12099 | // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED. |
12100 | const size_t moveCount = moves.size(); |
12101 | for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) |
12102 | { |
12103 | const VmaDefragmentationMove& move = moves[moveIndex]; |
12104 | pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED; |
12105 | pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED; |
12106 | } |
12107 | |
12108 | VMA_ASSERT(pDefragCtx->res == VK_SUCCESS); |
12109 | |
12110 | // Go over all blocks. Create and bind buffer for whole block if necessary. |
12111 | { |
12112 | VkBufferCreateInfo bufCreateInfo = {}; |
12113 | bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
12114 | bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | |
12115 | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
12116 | |
12117 | for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex) |
12118 | { |
12119 | VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex]; |
12120 | VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; |
12121 | if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0) |
12122 | { |
12123 | bufCreateInfo.size = pBlock->m_pMetadata->GetSize(); |
12124 | pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)( |
12125 | m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer); |
12126 | if(pDefragCtx->res == VK_SUCCESS) |
12127 | { |
12128 | pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)( |
12129 | m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0); |
12130 | } |
12131 | } |
12132 | } |
12133 | } |
12134 | |
12135 | // Go over all moves. Post data transfer commands to command buffer. |
12136 | if(pDefragCtx->res == VK_SUCCESS) |
12137 | { |
12138 | /*const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; |
12139 | VkMappedMemoryRange memRange = {}; |
12140 | memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;*/ |
12141 | |
12142 | for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) |
12143 | { |
12144 | const VmaDefragmentationMove& move = moves[moveIndex]; |
12145 | |
12146 | const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex]; |
12147 | const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex]; |
12148 | |
12149 | VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer); |
12150 | |
12151 | VkBufferCopy region = { |
12152 | .srcOffset: move.srcOffset, |
12153 | .dstOffset: move.dstOffset, |
12154 | .size: move.size }; |
12155 | (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)( |
12156 | commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion); |
12157 | } |
12158 | } |
12159 | |
12160 | // Save buffers to defrag context for later destruction. |
12161 | if(pDefragCtx->res == VK_SUCCESS && moveCount > 0) |
12162 | { |
12163 | pDefragCtx->res = VK_NOT_READY; |
12164 | } |
12165 | } |
12166 | |
12167 | void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats) |
12168 | { |
12169 | m_HasEmptyBlock = false; |
12170 | for(size_t blockIndex = m_Blocks.size(); blockIndex--; ) |
12171 | { |
12172 | VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; |
12173 | if(pBlock->m_pMetadata->IsEmpty()) |
12174 | { |
12175 | if(m_Blocks.size() > m_MinBlockCount) |
12176 | { |
12177 | if(pDefragmentationStats != VMA_NULL) |
12178 | { |
12179 | ++pDefragmentationStats->deviceMemoryBlocksFreed; |
12180 | pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize(); |
12181 | } |
12182 | |
12183 | VmaVectorRemove(vec&: m_Blocks, index: blockIndex); |
12184 | pBlock->Destroy(allocator: m_hAllocator); |
12185 | vma_delete(hAllocator: m_hAllocator, ptr: pBlock); |
12186 | } |
12187 | else |
12188 | { |
12189 | m_HasEmptyBlock = true; |
12190 | } |
12191 | } |
12192 | } |
12193 | } |
12194 | |
12195 | #if VMA_STATS_STRING_ENABLED |
12196 | |
12197 | void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json) |
12198 | { |
12199 | VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); |
12200 | |
12201 | json.BeginObject(); |
12202 | |
12203 | if(m_IsCustomPool) |
12204 | { |
12205 | json.WriteString(pStr: "MemoryTypeIndex" ); |
12206 | json.WriteNumber(n: m_MemoryTypeIndex); |
12207 | |
12208 | json.WriteString(pStr: "BlockSize" ); |
12209 | json.WriteNumber(n: m_PreferredBlockSize); |
12210 | |
12211 | json.WriteString(pStr: "BlockCount" ); |
12212 | json.BeginObject(singleLine: true); |
12213 | if(m_MinBlockCount > 0) |
12214 | { |
12215 | json.WriteString(pStr: "Min" ); |
12216 | json.WriteNumber(n: (uint64_t)m_MinBlockCount); |
12217 | } |
12218 | if(m_MaxBlockCount < SIZE_MAX) |
12219 | { |
12220 | json.WriteString(pStr: "Max" ); |
12221 | json.WriteNumber(n: (uint64_t)m_MaxBlockCount); |
12222 | } |
12223 | json.WriteString(pStr: "Cur" ); |
12224 | json.WriteNumber(n: (uint64_t)m_Blocks.size()); |
12225 | json.EndObject(); |
12226 | |
12227 | if(m_FrameInUseCount > 0) |
12228 | { |
12229 | json.WriteString(pStr: "FrameInUseCount" ); |
12230 | json.WriteNumber(n: m_FrameInUseCount); |
12231 | } |
12232 | |
12233 | if(m_Algorithm != 0) |
12234 | { |
12235 | json.WriteString(pStr: "Algorithm" ); |
12236 | json.WriteString(pStr: VmaAlgorithmToStr(algorithm: m_Algorithm)); |
12237 | } |
12238 | } |
12239 | else |
12240 | { |
12241 | json.WriteString(pStr: "PreferredBlockSize" ); |
12242 | json.WriteNumber(n: m_PreferredBlockSize); |
12243 | } |
12244 | |
12245 | json.WriteString(pStr: "Blocks" ); |
12246 | json.BeginObject(); |
12247 | for(size_t i = 0; i < m_Blocks.size(); ++i) |
12248 | { |
12249 | json.BeginString(); |
12250 | json.ContinueString(n: m_Blocks[i]->GetId()); |
12251 | json.EndString(); |
12252 | |
12253 | m_Blocks[i]->m_pMetadata->PrintDetailedMap(json); |
12254 | } |
12255 | json.EndObject(); |
12256 | |
12257 | json.EndObject(); |
12258 | } |
12259 | |
12260 | #endif // #if VMA_STATS_STRING_ENABLED |
12261 | |
12262 | void VmaBlockVector::Defragment( |
12263 | class VmaBlockVectorDefragmentationContext* pCtx, |
12264 | VmaDefragmentationStats* pStats, |
12265 | VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove, |
12266 | VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove, |
12267 | VkCommandBuffer commandBuffer) |
12268 | { |
12269 | pCtx->res = VK_SUCCESS; |
12270 | |
12271 | const VkMemoryPropertyFlags memPropFlags = |
12272 | m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags; |
12273 | const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; |
12274 | const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0; |
12275 | |
12276 | const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 && |
12277 | isHostVisible; |
12278 | const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 && |
12279 | (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent)); |
12280 | |
12281 | // There are options to defragment this memory type. |
12282 | if(canDefragmentOnCpu || canDefragmentOnGpu) |
12283 | { |
12284 | bool defragmentOnGpu; |
12285 | // There is only one option to defragment this memory type. |
12286 | if(canDefragmentOnGpu != canDefragmentOnCpu) |
12287 | { |
12288 | defragmentOnGpu = canDefragmentOnGpu; |
12289 | } |
12290 | // Both options are available: Heuristics to choose the best one. |
12291 | else |
12292 | { |
12293 | defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 || |
12294 | m_hAllocator->IsIntegratedGpu(); |
12295 | } |
12296 | |
12297 | bool overlappingMoveSupported = !defragmentOnGpu; |
12298 | |
12299 | if(m_hAllocator->m_UseMutex) |
12300 | { |
12301 | m_Mutex.LockWrite(); |
12302 | pCtx->mutexLocked = true; |
12303 | } |
12304 | |
12305 | pCtx->Begin(overlappingMoveSupported); |
12306 | |
12307 | // Defragment. |
12308 | |
12309 | const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove; |
12310 | const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove; |
12311 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves = |
12312 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks())); |
12313 | pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove); |
12314 | |
12315 | // Accumulate statistics. |
12316 | if(pStats != VMA_NULL) |
12317 | { |
12318 | const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved(); |
12319 | const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved(); |
12320 | pStats->bytesMoved += bytesMoved; |
12321 | pStats->allocationsMoved += allocationsMoved; |
12322 | VMA_ASSERT(bytesMoved <= maxBytesToMove); |
12323 | VMA_ASSERT(allocationsMoved <= maxAllocationsToMove); |
12324 | if(defragmentOnGpu) |
12325 | { |
12326 | maxGpuBytesToMove -= bytesMoved; |
12327 | maxGpuAllocationsToMove -= allocationsMoved; |
12328 | } |
12329 | else |
12330 | { |
12331 | maxCpuBytesToMove -= bytesMoved; |
12332 | maxCpuAllocationsToMove -= allocationsMoved; |
12333 | } |
12334 | } |
12335 | |
12336 | if(pCtx->res >= VK_SUCCESS) |
12337 | { |
12338 | if(defragmentOnGpu) |
12339 | { |
12340 | ApplyDefragmentationMovesGpu(pDefragCtx: pCtx, moves, commandBuffer); |
12341 | } |
12342 | else |
12343 | { |
12344 | ApplyDefragmentationMovesCpu(pDefragCtx: pCtx, moves); |
12345 | } |
12346 | } |
12347 | } |
12348 | } |
12349 | |
12350 | void VmaBlockVector::DefragmentationEnd( |
12351 | class VmaBlockVectorDefragmentationContext* pCtx, |
12352 | VmaDefragmentationStats* pStats) |
12353 | { |
12354 | // Destroy buffers. |
12355 | for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; ) |
12356 | { |
12357 | VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex]; |
12358 | if(blockCtx.hBuffer) |
12359 | { |
12360 | (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)( |
12361 | m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks()); |
12362 | } |
12363 | } |
12364 | |
12365 | if(pCtx->res >= VK_SUCCESS) |
12366 | { |
12367 | FreeEmptyBlocks(pDefragmentationStats: pStats); |
12368 | } |
12369 | |
12370 | if(pCtx->mutexLocked) |
12371 | { |
12372 | VMA_ASSERT(m_hAllocator->m_UseMutex); |
12373 | m_Mutex.UnlockWrite(); |
12374 | } |
12375 | } |
12376 | |
12377 | size_t VmaBlockVector::CalcAllocationCount() const |
12378 | { |
12379 | size_t result = 0; |
12380 | for(size_t i = 0; i < m_Blocks.size(); ++i) |
12381 | { |
12382 | result += m_Blocks[i]->m_pMetadata->GetAllocationCount(); |
12383 | } |
12384 | return result; |
12385 | } |
12386 | |
12387 | bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const |
12388 | { |
12389 | if(m_BufferImageGranularity == 1) |
12390 | { |
12391 | return false; |
12392 | } |
12393 | VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE; |
12394 | for(size_t i = 0, count = m_Blocks.size(); i < count; ++i) |
12395 | { |
12396 | VmaDeviceMemoryBlock* const pBlock = m_Blocks[i]; |
12397 | VMA_ASSERT(m_Algorithm == 0); |
12398 | VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata; |
12399 | if(pMetadata->IsBufferImageGranularityConflictPossible(bufferImageGranularity: m_BufferImageGranularity, inOutPrevSuballocType&: lastSuballocType)) |
12400 | { |
12401 | return true; |
12402 | } |
12403 | } |
12404 | return false; |
12405 | } |
12406 | |
12407 | void VmaBlockVector::MakePoolAllocationsLost( |
12408 | uint32_t currentFrameIndex, |
12409 | size_t* pLostAllocationCount) |
12410 | { |
12411 | VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); |
12412 | size_t lostAllocationCount = 0; |
12413 | for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) |
12414 | { |
12415 | VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; |
12416 | VMA_ASSERT(pBlock); |
12417 | lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, frameInUseCount: m_FrameInUseCount); |
12418 | } |
12419 | if(pLostAllocationCount != VMA_NULL) |
12420 | { |
12421 | *pLostAllocationCount = lostAllocationCount; |
12422 | } |
12423 | } |
12424 | |
12425 | VkResult VmaBlockVector::CheckCorruption() |
12426 | { |
12427 | if(!IsCorruptionDetectionEnabled()) |
12428 | { |
12429 | return VK_ERROR_FEATURE_NOT_PRESENT; |
12430 | } |
12431 | |
12432 | VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); |
12433 | for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) |
12434 | { |
12435 | VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; |
12436 | VMA_ASSERT(pBlock); |
12437 | VkResult res = pBlock->CheckCorruption(hAllocator: m_hAllocator); |
12438 | if(res != VK_SUCCESS) |
12439 | { |
12440 | return res; |
12441 | } |
12442 | } |
12443 | return VK_SUCCESS; |
12444 | } |
12445 | |
12446 | void VmaBlockVector::AddStats(VmaStats* pStats) |
12447 | { |
12448 | const uint32_t memTypeIndex = m_MemoryTypeIndex; |
12449 | const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex); |
12450 | |
12451 | VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); |
12452 | |
12453 | for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) |
12454 | { |
12455 | const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; |
12456 | VMA_ASSERT(pBlock); |
12457 | VMA_HEAVY_ASSERT(pBlock->Validate()); |
12458 | VmaStatInfo allocationStatInfo; |
12459 | pBlock->m_pMetadata->CalcAllocationStatInfo(outInfo&: allocationStatInfo); |
12460 | VmaAddStatInfo(inoutInfo&: pStats->total, srcInfo: allocationStatInfo); |
12461 | VmaAddStatInfo(inoutInfo&: pStats->memoryType[memTypeIndex], srcInfo: allocationStatInfo); |
12462 | VmaAddStatInfo(inoutInfo&: pStats->memoryHeap[memHeapIndex], srcInfo: allocationStatInfo); |
12463 | } |
12464 | } |
12465 | |
12466 | //////////////////////////////////////////////////////////////////////////////// |
12467 | // VmaDefragmentationAlgorithm_Generic members definition |
12468 | |
12469 | VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic( |
12470 | VmaAllocator hAllocator, |
12471 | VmaBlockVector* pBlockVector, |
12472 | uint32_t currentFrameIndex, |
12473 | bool /*overlappingMoveSupported*/) : |
12474 | VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex), |
12475 | m_AllocationCount(0), |
12476 | m_AllAllocations(false), |
12477 | m_BytesMoved(0), |
12478 | m_AllocationsMoved(0), |
12479 | m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks())) |
12480 | { |
12481 | // Create block info for each block. |
12482 | const size_t blockCount = m_pBlockVector->m_Blocks.size(); |
12483 | for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) |
12484 | { |
12485 | BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks()); |
12486 | pBlockInfo->m_OriginalBlockIndex = blockIndex; |
12487 | pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex]; |
12488 | m_Blocks.push_back(src: pBlockInfo); |
12489 | } |
12490 | |
12491 | // Sort them by m_pBlock pointer value. |
12492 | VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess()); |
12493 | } |
12494 | |
12495 | VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic() |
12496 | { |
12497 | for(size_t i = m_Blocks.size(); i--; ) |
12498 | { |
12499 | vma_delete(hAllocator: m_hAllocator, ptr: m_Blocks[i]); |
12500 | } |
12501 | } |
12502 | |
12503 | void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) |
12504 | { |
12505 | // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost. |
12506 | if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST) |
12507 | { |
12508 | VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock(); |
12509 | BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(beg: m_Blocks.begin(), end: m_Blocks.end(), key: pBlock, cmp: BlockPointerLess()); |
12510 | if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock) |
12511 | { |
12512 | AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged); |
12513 | (*it)->m_Allocations.push_back(src: allocInfo); |
12514 | } |
12515 | else |
12516 | { |
12517 | VMA_ASSERT(0); |
12518 | } |
12519 | |
12520 | ++m_AllocationCount; |
12521 | } |
12522 | } |
12523 | |
12524 | VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound( |
12525 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
12526 | VkDeviceSize maxBytesToMove, |
12527 | uint32_t maxAllocationsToMove) |
12528 | { |
12529 | if(m_Blocks.empty()) |
12530 | { |
12531 | return VK_SUCCESS; |
12532 | } |
12533 | |
12534 | // This is a choice based on research. |
12535 | // Option 1: |
12536 | uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT; |
12537 | // Option 2: |
12538 | //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT; |
12539 | // Option 3: |
12540 | //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT; |
12541 | |
12542 | size_t srcBlockMinIndex = 0; |
12543 | // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations. |
12544 | /* |
12545 | if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT) |
12546 | { |
12547 | const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount(); |
12548 | if(blocksWithNonMovableCount > 0) |
12549 | { |
12550 | srcBlockMinIndex = blocksWithNonMovableCount - 1; |
12551 | } |
12552 | } |
12553 | */ |
12554 | |
12555 | size_t srcBlockIndex = m_Blocks.size() - 1; |
12556 | size_t srcAllocIndex = SIZE_MAX; |
12557 | for(;;) |
12558 | { |
12559 | // 1. Find next allocation to move. |
12560 | // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source". |
12561 | // 1.2. Then start from last to first m_Allocations. |
12562 | while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size()) |
12563 | { |
12564 | if(m_Blocks[srcBlockIndex]->m_Allocations.empty()) |
12565 | { |
12566 | // Finished: no more allocations to process. |
12567 | if(srcBlockIndex == srcBlockMinIndex) |
12568 | { |
12569 | return VK_SUCCESS; |
12570 | } |
12571 | else |
12572 | { |
12573 | --srcBlockIndex; |
12574 | srcAllocIndex = SIZE_MAX; |
12575 | } |
12576 | } |
12577 | else |
12578 | { |
12579 | srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1; |
12580 | } |
12581 | } |
12582 | |
12583 | BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex]; |
12584 | AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex]; |
12585 | |
12586 | const VkDeviceSize size = allocInfo.m_hAllocation->GetSize(); |
12587 | const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset(); |
12588 | const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment(); |
12589 | const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType(); |
12590 | |
12591 | // 2. Try to find new place for this allocation in preceding or current block. |
12592 | for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex) |
12593 | { |
12594 | BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex]; |
12595 | VmaAllocationRequest dstAllocRequest; |
12596 | if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest( |
12597 | currentFrameIndex: m_CurrentFrameIndex, |
12598 | frameInUseCount: m_pBlockVector->GetFrameInUseCount(), |
12599 | bufferImageGranularity: m_pBlockVector->GetBufferImageGranularity(), |
12600 | allocSize: size, |
12601 | allocAlignment: alignment, |
12602 | upperAddress: false, // upperAddress |
12603 | allocType: suballocType, |
12604 | canMakeOtherLost: false, // canMakeOtherLost |
12605 | strategy, |
12606 | pAllocationRequest: &dstAllocRequest) && |
12607 | MoveMakesSense( |
12608 | dstBlockIndex, dstOffset: dstAllocRequest.offset, srcBlockIndex, srcOffset)) |
12609 | { |
12610 | VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0); |
12611 | |
12612 | // Reached limit on number of allocations or bytes to move. |
12613 | if((m_AllocationsMoved + 1 > maxAllocationsToMove) || |
12614 | (m_BytesMoved + size > maxBytesToMove)) |
12615 | { |
12616 | return VK_SUCCESS; |
12617 | } |
12618 | |
12619 | VmaDefragmentationMove move; |
12620 | move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex; |
12621 | move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex; |
12622 | move.srcOffset = srcOffset; |
12623 | move.dstOffset = dstAllocRequest.offset; |
12624 | move.size = size; |
12625 | moves.push_back(src: move); |
12626 | |
12627 | pDstBlockInfo->m_pBlock->m_pMetadata->Alloc( |
12628 | request: dstAllocRequest, |
12629 | type: suballocType, |
12630 | allocSize: size, |
12631 | upperAddress: false, // upperAddress |
12632 | hAllocation: allocInfo.m_hAllocation); |
12633 | pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(offset: srcOffset); |
12634 | |
12635 | allocInfo.m_hAllocation->ChangeBlockAllocation(hAllocator: m_hAllocator, block: pDstBlockInfo->m_pBlock, offset: dstAllocRequest.offset); |
12636 | |
12637 | if(allocInfo.m_pChanged != VMA_NULL) |
12638 | { |
12639 | *allocInfo.m_pChanged = VK_TRUE; |
12640 | } |
12641 | |
12642 | ++m_AllocationsMoved; |
12643 | m_BytesMoved += size; |
12644 | |
12645 | VmaVectorRemove(vec&: pSrcBlockInfo->m_Allocations, index: srcAllocIndex); |
12646 | |
12647 | break; |
12648 | } |
12649 | } |
12650 | |
12651 | // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round. |
12652 | |
12653 | if(srcAllocIndex > 0) |
12654 | { |
12655 | --srcAllocIndex; |
12656 | } |
12657 | else |
12658 | { |
12659 | if(srcBlockIndex > 0) |
12660 | { |
12661 | --srcBlockIndex; |
12662 | srcAllocIndex = SIZE_MAX; |
12663 | } |
12664 | else |
12665 | { |
12666 | return VK_SUCCESS; |
12667 | } |
12668 | } |
12669 | } |
12670 | } |
12671 | |
12672 | size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const |
12673 | { |
12674 | size_t result = 0; |
12675 | for(size_t i = 0; i < m_Blocks.size(); ++i) |
12676 | { |
12677 | if(m_Blocks[i]->m_HasNonMovableAllocations) |
12678 | { |
12679 | ++result; |
12680 | } |
12681 | } |
12682 | return result; |
12683 | } |
12684 | |
12685 | VkResult VmaDefragmentationAlgorithm_Generic::Defragment( |
12686 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
12687 | VkDeviceSize maxBytesToMove, |
12688 | uint32_t maxAllocationsToMove) |
12689 | { |
12690 | if(!m_AllAllocations && m_AllocationCount == 0) |
12691 | { |
12692 | return VK_SUCCESS; |
12693 | } |
12694 | |
12695 | const size_t blockCount = m_Blocks.size(); |
12696 | for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) |
12697 | { |
12698 | BlockInfo* pBlockInfo = m_Blocks[blockIndex]; |
12699 | |
12700 | if(m_AllAllocations) |
12701 | { |
12702 | VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata; |
12703 | for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin(); |
12704 | it != pMetadata->m_Suballocations.end(); |
12705 | ++it) |
12706 | { |
12707 | if(it->type != VMA_SUBALLOCATION_TYPE_FREE) |
12708 | { |
12709 | AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL); |
12710 | pBlockInfo->m_Allocations.push_back(src: allocInfo); |
12711 | } |
12712 | } |
12713 | } |
12714 | |
12715 | pBlockInfo->CalcHasNonMovableAllocations(); |
12716 | |
12717 | // This is a choice based on research. |
12718 | // Option 1: |
12719 | pBlockInfo->SortAllocationsByOffsetDescending(); |
12720 | // Option 2: |
12721 | //pBlockInfo->SortAllocationsBySizeDescending(); |
12722 | } |
12723 | |
12724 | // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks. |
12725 | VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination()); |
12726 | |
12727 | // This is a choice based on research. |
12728 | const uint32_t roundCount = 2; |
12729 | |
12730 | // Execute defragmentation rounds (the main part). |
12731 | VkResult result = VK_SUCCESS; |
12732 | for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round) |
12733 | { |
12734 | result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove); |
12735 | } |
12736 | |
12737 | return result; |
12738 | } |
12739 | |
12740 | bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense( |
12741 | size_t dstBlockIndex, VkDeviceSize dstOffset, |
12742 | size_t srcBlockIndex, VkDeviceSize srcOffset) |
12743 | { |
12744 | if(dstBlockIndex < srcBlockIndex) |
12745 | { |
12746 | return true; |
12747 | } |
12748 | if(dstBlockIndex > srcBlockIndex) |
12749 | { |
12750 | return false; |
12751 | } |
12752 | if(dstOffset < srcOffset) |
12753 | { |
12754 | return true; |
12755 | } |
12756 | return false; |
12757 | } |
12758 | |
12759 | //////////////////////////////////////////////////////////////////////////////// |
12760 | // VmaDefragmentationAlgorithm_Fast |
12761 | |
12762 | VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast( |
12763 | VmaAllocator hAllocator, |
12764 | VmaBlockVector* pBlockVector, |
12765 | uint32_t currentFrameIndex, |
12766 | bool overlappingMoveSupported) : |
12767 | VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex), |
12768 | m_OverlappingMoveSupported(overlappingMoveSupported), |
12769 | m_AllocationCount(0), |
12770 | m_AllAllocations(false), |
12771 | m_BytesMoved(0), |
12772 | m_AllocationsMoved(0), |
12773 | m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks())) |
12774 | { |
12775 | VMA_ASSERT(VMA_DEBUG_MARGIN == 0); |
12776 | |
12777 | } |
12778 | |
12779 | VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast() |
12780 | { |
12781 | } |
12782 | |
12783 | VkResult VmaDefragmentationAlgorithm_Fast::Defragment( |
12784 | VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves, |
12785 | VkDeviceSize maxBytesToMove, |
12786 | uint32_t maxAllocationsToMove) |
12787 | { |
12788 | VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount); |
12789 | |
12790 | const size_t blockCount = m_pBlockVector->GetBlockCount(); |
12791 | if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0) |
12792 | { |
12793 | return VK_SUCCESS; |
12794 | } |
12795 | |
12796 | PreprocessMetadata(); |
12797 | |
12798 | // Sort blocks in order from most destination. |
12799 | |
12800 | m_BlockInfos.resize(newCount: blockCount); |
12801 | for(size_t i = 0; i < blockCount; ++i) |
12802 | { |
12803 | m_BlockInfos[i].origBlockIndex = i; |
12804 | } |
12805 | |
12806 | VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool { |
12807 | return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() < |
12808 | m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize(); |
12809 | }); |
12810 | |
12811 | // THE MAIN ALGORITHM |
12812 | |
12813 | FreeSpaceDatabase freeSpaceDb; |
12814 | |
12815 | size_t dstBlockInfoIndex = 0; |
12816 | size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex; |
12817 | VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(index: dstOrigBlockIndex); |
12818 | VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata; |
12819 | VkDeviceSize dstBlockSize = pDstMetadata->GetSize(); |
12820 | VkDeviceSize dstOffset = 0; |
12821 | |
12822 | bool end = false; |
12823 | for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex) |
12824 | { |
12825 | const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex; |
12826 | VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(index: srcOrigBlockIndex); |
12827 | VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata; |
12828 | for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin(); |
12829 | !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); ) |
12830 | { |
12831 | VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation; |
12832 | const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment(); |
12833 | const VkDeviceSize srcAllocSize = srcSuballocIt->size; |
12834 | if(m_AllocationsMoved == maxAllocationsToMove || |
12835 | m_BytesMoved + srcAllocSize > maxBytesToMove) |
12836 | { |
12837 | end = true; |
12838 | break; |
12839 | } |
12840 | const VkDeviceSize srcAllocOffset = srcSuballocIt->offset; |
12841 | |
12842 | // Try to place it in one of free spaces from the database. |
12843 | size_t freeSpaceInfoIndex; |
12844 | VkDeviceSize dstAllocOffset; |
12845 | if(freeSpaceDb.Fetch(alignment: srcAllocAlignment, size: srcAllocSize, |
12846 | outBlockInfoIndex&: freeSpaceInfoIndex, outDstOffset&: dstAllocOffset)) |
12847 | { |
12848 | size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex; |
12849 | VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(index: freeSpaceOrigBlockIndex); |
12850 | VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata; |
12851 | /*VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();*/ |
12852 | |
12853 | // Same block |
12854 | if(freeSpaceInfoIndex == srcBlockInfoIndex) |
12855 | { |
12856 | VMA_ASSERT(dstAllocOffset <= srcAllocOffset); |
12857 | |
12858 | // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset. |
12859 | |
12860 | VmaSuballocation suballoc = *srcSuballocIt; |
12861 | suballoc.offset = dstAllocOffset; |
12862 | suballoc.hAllocation->ChangeOffset(newOffset: dstAllocOffset); |
12863 | m_BytesMoved += srcAllocSize; |
12864 | ++m_AllocationsMoved; |
12865 | |
12866 | VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; |
12867 | ++nextSuballocIt; |
12868 | pSrcMetadata->m_Suballocations.erase(it: srcSuballocIt); |
12869 | srcSuballocIt = nextSuballocIt; |
12870 | |
12871 | InsertSuballoc(pMetadata: pFreeSpaceMetadata, suballoc); |
12872 | |
12873 | VmaDefragmentationMove move = { |
12874 | .srcBlockIndex: srcOrigBlockIndex, .dstBlockIndex: freeSpaceOrigBlockIndex, |
12875 | .srcOffset: srcAllocOffset, .dstOffset: dstAllocOffset, |
12876 | .size: srcAllocSize }; |
12877 | moves.push_back(src: move); |
12878 | } |
12879 | // Different block |
12880 | else |
12881 | { |
12882 | // MOVE OPTION 2: Move the allocation to a different block. |
12883 | |
12884 | VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex); |
12885 | |
12886 | VmaSuballocation suballoc = *srcSuballocIt; |
12887 | suballoc.offset = dstAllocOffset; |
12888 | suballoc.hAllocation->ChangeBlockAllocation(hAllocator: m_hAllocator, block: pFreeSpaceBlock, offset: dstAllocOffset); |
12889 | m_BytesMoved += srcAllocSize; |
12890 | ++m_AllocationsMoved; |
12891 | |
12892 | VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; |
12893 | ++nextSuballocIt; |
12894 | pSrcMetadata->m_Suballocations.erase(it: srcSuballocIt); |
12895 | srcSuballocIt = nextSuballocIt; |
12896 | |
12897 | InsertSuballoc(pMetadata: pFreeSpaceMetadata, suballoc); |
12898 | |
12899 | VmaDefragmentationMove move = { |
12900 | .srcBlockIndex: srcOrigBlockIndex, .dstBlockIndex: freeSpaceOrigBlockIndex, |
12901 | .srcOffset: srcAllocOffset, .dstOffset: dstAllocOffset, |
12902 | .size: srcAllocSize }; |
12903 | moves.push_back(src: move); |
12904 | } |
12905 | } |
12906 | else |
12907 | { |
12908 | dstAllocOffset = VmaAlignUp(val: dstOffset, align: srcAllocAlignment); |
12909 | |
12910 | // If the allocation doesn't fit before the end of dstBlock, forward to next block. |
12911 | while(dstBlockInfoIndex < srcBlockInfoIndex && |
12912 | dstAllocOffset + srcAllocSize > dstBlockSize) |
12913 | { |
12914 | // But before that, register remaining free space at the end of dst block. |
12915 | freeSpaceDb.Register(blockInfoIndex: dstBlockInfoIndex, offset: dstOffset, size: dstBlockSize - dstOffset); |
12916 | |
12917 | ++dstBlockInfoIndex; |
12918 | dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex; |
12919 | pDstBlock = m_pBlockVector->GetBlock(index: dstOrigBlockIndex); |
12920 | pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata; |
12921 | dstBlockSize = pDstMetadata->GetSize(); |
12922 | dstOffset = 0; |
12923 | dstAllocOffset = 0; |
12924 | } |
12925 | |
12926 | // Same block |
12927 | if(dstBlockInfoIndex == srcBlockInfoIndex) |
12928 | { |
12929 | VMA_ASSERT(dstAllocOffset <= srcAllocOffset); |
12930 | |
12931 | const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset; |
12932 | |
12933 | bool skipOver = overlap; |
12934 | if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset) |
12935 | { |
12936 | // If destination and source place overlap, skip if it would move it |
12937 | // by only < 1/64 of its size. |
12938 | skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize; |
12939 | } |
12940 | |
12941 | if(skipOver) |
12942 | { |
12943 | freeSpaceDb.Register(blockInfoIndex: dstBlockInfoIndex, offset: dstOffset, size: srcAllocOffset - dstOffset); |
12944 | |
12945 | dstOffset = srcAllocOffset + srcAllocSize; |
12946 | ++srcSuballocIt; |
12947 | } |
12948 | // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset. |
12949 | else |
12950 | { |
12951 | srcSuballocIt->offset = dstAllocOffset; |
12952 | srcSuballocIt->hAllocation->ChangeOffset(newOffset: dstAllocOffset); |
12953 | dstOffset = dstAllocOffset + srcAllocSize; |
12954 | m_BytesMoved += srcAllocSize; |
12955 | ++m_AllocationsMoved; |
12956 | ++srcSuballocIt; |
12957 | VmaDefragmentationMove move = { |
12958 | .srcBlockIndex: srcOrigBlockIndex, .dstBlockIndex: dstOrigBlockIndex, |
12959 | .srcOffset: srcAllocOffset, .dstOffset: dstAllocOffset, |
12960 | .size: srcAllocSize }; |
12961 | moves.push_back(src: move); |
12962 | } |
12963 | } |
12964 | // Different block |
12965 | else |
12966 | { |
12967 | // MOVE OPTION 2: Move the allocation to a different block. |
12968 | |
12969 | VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex); |
12970 | VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize); |
12971 | |
12972 | VmaSuballocation suballoc = *srcSuballocIt; |
12973 | suballoc.offset = dstAllocOffset; |
12974 | suballoc.hAllocation->ChangeBlockAllocation(hAllocator: m_hAllocator, block: pDstBlock, offset: dstAllocOffset); |
12975 | dstOffset = dstAllocOffset + srcAllocSize; |
12976 | m_BytesMoved += srcAllocSize; |
12977 | ++m_AllocationsMoved; |
12978 | |
12979 | VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; |
12980 | ++nextSuballocIt; |
12981 | pSrcMetadata->m_Suballocations.erase(it: srcSuballocIt); |
12982 | srcSuballocIt = nextSuballocIt; |
12983 | |
12984 | pDstMetadata->m_Suballocations.push_back(value: suballoc); |
12985 | |
12986 | VmaDefragmentationMove move = { |
12987 | .srcBlockIndex: srcOrigBlockIndex, .dstBlockIndex: dstOrigBlockIndex, |
12988 | .srcOffset: srcAllocOffset, .dstOffset: dstAllocOffset, |
12989 | .size: srcAllocSize }; |
12990 | moves.push_back(src: move); |
12991 | } |
12992 | } |
12993 | } |
12994 | } |
12995 | |
12996 | m_BlockInfos.clear(); |
12997 | |
12998 | PostprocessMetadata(); |
12999 | |
13000 | return VK_SUCCESS; |
13001 | } |
13002 | |
13003 | void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata() |
13004 | { |
13005 | const size_t blockCount = m_pBlockVector->GetBlockCount(); |
13006 | for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) |
13007 | { |
13008 | VmaBlockMetadata_Generic* const pMetadata = |
13009 | (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(index: blockIndex)->m_pMetadata; |
13010 | pMetadata->m_FreeCount = 0; |
13011 | pMetadata->m_SumFreeSize = pMetadata->GetSize(); |
13012 | pMetadata->m_FreeSuballocationsBySize.clear(); |
13013 | for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin(); |
13014 | it != pMetadata->m_Suballocations.end(); ) |
13015 | { |
13016 | if(it->type == VMA_SUBALLOCATION_TYPE_FREE) |
13017 | { |
13018 | VmaSuballocationList::iterator nextIt = it; |
13019 | ++nextIt; |
13020 | pMetadata->m_Suballocations.erase(it); |
13021 | it = nextIt; |
13022 | } |
13023 | else |
13024 | { |
13025 | ++it; |
13026 | } |
13027 | } |
13028 | } |
13029 | } |
13030 | |
13031 | void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata() |
13032 | { |
13033 | const size_t blockCount = m_pBlockVector->GetBlockCount(); |
13034 | for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) |
13035 | { |
13036 | VmaBlockMetadata_Generic* const pMetadata = |
13037 | (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(index: blockIndex)->m_pMetadata; |
13038 | const VkDeviceSize blockSize = pMetadata->GetSize(); |
13039 | |
13040 | // No allocations in this block - entire area is free. |
13041 | if(pMetadata->m_Suballocations.empty()) |
13042 | { |
13043 | pMetadata->m_FreeCount = 1; |
13044 | //pMetadata->m_SumFreeSize is already set to blockSize. |
13045 | VmaSuballocation suballoc = { |
13046 | .offset: 0, // offset |
13047 | .size: blockSize, // size |
13048 | VMA_NULL, // hAllocation |
13049 | .type: VMA_SUBALLOCATION_TYPE_FREE }; |
13050 | pMetadata->m_Suballocations.push_back(value: suballoc); |
13051 | pMetadata->RegisterFreeSuballocation(item: pMetadata->m_Suballocations.begin()); |
13052 | } |
13053 | // There are some allocations in this block. |
13054 | else |
13055 | { |
13056 | VkDeviceSize offset = 0; |
13057 | VmaSuballocationList::iterator it; |
13058 | for(it = pMetadata->m_Suballocations.begin(); |
13059 | it != pMetadata->m_Suballocations.end(); |
13060 | ++it) |
13061 | { |
13062 | VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE); |
13063 | VMA_ASSERT(it->offset >= offset); |
13064 | |
13065 | // Need to insert preceding free space. |
13066 | if(it->offset > offset) |
13067 | { |
13068 | ++pMetadata->m_FreeCount; |
13069 | const VkDeviceSize freeSize = it->offset - offset; |
13070 | VmaSuballocation suballoc = { |
13071 | .offset: offset, // offset |
13072 | .size: freeSize, // size |
13073 | VMA_NULL, // hAllocation |
13074 | .type: VMA_SUBALLOCATION_TYPE_FREE }; |
13075 | VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, value: suballoc); |
13076 | if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) |
13077 | { |
13078 | pMetadata->m_FreeSuballocationsBySize.push_back(src: precedingFreeIt); |
13079 | } |
13080 | } |
13081 | |
13082 | pMetadata->m_SumFreeSize -= it->size; |
13083 | offset = it->offset + it->size; |
13084 | } |
13085 | |
13086 | // Need to insert trailing free space. |
13087 | if(offset < blockSize) |
13088 | { |
13089 | ++pMetadata->m_FreeCount; |
13090 | const VkDeviceSize freeSize = blockSize - offset; |
13091 | VmaSuballocation suballoc = { |
13092 | .offset: offset, // offset |
13093 | .size: freeSize, // size |
13094 | VMA_NULL, // hAllocation |
13095 | .type: VMA_SUBALLOCATION_TYPE_FREE }; |
13096 | VMA_ASSERT(it == pMetadata->m_Suballocations.end()); |
13097 | VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, value: suballoc); |
13098 | if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) |
13099 | { |
13100 | pMetadata->m_FreeSuballocationsBySize.push_back(src: trailingFreeIt); |
13101 | } |
13102 | } |
13103 | |
13104 | VMA_SORT( |
13105 | pMetadata->m_FreeSuballocationsBySize.begin(), |
13106 | pMetadata->m_FreeSuballocationsBySize.end(), |
13107 | VmaSuballocationItemSizeLess()); |
13108 | } |
13109 | |
13110 | VMA_HEAVY_ASSERT(pMetadata->Validate()); |
13111 | } |
13112 | } |
13113 | |
13114 | void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc) |
13115 | { |
13116 | // TODO: Optimize somehow. Remember iterator instead of searching for it linearly. |
13117 | VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin(); |
13118 | while(it != pMetadata->m_Suballocations.end()) |
13119 | { |
13120 | if(it->offset < suballoc.offset) |
13121 | { |
13122 | ++it; |
13123 | } |
13124 | } |
13125 | pMetadata->m_Suballocations.insert(it, value: suballoc); |
13126 | } |
13127 | |
13128 | //////////////////////////////////////////////////////////////////////////////// |
13129 | // VmaBlockVectorDefragmentationContext |
13130 | |
13131 | VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext( |
13132 | VmaAllocator hAllocator, |
13133 | VmaPool hCustomPool, |
13134 | VmaBlockVector* pBlockVector, |
13135 | uint32_t currFrameIndex, |
13136 | uint32_t /*algorithmFlags*/) : |
13137 | res(VK_SUCCESS), |
13138 | mutexLocked(false), |
13139 | blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())), |
13140 | m_hAllocator(hAllocator), |
13141 | m_hCustomPool(hCustomPool), |
13142 | m_pBlockVector(pBlockVector), |
13143 | m_CurrFrameIndex(currFrameIndex), |
13144 | /*m_AlgorithmFlags(algorithmFlags),*/ |
13145 | m_pAlgorithm(VMA_NULL), |
13146 | m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())), |
13147 | m_AllAllocations(false) |
13148 | { |
13149 | } |
13150 | |
13151 | VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext() |
13152 | { |
13153 | vma_delete(hAllocator: m_hAllocator, ptr: m_pAlgorithm); |
13154 | } |
13155 | |
13156 | void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) |
13157 | { |
13158 | AllocInfo info = { .hAlloc: hAlloc, .pChanged: pChanged }; |
13159 | m_Allocations.push_back(src: info); |
13160 | } |
13161 | |
13162 | void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported) |
13163 | { |
13164 | const bool allAllocations = m_AllAllocations || |
13165 | m_Allocations.size() == m_pBlockVector->CalcAllocationCount(); |
13166 | |
13167 | /******************************** |
13168 | HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM. |
13169 | ********************************/ |
13170 | |
13171 | /* |
13172 | Fast algorithm is supported only when certain criteria are met: |
13173 | - VMA_DEBUG_MARGIN is 0. |
13174 | - All allocations in this block vector are moveable. |
13175 | - There is no possibility of image/buffer granularity conflict. |
13176 | */ |
13177 | if(VMA_DEBUG_MARGIN == 0 && |
13178 | allAllocations && |
13179 | !m_pBlockVector->IsBufferImageGranularityConflictPossible()) |
13180 | { |
13181 | m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)( |
13182 | m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported); |
13183 | } |
13184 | else |
13185 | { |
13186 | m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)( |
13187 | m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported); |
13188 | } |
13189 | |
13190 | if(allAllocations) |
13191 | { |
13192 | m_pAlgorithm->AddAll(); |
13193 | } |
13194 | else |
13195 | { |
13196 | for(size_t i = 0, count = m_Allocations.size(); i < count; ++i) |
13197 | { |
13198 | m_pAlgorithm->AddAllocation(hAlloc: m_Allocations[i].hAlloc, pChanged: m_Allocations[i].pChanged); |
13199 | } |
13200 | } |
13201 | } |
13202 | |
13203 | //////////////////////////////////////////////////////////////////////////////// |
13204 | // VmaDefragmentationContext |
13205 | |
13206 | VmaDefragmentationContext_T::VmaDefragmentationContext_T( |
13207 | VmaAllocator hAllocator, |
13208 | uint32_t currFrameIndex, |
13209 | uint32_t flags, |
13210 | VmaDefragmentationStats* pStats) : |
13211 | m_hAllocator(hAllocator), |
13212 | m_CurrFrameIndex(currFrameIndex), |
13213 | m_Flags(flags), |
13214 | m_pStats(pStats), |
13215 | m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks())) |
13216 | { |
13217 | memset(s: m_DefaultPoolContexts, c: 0, n: sizeof(m_DefaultPoolContexts)); |
13218 | } |
13219 | |
13220 | VmaDefragmentationContext_T::~VmaDefragmentationContext_T() |
13221 | { |
13222 | for(size_t i = m_CustomPoolContexts.size(); i--; ) |
13223 | { |
13224 | VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i]; |
13225 | pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pCtx: pBlockVectorCtx, pStats: m_pStats); |
13226 | vma_delete(hAllocator: m_hAllocator, ptr: pBlockVectorCtx); |
13227 | } |
13228 | for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; ) |
13229 | { |
13230 | VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i]; |
13231 | if(pBlockVectorCtx) |
13232 | { |
13233 | pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pCtx: pBlockVectorCtx, pStats: m_pStats); |
13234 | vma_delete(hAllocator: m_hAllocator, ptr: pBlockVectorCtx); |
13235 | } |
13236 | } |
13237 | } |
13238 | |
13239 | void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools) |
13240 | { |
13241 | for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex) |
13242 | { |
13243 | VmaPool pool = pPools[poolIndex]; |
13244 | VMA_ASSERT(pool); |
13245 | // Pools with algorithm other than default are not defragmented. |
13246 | if(pool->m_BlockVector.GetAlgorithm() == 0) |
13247 | { |
13248 | VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL; |
13249 | |
13250 | for(size_t i = m_CustomPoolContexts.size(); i--; ) |
13251 | { |
13252 | if(m_CustomPoolContexts[i]->GetCustomPool() == pool) |
13253 | { |
13254 | pBlockVectorDefragCtx = m_CustomPoolContexts[i]; |
13255 | break; |
13256 | } |
13257 | } |
13258 | |
13259 | if(!pBlockVectorDefragCtx) |
13260 | { |
13261 | pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( |
13262 | m_hAllocator, |
13263 | pool, |
13264 | &pool->m_BlockVector, |
13265 | m_CurrFrameIndex, |
13266 | m_Flags); |
13267 | m_CustomPoolContexts.push_back(src: pBlockVectorDefragCtx); |
13268 | } |
13269 | |
13270 | pBlockVectorDefragCtx->AddAll(); |
13271 | } |
13272 | } |
13273 | } |
13274 | |
13275 | void VmaDefragmentationContext_T::AddAllocations( |
13276 | uint32_t allocationCount, |
13277 | VmaAllocation* pAllocations, |
13278 | VkBool32* pAllocationsChanged) |
13279 | { |
13280 | // Dispatch pAllocations among defragmentators. Create them when necessary. |
13281 | for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) |
13282 | { |
13283 | const VmaAllocation hAlloc = pAllocations[allocIndex]; |
13284 | VMA_ASSERT(hAlloc); |
13285 | // DedicatedAlloc cannot be defragmented. |
13286 | if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) && |
13287 | // Lost allocation cannot be defragmented. |
13288 | (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)) |
13289 | { |
13290 | VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL; |
13291 | |
13292 | const VmaPool hAllocPool = hAlloc->GetPool(); |
13293 | // This allocation belongs to custom pool. |
13294 | if(hAllocPool != VK_NULL_HANDLE) |
13295 | { |
13296 | // Pools with algorithm other than default are not defragmented. |
13297 | if(hAllocPool->m_BlockVector.GetAlgorithm() == 0) |
13298 | { |
13299 | for(size_t i = m_CustomPoolContexts.size(); i--; ) |
13300 | { |
13301 | if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool) |
13302 | { |
13303 | pBlockVectorDefragCtx = m_CustomPoolContexts[i]; |
13304 | break; |
13305 | } |
13306 | } |
13307 | if(!pBlockVectorDefragCtx) |
13308 | { |
13309 | pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( |
13310 | m_hAllocator, |
13311 | hAllocPool, |
13312 | &hAllocPool->m_BlockVector, |
13313 | m_CurrFrameIndex, |
13314 | m_Flags); |
13315 | m_CustomPoolContexts.push_back(src: pBlockVectorDefragCtx); |
13316 | } |
13317 | } |
13318 | } |
13319 | // This allocation belongs to default pool. |
13320 | else |
13321 | { |
13322 | const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex(); |
13323 | pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex]; |
13324 | if(!pBlockVectorDefragCtx) |
13325 | { |
13326 | pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( |
13327 | m_hAllocator, |
13328 | VMA_NULL, // hCustomPool |
13329 | m_hAllocator->m_pBlockVectors[memTypeIndex], |
13330 | m_CurrFrameIndex, |
13331 | m_Flags); |
13332 | m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx; |
13333 | } |
13334 | } |
13335 | |
13336 | if(pBlockVectorDefragCtx) |
13337 | { |
13338 | VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ? |
13339 | &pAllocationsChanged[allocIndex] : VMA_NULL; |
13340 | pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged); |
13341 | } |
13342 | } |
13343 | } |
13344 | } |
13345 | |
13346 | VkResult VmaDefragmentationContext_T::Defragment( |
13347 | VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove, |
13348 | VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove, |
13349 | VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats) |
13350 | { |
13351 | if(pStats) |
13352 | { |
13353 | memset(s: pStats, c: 0, n: sizeof(VmaDefragmentationStats)); |
13354 | } |
13355 | |
13356 | if(commandBuffer == VK_NULL_HANDLE) |
13357 | { |
13358 | maxGpuBytesToMove = 0; |
13359 | maxGpuAllocationsToMove = 0; |
13360 | } |
13361 | |
13362 | VkResult res = VK_SUCCESS; |
13363 | |
13364 | // Process default pools. |
13365 | for(uint32_t memTypeIndex = 0; |
13366 | memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS; |
13367 | ++memTypeIndex) |
13368 | { |
13369 | VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex]; |
13370 | if(pBlockVectorCtx) |
13371 | { |
13372 | VMA_ASSERT(pBlockVectorCtx->GetBlockVector()); |
13373 | pBlockVectorCtx->GetBlockVector()->Defragment( |
13374 | pCtx: pBlockVectorCtx, |
13375 | pStats, |
13376 | maxCpuBytesToMove, maxCpuAllocationsToMove, |
13377 | maxGpuBytesToMove, maxGpuAllocationsToMove, |
13378 | commandBuffer); |
13379 | if(pBlockVectorCtx->res != VK_SUCCESS) |
13380 | { |
13381 | res = pBlockVectorCtx->res; |
13382 | } |
13383 | } |
13384 | } |
13385 | |
13386 | // Process custom pools. |
13387 | for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size(); |
13388 | customCtxIndex < customCtxCount && res >= VK_SUCCESS; |
13389 | ++customCtxIndex) |
13390 | { |
13391 | VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex]; |
13392 | VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector()); |
13393 | pBlockVectorCtx->GetBlockVector()->Defragment( |
13394 | pCtx: pBlockVectorCtx, |
13395 | pStats, |
13396 | maxCpuBytesToMove, maxCpuAllocationsToMove, |
13397 | maxGpuBytesToMove, maxGpuAllocationsToMove, |
13398 | commandBuffer); |
13399 | if(pBlockVectorCtx->res != VK_SUCCESS) |
13400 | { |
13401 | res = pBlockVectorCtx->res; |
13402 | } |
13403 | } |
13404 | |
13405 | return res; |
13406 | } |
13407 | |
13408 | //////////////////////////////////////////////////////////////////////////////// |
13409 | // VmaRecorder |
13410 | |
13411 | #if VMA_RECORDING_ENABLED |
13412 | |
13413 | VmaRecorder::VmaRecorder() : |
13414 | m_UseMutex(true), |
13415 | m_Flags(0), |
13416 | m_File(VMA_NULL), |
13417 | m_Freq(INT64_MAX), |
13418 | m_StartCounter(INT64_MAX) |
13419 | { |
13420 | } |
13421 | |
13422 | VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex) |
13423 | { |
13424 | m_UseMutex = useMutex; |
13425 | m_Flags = settings.flags; |
13426 | |
13427 | QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq); |
13428 | QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter); |
13429 | |
13430 | // Open file for writing. |
13431 | errno_t err = fopen_s(&m_File, settings.pFilePath, "wb" ); |
13432 | if(err != 0) |
13433 | { |
13434 | return VK_ERROR_INITIALIZATION_FAILED; |
13435 | } |
13436 | |
13437 | // Write header. |
13438 | fprintf(m_File, "%s\n" , "Vulkan Memory Allocator,Calls recording" ); |
13439 | fprintf(m_File, "%s\n" , "1,5" ); |
13440 | |
13441 | return VK_SUCCESS; |
13442 | } |
13443 | |
13444 | VmaRecorder::~VmaRecorder() |
13445 | { |
13446 | if(m_File != VMA_NULL) |
13447 | { |
13448 | fclose(m_File); |
13449 | } |
13450 | } |
13451 | |
13452 | void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex) |
13453 | { |
13454 | CallParams callParams; |
13455 | GetBasicParams(callParams); |
13456 | |
13457 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13458 | fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n" , callParams.threadId, callParams.time, frameIndex); |
13459 | Flush(); |
13460 | } |
13461 | |
13462 | void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex) |
13463 | { |
13464 | CallParams callParams; |
13465 | GetBasicParams(callParams); |
13466 | |
13467 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13468 | fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n" , callParams.threadId, callParams.time, frameIndex); |
13469 | Flush(); |
13470 | } |
13471 | |
13472 | void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool) |
13473 | { |
13474 | CallParams callParams; |
13475 | GetBasicParams(callParams); |
13476 | |
13477 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13478 | fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13479 | createInfo.memoryTypeIndex, |
13480 | createInfo.flags, |
13481 | createInfo.blockSize, |
13482 | (uint64_t)createInfo.minBlockCount, |
13483 | (uint64_t)createInfo.maxBlockCount, |
13484 | createInfo.frameInUseCount, |
13485 | pool); |
13486 | Flush(); |
13487 | } |
13488 | |
13489 | void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool) |
13490 | { |
13491 | CallParams callParams; |
13492 | GetBasicParams(callParams); |
13493 | |
13494 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13495 | fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13496 | pool); |
13497 | Flush(); |
13498 | } |
13499 | |
13500 | void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex, |
13501 | const VkMemoryRequirements& vkMemReq, |
13502 | const VmaAllocationCreateInfo& createInfo, |
13503 | VmaAllocation allocation) |
13504 | { |
13505 | CallParams callParams; |
13506 | GetBasicParams(callParams); |
13507 | |
13508 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13509 | UserDataString userDataStr(createInfo.flags, createInfo.pUserData); |
13510 | fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n" , callParams.threadId, callParams.time, frameIndex, |
13511 | vkMemReq.size, |
13512 | vkMemReq.alignment, |
13513 | vkMemReq.memoryTypeBits, |
13514 | createInfo.flags, |
13515 | createInfo.usage, |
13516 | createInfo.requiredFlags, |
13517 | createInfo.preferredFlags, |
13518 | createInfo.memoryTypeBits, |
13519 | createInfo.pool, |
13520 | allocation, |
13521 | userDataStr.GetString()); |
13522 | Flush(); |
13523 | } |
13524 | |
13525 | void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex, |
13526 | const VkMemoryRequirements& vkMemReq, |
13527 | const VmaAllocationCreateInfo& createInfo, |
13528 | uint64_t allocationCount, |
13529 | const VmaAllocation* pAllocations) |
13530 | { |
13531 | CallParams callParams; |
13532 | GetBasicParams(callParams); |
13533 | |
13534 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13535 | UserDataString userDataStr(createInfo.flags, createInfo.pUserData); |
13536 | fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p," , callParams.threadId, callParams.time, frameIndex, |
13537 | vkMemReq.size, |
13538 | vkMemReq.alignment, |
13539 | vkMemReq.memoryTypeBits, |
13540 | createInfo.flags, |
13541 | createInfo.usage, |
13542 | createInfo.requiredFlags, |
13543 | createInfo.preferredFlags, |
13544 | createInfo.memoryTypeBits, |
13545 | createInfo.pool); |
13546 | PrintPointerList(allocationCount, pAllocations); |
13547 | fprintf(m_File, ",%s\n" , userDataStr.GetString()); |
13548 | Flush(); |
13549 | } |
13550 | |
13551 | void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex, |
13552 | const VkMemoryRequirements& vkMemReq, |
13553 | bool requiresDedicatedAllocation, |
13554 | bool prefersDedicatedAllocation, |
13555 | const VmaAllocationCreateInfo& createInfo, |
13556 | VmaAllocation allocation) |
13557 | { |
13558 | CallParams callParams; |
13559 | GetBasicParams(callParams); |
13560 | |
13561 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13562 | UserDataString userDataStr(createInfo.flags, createInfo.pUserData); |
13563 | fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n" , callParams.threadId, callParams.time, frameIndex, |
13564 | vkMemReq.size, |
13565 | vkMemReq.alignment, |
13566 | vkMemReq.memoryTypeBits, |
13567 | requiresDedicatedAllocation ? 1 : 0, |
13568 | prefersDedicatedAllocation ? 1 : 0, |
13569 | createInfo.flags, |
13570 | createInfo.usage, |
13571 | createInfo.requiredFlags, |
13572 | createInfo.preferredFlags, |
13573 | createInfo.memoryTypeBits, |
13574 | createInfo.pool, |
13575 | allocation, |
13576 | userDataStr.GetString()); |
13577 | Flush(); |
13578 | } |
13579 | |
13580 | void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex, |
13581 | const VkMemoryRequirements& vkMemReq, |
13582 | bool requiresDedicatedAllocation, |
13583 | bool prefersDedicatedAllocation, |
13584 | const VmaAllocationCreateInfo& createInfo, |
13585 | VmaAllocation allocation) |
13586 | { |
13587 | CallParams callParams; |
13588 | GetBasicParams(callParams); |
13589 | |
13590 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13591 | UserDataString userDataStr(createInfo.flags, createInfo.pUserData); |
13592 | fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n" , callParams.threadId, callParams.time, frameIndex, |
13593 | vkMemReq.size, |
13594 | vkMemReq.alignment, |
13595 | vkMemReq.memoryTypeBits, |
13596 | requiresDedicatedAllocation ? 1 : 0, |
13597 | prefersDedicatedAllocation ? 1 : 0, |
13598 | createInfo.flags, |
13599 | createInfo.usage, |
13600 | createInfo.requiredFlags, |
13601 | createInfo.preferredFlags, |
13602 | createInfo.memoryTypeBits, |
13603 | createInfo.pool, |
13604 | allocation, |
13605 | userDataStr.GetString()); |
13606 | Flush(); |
13607 | } |
13608 | |
13609 | void VmaRecorder::RecordFreeMemory(uint32_t frameIndex, |
13610 | VmaAllocation allocation) |
13611 | { |
13612 | CallParams callParams; |
13613 | GetBasicParams(callParams); |
13614 | |
13615 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13616 | fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13617 | allocation); |
13618 | Flush(); |
13619 | } |
13620 | |
13621 | void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex, |
13622 | uint64_t allocationCount, |
13623 | const VmaAllocation* pAllocations) |
13624 | { |
13625 | CallParams callParams; |
13626 | GetBasicParams(callParams); |
13627 | |
13628 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13629 | fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages," , callParams.threadId, callParams.time, frameIndex); |
13630 | PrintPointerList(allocationCount, pAllocations); |
13631 | fprintf(m_File, "\n" ); |
13632 | Flush(); |
13633 | } |
13634 | |
13635 | void VmaRecorder::RecordResizeAllocation( |
13636 | uint32_t frameIndex, |
13637 | VmaAllocation allocation, |
13638 | VkDeviceSize newSize) |
13639 | { |
13640 | CallParams callParams; |
13641 | GetBasicParams(callParams); |
13642 | |
13643 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13644 | fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n" , callParams.threadId, callParams.time, frameIndex, |
13645 | allocation, newSize); |
13646 | Flush(); |
13647 | } |
13648 | |
13649 | void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex, |
13650 | VmaAllocation allocation, |
13651 | const void* pUserData) |
13652 | { |
13653 | CallParams callParams; |
13654 | GetBasicParams(callParams); |
13655 | |
13656 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13657 | UserDataString userDataStr( |
13658 | allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0, |
13659 | pUserData); |
13660 | fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n" , callParams.threadId, callParams.time, frameIndex, |
13661 | allocation, |
13662 | userDataStr.GetString()); |
13663 | Flush(); |
13664 | } |
13665 | |
13666 | void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex, |
13667 | VmaAllocation allocation) |
13668 | { |
13669 | CallParams callParams; |
13670 | GetBasicParams(callParams); |
13671 | |
13672 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13673 | fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13674 | allocation); |
13675 | Flush(); |
13676 | } |
13677 | |
13678 | void VmaRecorder::RecordMapMemory(uint32_t frameIndex, |
13679 | VmaAllocation allocation) |
13680 | { |
13681 | CallParams callParams; |
13682 | GetBasicParams(callParams); |
13683 | |
13684 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13685 | fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13686 | allocation); |
13687 | Flush(); |
13688 | } |
13689 | |
13690 | void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex, |
13691 | VmaAllocation allocation) |
13692 | { |
13693 | CallParams callParams; |
13694 | GetBasicParams(callParams); |
13695 | |
13696 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13697 | fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13698 | allocation); |
13699 | Flush(); |
13700 | } |
13701 | |
13702 | void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex, |
13703 | VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) |
13704 | { |
13705 | CallParams callParams; |
13706 | GetBasicParams(callParams); |
13707 | |
13708 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13709 | fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n" , callParams.threadId, callParams.time, frameIndex, |
13710 | allocation, |
13711 | offset, |
13712 | size); |
13713 | Flush(); |
13714 | } |
13715 | |
13716 | void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex, |
13717 | VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) |
13718 | { |
13719 | CallParams callParams; |
13720 | GetBasicParams(callParams); |
13721 | |
13722 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13723 | fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n" , callParams.threadId, callParams.time, frameIndex, |
13724 | allocation, |
13725 | offset, |
13726 | size); |
13727 | Flush(); |
13728 | } |
13729 | |
13730 | void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex, |
13731 | const VkBufferCreateInfo& bufCreateInfo, |
13732 | const VmaAllocationCreateInfo& allocCreateInfo, |
13733 | VmaAllocation allocation) |
13734 | { |
13735 | CallParams callParams; |
13736 | GetBasicParams(callParams); |
13737 | |
13738 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13739 | UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData); |
13740 | fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n" , callParams.threadId, callParams.time, frameIndex, |
13741 | bufCreateInfo.flags, |
13742 | bufCreateInfo.size, |
13743 | bufCreateInfo.usage, |
13744 | bufCreateInfo.sharingMode, |
13745 | allocCreateInfo.flags, |
13746 | allocCreateInfo.usage, |
13747 | allocCreateInfo.requiredFlags, |
13748 | allocCreateInfo.preferredFlags, |
13749 | allocCreateInfo.memoryTypeBits, |
13750 | allocCreateInfo.pool, |
13751 | allocation, |
13752 | userDataStr.GetString()); |
13753 | Flush(); |
13754 | } |
13755 | |
13756 | void VmaRecorder::RecordCreateImage(uint32_t frameIndex, |
13757 | const VkImageCreateInfo& imageCreateInfo, |
13758 | const VmaAllocationCreateInfo& allocCreateInfo, |
13759 | VmaAllocation allocation) |
13760 | { |
13761 | CallParams callParams; |
13762 | GetBasicParams(callParams); |
13763 | |
13764 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13765 | UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData); |
13766 | fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n" , callParams.threadId, callParams.time, frameIndex, |
13767 | imageCreateInfo.flags, |
13768 | imageCreateInfo.imageType, |
13769 | imageCreateInfo.format, |
13770 | imageCreateInfo.extent.width, |
13771 | imageCreateInfo.extent.height, |
13772 | imageCreateInfo.extent.depth, |
13773 | imageCreateInfo.mipLevels, |
13774 | imageCreateInfo.arrayLayers, |
13775 | imageCreateInfo.samples, |
13776 | imageCreateInfo.tiling, |
13777 | imageCreateInfo.usage, |
13778 | imageCreateInfo.sharingMode, |
13779 | imageCreateInfo.initialLayout, |
13780 | allocCreateInfo.flags, |
13781 | allocCreateInfo.usage, |
13782 | allocCreateInfo.requiredFlags, |
13783 | allocCreateInfo.preferredFlags, |
13784 | allocCreateInfo.memoryTypeBits, |
13785 | allocCreateInfo.pool, |
13786 | allocation, |
13787 | userDataStr.GetString()); |
13788 | Flush(); |
13789 | } |
13790 | |
13791 | void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex, |
13792 | VmaAllocation allocation) |
13793 | { |
13794 | CallParams callParams; |
13795 | GetBasicParams(callParams); |
13796 | |
13797 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13798 | fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13799 | allocation); |
13800 | Flush(); |
13801 | } |
13802 | |
13803 | void VmaRecorder::RecordDestroyImage(uint32_t frameIndex, |
13804 | VmaAllocation allocation) |
13805 | { |
13806 | CallParams callParams; |
13807 | GetBasicParams(callParams); |
13808 | |
13809 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13810 | fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13811 | allocation); |
13812 | Flush(); |
13813 | } |
13814 | |
13815 | void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex, |
13816 | VmaAllocation allocation) |
13817 | { |
13818 | CallParams callParams; |
13819 | GetBasicParams(callParams); |
13820 | |
13821 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13822 | fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13823 | allocation); |
13824 | Flush(); |
13825 | } |
13826 | |
13827 | void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex, |
13828 | VmaAllocation allocation) |
13829 | { |
13830 | CallParams callParams; |
13831 | GetBasicParams(callParams); |
13832 | |
13833 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13834 | fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13835 | allocation); |
13836 | Flush(); |
13837 | } |
13838 | |
13839 | void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex, |
13840 | VmaPool pool) |
13841 | { |
13842 | CallParams callParams; |
13843 | GetBasicParams(callParams); |
13844 | |
13845 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13846 | fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13847 | pool); |
13848 | Flush(); |
13849 | } |
13850 | |
13851 | void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex, |
13852 | const VmaDefragmentationInfo2& info, |
13853 | VmaDefragmentationContext ctx) |
13854 | { |
13855 | CallParams callParams; |
13856 | GetBasicParams(callParams); |
13857 | |
13858 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13859 | fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u," , callParams.threadId, callParams.time, frameIndex, |
13860 | info.flags); |
13861 | PrintPointerList(info.allocationCount, info.pAllocations); |
13862 | fprintf(m_File, "," ); |
13863 | PrintPointerList(info.poolCount, info.pPools); |
13864 | fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n" , |
13865 | info.maxCpuBytesToMove, |
13866 | info.maxCpuAllocationsToMove, |
13867 | info.maxGpuBytesToMove, |
13868 | info.maxGpuAllocationsToMove, |
13869 | info.commandBuffer, |
13870 | ctx); |
13871 | Flush(); |
13872 | } |
13873 | |
13874 | void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex, |
13875 | VmaDefragmentationContext ctx) |
13876 | { |
13877 | CallParams callParams; |
13878 | GetBasicParams(callParams); |
13879 | |
13880 | VmaMutexLock lock(m_FileMutex, m_UseMutex); |
13881 | fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n" , callParams.threadId, callParams.time, frameIndex, |
13882 | ctx); |
13883 | Flush(); |
13884 | } |
13885 | |
13886 | VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData) |
13887 | { |
13888 | if(pUserData != VMA_NULL) |
13889 | { |
13890 | if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0) |
13891 | { |
13892 | m_Str = (const char*)pUserData; |
13893 | } |
13894 | else |
13895 | { |
13896 | sprintf_s(m_PtrStr, "%p" , pUserData); |
13897 | m_Str = m_PtrStr; |
13898 | } |
13899 | } |
13900 | else |
13901 | { |
13902 | m_Str = "" ; |
13903 | } |
13904 | } |
13905 | |
13906 | void VmaRecorder::WriteConfiguration( |
13907 | const VkPhysicalDeviceProperties& devProps, |
13908 | const VkPhysicalDeviceMemoryProperties& memProps, |
13909 | bool dedicatedAllocationExtensionEnabled) |
13910 | { |
13911 | fprintf(m_File, "Config,Begin\n" ); |
13912 | |
13913 | fprintf(m_File, "PhysicalDevice,apiVersion,%u\n" , devProps.apiVersion); |
13914 | fprintf(m_File, "PhysicalDevice,driverVersion,%u\n" , devProps.driverVersion); |
13915 | fprintf(m_File, "PhysicalDevice,vendorID,%u\n" , devProps.vendorID); |
13916 | fprintf(m_File, "PhysicalDevice,deviceID,%u\n" , devProps.deviceID); |
13917 | fprintf(m_File, "PhysicalDevice,deviceType,%u\n" , devProps.deviceType); |
13918 | fprintf(m_File, "PhysicalDevice,deviceName,%s\n" , devProps.deviceName); |
13919 | |
13920 | fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n" , devProps.limits.maxMemoryAllocationCount); |
13921 | fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n" , devProps.limits.bufferImageGranularity); |
13922 | fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n" , devProps.limits.nonCoherentAtomSize); |
13923 | |
13924 | fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n" , memProps.memoryHeapCount); |
13925 | for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i) |
13926 | { |
13927 | fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n" , i, memProps.memoryHeaps[i].size); |
13928 | fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n" , i, memProps.memoryHeaps[i].flags); |
13929 | } |
13930 | fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n" , memProps.memoryTypeCount); |
13931 | for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i) |
13932 | { |
13933 | fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n" , i, memProps.memoryTypes[i].heapIndex); |
13934 | fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n" , i, memProps.memoryTypes[i].propertyFlags); |
13935 | } |
13936 | |
13937 | fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n" , dedicatedAllocationExtensionEnabled ? 1 : 0); |
13938 | |
13939 | fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n" , VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0); |
13940 | fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n" , (VkDeviceSize)VMA_DEBUG_ALIGNMENT); |
13941 | fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n" , (VkDeviceSize)VMA_DEBUG_MARGIN); |
13942 | fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n" , VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0); |
13943 | fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n" , VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0); |
13944 | fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n" , VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0); |
13945 | fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n" , (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY); |
13946 | fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n" , (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE); |
13947 | fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n" , (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); |
13948 | |
13949 | fprintf(m_File, "Config,End\n" ); |
13950 | } |
13951 | |
13952 | void VmaRecorder::GetBasicParams(CallParams& outParams) |
13953 | { |
13954 | outParams.threadId = GetCurrentThreadId(); |
13955 | |
13956 | LARGE_INTEGER counter; |
13957 | QueryPerformanceCounter(&counter); |
13958 | outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq; |
13959 | } |
13960 | |
13961 | void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems) |
13962 | { |
13963 | if(count) |
13964 | { |
13965 | fprintf(m_File, "%p" , pItems[0]); |
13966 | for(uint64_t i = 1; i < count; ++i) |
13967 | { |
13968 | fprintf(m_File, " %p" , pItems[i]); |
13969 | } |
13970 | } |
13971 | } |
13972 | |
13973 | void VmaRecorder::Flush() |
13974 | { |
13975 | if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0) |
13976 | { |
13977 | fflush(m_File); |
13978 | } |
13979 | } |
13980 | |
13981 | #endif // #if VMA_RECORDING_ENABLED |
13982 | |
13983 | //////////////////////////////////////////////////////////////////////////////// |
13984 | // VmaAllocator_T |
13985 | |
13986 | VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : |
13987 | m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0), |
13988 | m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0), |
13989 | m_hDevice(pCreateInfo->device), |
13990 | m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), |
13991 | m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ? |
13992 | *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks), |
13993 | m_PreferredLargeHeapBlockSize(0), |
13994 | m_PhysicalDevice(pCreateInfo->physicalDevice), |
13995 | m_CurrentFrameIndex(0), |
13996 | m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())), |
13997 | m_NextPoolId(0) |
13998 | #if VMA_RECORDING_ENABLED |
13999 | ,m_pRecorder(VMA_NULL) |
14000 | #endif |
14001 | { |
14002 | if(VMA_DEBUG_DETECT_CORRUPTION) |
14003 | { |
14004 | // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it. |
14005 | VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0); |
14006 | } |
14007 | |
14008 | VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device); |
14009 | |
14010 | #if !(VMA_DEDICATED_ALLOCATION) |
14011 | if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0) |
14012 | { |
14013 | VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros." ); |
14014 | } |
14015 | #endif |
14016 | |
14017 | memset(s: &m_DeviceMemoryCallbacks, c: 0 ,n: sizeof(m_DeviceMemoryCallbacks)); |
14018 | memset(s: &m_PhysicalDeviceProperties, c: 0, n: sizeof(m_PhysicalDeviceProperties)); |
14019 | memset(s: &m_MemProps, c: 0, n: sizeof(m_MemProps)); |
14020 | |
14021 | memset(s: &m_pBlockVectors, c: 0, n: sizeof(m_pBlockVectors)); |
14022 | memset(s: &m_pDedicatedAllocations, c: 0, n: sizeof(m_pDedicatedAllocations)); |
14023 | |
14024 | for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) |
14025 | { |
14026 | m_HeapSizeLimit[i] = VK_WHOLE_SIZE; |
14027 | } |
14028 | |
14029 | if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) |
14030 | { |
14031 | m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate; |
14032 | m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree; |
14033 | } |
14034 | |
14035 | ImportVulkanFunctions(pVulkanFunctions: pCreateInfo->pVulkanFunctions); |
14036 | |
14037 | (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties); |
14038 | (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps); |
14039 | |
14040 | VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT)); |
14041 | VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY)); |
14042 | VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity)); |
14043 | VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize)); |
14044 | |
14045 | m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ? |
14046 | pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); |
14047 | |
14048 | if(pCreateInfo->pHeapSizeLimit != VMA_NULL) |
14049 | { |
14050 | for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) |
14051 | { |
14052 | const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex]; |
14053 | if(limit != VK_WHOLE_SIZE) |
14054 | { |
14055 | m_HeapSizeLimit[heapIndex] = limit; |
14056 | if(limit < m_MemProps.memoryHeaps[heapIndex].size) |
14057 | { |
14058 | m_MemProps.memoryHeaps[heapIndex].size = limit; |
14059 | } |
14060 | } |
14061 | } |
14062 | } |
14063 | |
14064 | for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) |
14065 | { |
14066 | const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex); |
14067 | |
14068 | m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)( |
14069 | this, |
14070 | memTypeIndex, |
14071 | preferredBlockSize, |
14072 | 0, |
14073 | SIZE_MAX, |
14074 | GetBufferImageGranularity(), |
14075 | pCreateInfo->frameInUseCount, |
14076 | false, // isCustomPool |
14077 | false, // explicitBlockSize |
14078 | false); // linearAlgorithm |
14079 | // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here, |
14080 | // becase minBlockCount is 0. |
14081 | m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks())); |
14082 | |
14083 | } |
14084 | } |
14085 | |
14086 | VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) |
14087 | { |
14088 | VkResult res = VK_SUCCESS; |
14089 | |
14090 | if(pCreateInfo->pRecordSettings != VMA_NULL && |
14091 | !VmaStrIsEmpty(pStr: pCreateInfo->pRecordSettings->pFilePath)) |
14092 | { |
14093 | #if VMA_RECORDING_ENABLED |
14094 | m_pRecorder = vma_new(this, VmaRecorder)(); |
14095 | res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex); |
14096 | if(res != VK_SUCCESS) |
14097 | { |
14098 | return res; |
14099 | } |
14100 | m_pRecorder->WriteConfiguration( |
14101 | m_PhysicalDeviceProperties, |
14102 | m_MemProps, |
14103 | m_UseKhrDedicatedAllocation); |
14104 | m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex()); |
14105 | #else |
14106 | VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1." ); |
14107 | return VK_ERROR_FEATURE_NOT_PRESENT; |
14108 | #endif |
14109 | } |
14110 | |
14111 | return res; |
14112 | } |
14113 | |
14114 | VmaAllocator_T::~VmaAllocator_T() |
14115 | { |
14116 | #if VMA_RECORDING_ENABLED |
14117 | if(m_pRecorder != VMA_NULL) |
14118 | { |
14119 | m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex()); |
14120 | vma_delete(this, m_pRecorder); |
14121 | } |
14122 | #endif |
14123 | |
14124 | VMA_ASSERT(m_Pools.empty()); |
14125 | |
14126 | for(size_t i = GetMemoryTypeCount(); i--; ) |
14127 | { |
14128 | vma_delete(hAllocator: this, ptr: m_pDedicatedAllocations[i]); |
14129 | vma_delete(hAllocator: this, ptr: m_pBlockVectors[i]); |
14130 | } |
14131 | } |
14132 | |
14133 | void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions) |
14134 | { |
14135 | #if VMA_STATIC_VULKAN_FUNCTIONS == 1 |
14136 | m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties; |
14137 | m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties; |
14138 | m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory; |
14139 | m_VulkanFunctions.vkFreeMemory = &vkFreeMemory; |
14140 | m_VulkanFunctions.vkMapMemory = &vkMapMemory; |
14141 | m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory; |
14142 | m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges; |
14143 | m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges; |
14144 | m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory; |
14145 | m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory; |
14146 | m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements; |
14147 | m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements; |
14148 | m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer; |
14149 | m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer; |
14150 | m_VulkanFunctions.vkCreateImage = &vkCreateImage; |
14151 | m_VulkanFunctions.vkDestroyImage = &vkDestroyImage; |
14152 | m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer; |
14153 | #if VMA_DEDICATED_ALLOCATION |
14154 | if(m_UseKhrDedicatedAllocation) |
14155 | { |
14156 | m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = |
14157 | (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR" ); |
14158 | m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = |
14159 | (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR" ); |
14160 | } |
14161 | #endif // #if VMA_DEDICATED_ALLOCATION |
14162 | #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1 |
14163 | |
14164 | #define VMA_COPY_IF_NOT_NULL(funcName) \ |
14165 | if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName; |
14166 | |
14167 | if(pVulkanFunctions != VMA_NULL) |
14168 | { |
14169 | VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties); |
14170 | VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties); |
14171 | VMA_COPY_IF_NOT_NULL(vkAllocateMemory); |
14172 | VMA_COPY_IF_NOT_NULL(vkFreeMemory); |
14173 | VMA_COPY_IF_NOT_NULL(vkMapMemory); |
14174 | VMA_COPY_IF_NOT_NULL(vkUnmapMemory); |
14175 | VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges); |
14176 | VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges); |
14177 | VMA_COPY_IF_NOT_NULL(vkBindBufferMemory); |
14178 | VMA_COPY_IF_NOT_NULL(vkBindImageMemory); |
14179 | VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements); |
14180 | VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements); |
14181 | VMA_COPY_IF_NOT_NULL(vkCreateBuffer); |
14182 | VMA_COPY_IF_NOT_NULL(vkDestroyBuffer); |
14183 | VMA_COPY_IF_NOT_NULL(vkCreateImage); |
14184 | VMA_COPY_IF_NOT_NULL(vkDestroyImage); |
14185 | VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer); |
14186 | #if VMA_DEDICATED_ALLOCATION |
14187 | VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR); |
14188 | VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR); |
14189 | #endif |
14190 | } |
14191 | |
14192 | #undef VMA_COPY_IF_NOT_NULL |
14193 | |
14194 | // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1 |
14195 | // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions. |
14196 | VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL); |
14197 | VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL); |
14198 | VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL); |
14199 | VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL); |
14200 | VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL); |
14201 | VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL); |
14202 | VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL); |
14203 | VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL); |
14204 | VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL); |
14205 | VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL); |
14206 | VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL); |
14207 | VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL); |
14208 | VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL); |
14209 | VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL); |
14210 | VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL); |
14211 | VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL); |
14212 | VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL); |
14213 | #if VMA_DEDICATED_ALLOCATION |
14214 | if(m_UseKhrDedicatedAllocation) |
14215 | { |
14216 | VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL); |
14217 | VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL); |
14218 | } |
14219 | #endif |
14220 | } |
14221 | |
14222 | VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex) |
14223 | { |
14224 | const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); |
14225 | const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; |
14226 | const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE; |
14227 | return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize; |
14228 | } |
14229 | |
14230 | VkResult VmaAllocator_T::AllocateMemoryOfType( |
14231 | VkDeviceSize size, |
14232 | VkDeviceSize alignment, |
14233 | bool dedicatedAllocation, |
14234 | VkBuffer dedicatedBuffer, |
14235 | VkImage dedicatedImage, |
14236 | const VmaAllocationCreateInfo& createInfo, |
14237 | uint32_t memTypeIndex, |
14238 | VmaSuballocationType suballocType, |
14239 | size_t allocationCount, |
14240 | VmaAllocation* pAllocations) |
14241 | { |
14242 | VMA_ASSERT(pAllocations != VMA_NULL); |
14243 | VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu" , memTypeIndex, allocationCount, vkMemReq.size); |
14244 | |
14245 | VmaAllocationCreateInfo finalCreateInfo = createInfo; |
14246 | |
14247 | // If memory type is not HOST_VISIBLE, disable MAPPED. |
14248 | if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && |
14249 | (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) |
14250 | { |
14251 | finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; |
14252 | } |
14253 | |
14254 | VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex]; |
14255 | VMA_ASSERT(blockVector); |
14256 | |
14257 | const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize(); |
14258 | bool preferDedicatedMemory = |
14259 | VMA_DEBUG_ALWAYS_DEDICATED_MEMORY || |
14260 | dedicatedAllocation || |
14261 | // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size. |
14262 | size > preferredBlockSize / 2; |
14263 | |
14264 | if(preferDedicatedMemory && |
14265 | (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 && |
14266 | finalCreateInfo.pool == VK_NULL_HANDLE) |
14267 | { |
14268 | finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; |
14269 | } |
14270 | |
14271 | if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) |
14272 | { |
14273 | if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) |
14274 | { |
14275 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
14276 | } |
14277 | else |
14278 | { |
14279 | return AllocateDedicatedMemory( |
14280 | size, |
14281 | suballocType, |
14282 | memTypeIndex, |
14283 | map: (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, |
14284 | isUserDataString: (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, |
14285 | pUserData: finalCreateInfo.pUserData, |
14286 | dedicatedBuffer, |
14287 | dedicatedImage, |
14288 | allocationCount, |
14289 | pAllocations); |
14290 | } |
14291 | } |
14292 | else |
14293 | { |
14294 | VkResult res = blockVector->Allocate( |
14295 | VK_NULL_HANDLE, // hCurrentPool |
14296 | currentFrameIndex: m_CurrentFrameIndex.load(), |
14297 | size, |
14298 | alignment, |
14299 | createInfo: finalCreateInfo, |
14300 | suballocType, |
14301 | allocationCount, |
14302 | pAllocations); |
14303 | if(res == VK_SUCCESS) |
14304 | { |
14305 | return res; |
14306 | } |
14307 | |
14308 | // 5. Try dedicated memory. |
14309 | if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) |
14310 | { |
14311 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
14312 | } |
14313 | else |
14314 | { |
14315 | res = AllocateDedicatedMemory( |
14316 | size, |
14317 | suballocType, |
14318 | memTypeIndex, |
14319 | map: (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, |
14320 | isUserDataString: (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, |
14321 | pUserData: finalCreateInfo.pUserData, |
14322 | dedicatedBuffer, |
14323 | dedicatedImage, |
14324 | allocationCount, |
14325 | pAllocations); |
14326 | if(res == VK_SUCCESS) |
14327 | { |
14328 | // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here. |
14329 | VMA_DEBUG_LOG(" Allocated as DedicatedMemory" ); |
14330 | return VK_SUCCESS; |
14331 | } |
14332 | else |
14333 | { |
14334 | // Everything failed: Return error code. |
14335 | VMA_DEBUG_LOG(" vkAllocateMemory FAILED" ); |
14336 | return res; |
14337 | } |
14338 | } |
14339 | } |
14340 | } |
14341 | |
14342 | VkResult VmaAllocator_T::AllocateDedicatedMemory( |
14343 | VkDeviceSize size, |
14344 | VmaSuballocationType suballocType, |
14345 | uint32_t memTypeIndex, |
14346 | bool map, |
14347 | bool isUserDataString, |
14348 | void* pUserData, |
14349 | VkBuffer /*dedicatedBuffer*/, |
14350 | VkImage /*dedicatedImage*/, |
14351 | size_t allocationCount, |
14352 | VmaAllocation* pAllocations) |
14353 | { |
14354 | VMA_ASSERT(allocationCount > 0 && pAllocations); |
14355 | |
14356 | VkMemoryAllocateInfo allocInfo = {}; |
14357 | allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
14358 | allocInfo.memoryTypeIndex = memTypeIndex; |
14359 | allocInfo.allocationSize = size; |
14360 | |
14361 | #if VMA_DEDICATED_ALLOCATION |
14362 | VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = {}; |
14363 | dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; |
14364 | if(m_UseKhrDedicatedAllocation) |
14365 | { |
14366 | if(dedicatedBuffer != VK_NULL_HANDLE) |
14367 | { |
14368 | VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE); |
14369 | dedicatedAllocInfo.buffer = dedicatedBuffer; |
14370 | allocInfo.pNext = &dedicatedAllocInfo; |
14371 | } |
14372 | else if(dedicatedImage != VK_NULL_HANDLE) |
14373 | { |
14374 | dedicatedAllocInfo.image = dedicatedImage; |
14375 | allocInfo.pNext = &dedicatedAllocInfo; |
14376 | } |
14377 | } |
14378 | #endif // #if VMA_DEDICATED_ALLOCATION |
14379 | |
14380 | size_t allocIndex; |
14381 | VkResult res = VK_SUCCESS; |
14382 | for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) |
14383 | { |
14384 | res = AllocateDedicatedMemoryPage( |
14385 | size, |
14386 | suballocType, |
14387 | memTypeIndex, |
14388 | allocInfo, |
14389 | map, |
14390 | isUserDataString, |
14391 | pUserData, |
14392 | pAllocation: pAllocations + allocIndex); |
14393 | if(res != VK_SUCCESS) |
14394 | { |
14395 | break; |
14396 | } |
14397 | } |
14398 | |
14399 | if(res == VK_SUCCESS) |
14400 | { |
14401 | // Register them in m_pDedicatedAllocations. |
14402 | { |
14403 | VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); |
14404 | AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; |
14405 | VMA_ASSERT(pDedicatedAllocations); |
14406 | for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) |
14407 | { |
14408 | VmaVectorInsertSorted<VmaPointerLess>(vector&: *pDedicatedAllocations, value: pAllocations[allocIndex]); |
14409 | } |
14410 | } |
14411 | |
14412 | VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u" , allocationCount, memTypeIndex); |
14413 | } |
14414 | else |
14415 | { |
14416 | // Free all already created allocations. |
14417 | while(allocIndex--) |
14418 | { |
14419 | VmaAllocation currAlloc = pAllocations[allocIndex]; |
14420 | VkDeviceMemory hMemory = currAlloc->GetMemory(); |
14421 | |
14422 | /* |
14423 | There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory |
14424 | before vkFreeMemory. |
14425 | |
14426 | if(currAlloc->GetMappedData() != VMA_NULL) |
14427 | { |
14428 | (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); |
14429 | } |
14430 | */ |
14431 | |
14432 | FreeVulkanMemory(memoryType: memTypeIndex, size: currAlloc->GetSize(), hMemory); |
14433 | |
14434 | currAlloc->SetUserData(hAllocator: this, VMA_NULL); |
14435 | vma_delete(hAllocator: this, ptr: currAlloc); |
14436 | } |
14437 | |
14438 | memset(s: pAllocations, c: 0, n: sizeof(VmaAllocation) * allocationCount); |
14439 | } |
14440 | |
14441 | return res; |
14442 | } |
14443 | |
14444 | VkResult VmaAllocator_T::AllocateDedicatedMemoryPage( |
14445 | VkDeviceSize size, |
14446 | VmaSuballocationType suballocType, |
14447 | uint32_t memTypeIndex, |
14448 | const VkMemoryAllocateInfo& allocInfo, |
14449 | bool map, |
14450 | bool isUserDataString, |
14451 | void* pUserData, |
14452 | VmaAllocation* pAllocation) |
14453 | { |
14454 | VkDeviceMemory hMemory = VK_NULL_HANDLE; |
14455 | VkResult res = AllocateVulkanMemory(pAllocateInfo: &allocInfo, pMemory: &hMemory); |
14456 | if(res < 0) |
14457 | { |
14458 | VMA_DEBUG_LOG(" vkAllocateMemory FAILED" ); |
14459 | return res; |
14460 | } |
14461 | |
14462 | void* pMappedData = VMA_NULL; |
14463 | if(map) |
14464 | { |
14465 | res = (*m_VulkanFunctions.vkMapMemory)( |
14466 | m_hDevice, |
14467 | hMemory, |
14468 | 0, |
14469 | VK_WHOLE_SIZE, |
14470 | 0, |
14471 | &pMappedData); |
14472 | if(res < 0) |
14473 | { |
14474 | VMA_DEBUG_LOG(" vkMapMemory FAILED" ); |
14475 | FreeVulkanMemory(memoryType: memTypeIndex, size, hMemory); |
14476 | return res; |
14477 | } |
14478 | } |
14479 | |
14480 | *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString); |
14481 | (*pAllocation)->InitDedicatedAllocation(memoryTypeIndex: memTypeIndex, hMemory, suballocationType: suballocType, pMappedData, size); |
14482 | (*pAllocation)->SetUserData(hAllocator: this, pUserData); |
14483 | if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) |
14484 | { |
14485 | FillAllocation(hAllocation: *pAllocation, pattern: VMA_ALLOCATION_FILL_PATTERN_CREATED); |
14486 | } |
14487 | |
14488 | return VK_SUCCESS; |
14489 | } |
14490 | |
14491 | void VmaAllocator_T::GetBufferMemoryRequirements( |
14492 | VkBuffer hBuffer, |
14493 | VkMemoryRequirements& memReq, |
14494 | bool& requiresDedicatedAllocation, |
14495 | bool& prefersDedicatedAllocation) const |
14496 | { |
14497 | #if VMA_DEDICATED_ALLOCATION |
14498 | if(m_UseKhrDedicatedAllocation) |
14499 | { |
14500 | VkBufferMemoryRequirementsInfo2KHR memReqInfo = {}; |
14501 | memReqInfo.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR; |
14502 | memReqInfo.buffer = hBuffer; |
14503 | |
14504 | VkMemoryDedicatedRequirementsKHR memDedicatedReq = {}; |
14505 | memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR; |
14506 | |
14507 | VkMemoryRequirements2KHR memReq2 = {}; |
14508 | memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; |
14509 | memReq2.pNext = &memDedicatedReq; |
14510 | |
14511 | (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); |
14512 | |
14513 | memReq = memReq2.memoryRequirements; |
14514 | requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); |
14515 | prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); |
14516 | } |
14517 | else |
14518 | #endif // #if VMA_DEDICATED_ALLOCATION |
14519 | { |
14520 | (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq); |
14521 | requiresDedicatedAllocation = false; |
14522 | prefersDedicatedAllocation = false; |
14523 | } |
14524 | } |
14525 | |
14526 | void VmaAllocator_T::GetImageMemoryRequirements( |
14527 | VkImage hImage, |
14528 | VkMemoryRequirements& memReq, |
14529 | bool& requiresDedicatedAllocation, |
14530 | bool& prefersDedicatedAllocation) const |
14531 | { |
14532 | #if VMA_DEDICATED_ALLOCATION |
14533 | if(m_UseKhrDedicatedAllocation) |
14534 | { |
14535 | VkImageMemoryRequirementsInfo2KHR memReqInfo = {}; |
14536 | memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR; |
14537 | memReqInfo.image = hImage; |
14538 | |
14539 | VkMemoryDedicatedRequirementsKHR memDedicatedReq = {}; |
14540 | memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR; |
14541 | |
14542 | VkMemoryRequirements2KHR memReq2 = {}; |
14543 | memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; |
14544 | memReq2.pNext = &memDedicatedReq; |
14545 | |
14546 | (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); |
14547 | |
14548 | memReq = memReq2.memoryRequirements; |
14549 | requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); |
14550 | prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); |
14551 | } |
14552 | else |
14553 | #endif // #if VMA_DEDICATED_ALLOCATION |
14554 | { |
14555 | (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq); |
14556 | requiresDedicatedAllocation = false; |
14557 | prefersDedicatedAllocation = false; |
14558 | } |
14559 | } |
14560 | |
14561 | VkResult VmaAllocator_T::AllocateMemory( |
14562 | const VkMemoryRequirements& vkMemReq, |
14563 | bool requiresDedicatedAllocation, |
14564 | bool prefersDedicatedAllocation, |
14565 | VkBuffer dedicatedBuffer, |
14566 | VkImage dedicatedImage, |
14567 | const VmaAllocationCreateInfo& createInfo, |
14568 | VmaSuballocationType suballocType, |
14569 | size_t allocationCount, |
14570 | VmaAllocation* pAllocations) |
14571 | { |
14572 | memset(s: pAllocations, c: 0, n: sizeof(VmaAllocation) * allocationCount); |
14573 | |
14574 | VMA_ASSERT(VmaIsPow2(vkMemReq.alignment)); |
14575 | |
14576 | if(vkMemReq.size == 0) |
14577 | { |
14578 | return VK_ERROR_VALIDATION_FAILED_EXT; |
14579 | } |
14580 | if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && |
14581 | (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) |
14582 | { |
14583 | VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense." ); |
14584 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
14585 | } |
14586 | if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && |
14587 | (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0) |
14588 | { |
14589 | VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid." ); |
14590 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
14591 | } |
14592 | if(requiresDedicatedAllocation) |
14593 | { |
14594 | if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) |
14595 | { |
14596 | VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required." ); |
14597 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
14598 | } |
14599 | if(createInfo.pool != VK_NULL_HANDLE) |
14600 | { |
14601 | VMA_ASSERT(0 && "Pool specified while dedicated allocation is required." ); |
14602 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
14603 | } |
14604 | } |
14605 | if((createInfo.pool != VK_NULL_HANDLE) && |
14606 | ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0)) |
14607 | { |
14608 | VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid." ); |
14609 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
14610 | } |
14611 | |
14612 | if(createInfo.pool != VK_NULL_HANDLE) |
14613 | { |
14614 | const VkDeviceSize alignmentForPool = VMA_MAX( |
14615 | vkMemReq.alignment, |
14616 | GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex())); |
14617 | return createInfo.pool->m_BlockVector.Allocate( |
14618 | hCurrentPool: createInfo.pool, |
14619 | currentFrameIndex: m_CurrentFrameIndex.load(), |
14620 | size: vkMemReq.size, |
14621 | alignment: alignmentForPool, |
14622 | createInfo, |
14623 | suballocType, |
14624 | allocationCount, |
14625 | pAllocations); |
14626 | } |
14627 | else |
14628 | { |
14629 | // Bit mask of memory Vulkan types acceptable for this allocation. |
14630 | uint32_t memoryTypeBits = vkMemReq.memoryTypeBits; |
14631 | uint32_t memTypeIndex = UINT32_MAX; |
14632 | VkResult res = vmaFindMemoryTypeIndex(allocator: this, memoryTypeBits, pAllocationCreateInfo: &createInfo, pMemoryTypeIndex: &memTypeIndex); |
14633 | if(res == VK_SUCCESS) |
14634 | { |
14635 | VkDeviceSize alignmentForMemType = VMA_MAX( |
14636 | vkMemReq.alignment, |
14637 | GetMemoryTypeMinAlignment(memTypeIndex)); |
14638 | |
14639 | res = AllocateMemoryOfType( |
14640 | size: vkMemReq.size, |
14641 | alignment: alignmentForMemType, |
14642 | dedicatedAllocation: requiresDedicatedAllocation || prefersDedicatedAllocation, |
14643 | dedicatedBuffer, |
14644 | dedicatedImage, |
14645 | createInfo, |
14646 | memTypeIndex, |
14647 | suballocType, |
14648 | allocationCount, |
14649 | pAllocations); |
14650 | // Succeeded on first try. |
14651 | if(res == VK_SUCCESS) |
14652 | { |
14653 | return res; |
14654 | } |
14655 | // Allocation from this memory type failed. Try other compatible memory types. |
14656 | else |
14657 | { |
14658 | for(;;) |
14659 | { |
14660 | // Remove old memTypeIndex from list of possibilities. |
14661 | memoryTypeBits &= ~(1u << memTypeIndex); |
14662 | // Find alternative memTypeIndex. |
14663 | res = vmaFindMemoryTypeIndex(allocator: this, memoryTypeBits, pAllocationCreateInfo: &createInfo, pMemoryTypeIndex: &memTypeIndex); |
14664 | if(res == VK_SUCCESS) |
14665 | { |
14666 | alignmentForMemType = VMA_MAX( |
14667 | vkMemReq.alignment, |
14668 | GetMemoryTypeMinAlignment(memTypeIndex)); |
14669 | |
14670 | res = AllocateMemoryOfType( |
14671 | size: vkMemReq.size, |
14672 | alignment: alignmentForMemType, |
14673 | dedicatedAllocation: requiresDedicatedAllocation || prefersDedicatedAllocation, |
14674 | dedicatedBuffer, |
14675 | dedicatedImage, |
14676 | createInfo, |
14677 | memTypeIndex, |
14678 | suballocType, |
14679 | allocationCount, |
14680 | pAllocations); |
14681 | // Allocation from this alternative memory type succeeded. |
14682 | if(res == VK_SUCCESS) |
14683 | { |
14684 | return res; |
14685 | } |
14686 | // else: Allocation from this memory type failed. Try next one - next loop iteration. |
14687 | } |
14688 | // No other matching memory type index could be found. |
14689 | else |
14690 | { |
14691 | // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once. |
14692 | return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
14693 | } |
14694 | } |
14695 | } |
14696 | } |
14697 | // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT. |
14698 | else |
14699 | return res; |
14700 | } |
14701 | } |
14702 | |
14703 | void VmaAllocator_T::FreeMemory( |
14704 | size_t allocationCount, |
14705 | const VmaAllocation* pAllocations) |
14706 | { |
14707 | VMA_ASSERT(pAllocations); |
14708 | |
14709 | for(size_t allocIndex = allocationCount; allocIndex--; ) |
14710 | { |
14711 | VmaAllocation allocation = pAllocations[allocIndex]; |
14712 | |
14713 | if(allocation != VK_NULL_HANDLE) |
14714 | { |
14715 | if(TouchAllocation(hAllocation: allocation)) |
14716 | { |
14717 | if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) |
14718 | { |
14719 | FillAllocation(hAllocation: allocation, pattern: VMA_ALLOCATION_FILL_PATTERN_DESTROYED); |
14720 | } |
14721 | |
14722 | switch(allocation->GetType()) |
14723 | { |
14724 | case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: |
14725 | { |
14726 | VmaBlockVector* pBlockVector = VMA_NULL; |
14727 | VmaPool hPool = allocation->GetPool(); |
14728 | if(hPool != VK_NULL_HANDLE) |
14729 | { |
14730 | pBlockVector = &hPool->m_BlockVector; |
14731 | } |
14732 | else |
14733 | { |
14734 | const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); |
14735 | pBlockVector = m_pBlockVectors[memTypeIndex]; |
14736 | } |
14737 | pBlockVector->Free(hAllocation: allocation); |
14738 | } |
14739 | break; |
14740 | case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: |
14741 | FreeDedicatedMemory(allocation); |
14742 | break; |
14743 | default: |
14744 | VMA_ASSERT(0); |
14745 | } |
14746 | } |
14747 | |
14748 | allocation->SetUserData(hAllocator: this, VMA_NULL); |
14749 | vma_delete(hAllocator: this, ptr: allocation); |
14750 | } |
14751 | } |
14752 | } |
14753 | |
14754 | VkResult VmaAllocator_T::ResizeAllocation( |
14755 | const VmaAllocation alloc, |
14756 | VkDeviceSize newSize) |
14757 | { |
14758 | if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST) |
14759 | { |
14760 | return VK_ERROR_VALIDATION_FAILED_EXT; |
14761 | } |
14762 | if(newSize == alloc->GetSize()) |
14763 | { |
14764 | return VK_SUCCESS; |
14765 | } |
14766 | |
14767 | switch(alloc->GetType()) |
14768 | { |
14769 | case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: |
14770 | return VK_ERROR_FEATURE_NOT_PRESENT; |
14771 | case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: |
14772 | if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize)) |
14773 | { |
14774 | alloc->ChangeSize(newSize); |
14775 | VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate()); |
14776 | return VK_SUCCESS; |
14777 | } |
14778 | else |
14779 | { |
14780 | return VkResult(-1000069000); // VK_ERROR_OUT_OF_POOL_MEMORY |
14781 | } |
14782 | default: |
14783 | VMA_ASSERT(0); |
14784 | return VK_ERROR_VALIDATION_FAILED_EXT; |
14785 | } |
14786 | } |
14787 | |
14788 | void VmaAllocator_T::CalculateStats(VmaStats* pStats) |
14789 | { |
14790 | // Initialize. |
14791 | InitStatInfo(outInfo&: pStats->total); |
14792 | for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) |
14793 | InitStatInfo(outInfo&: pStats->memoryType[i]); |
14794 | for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) |
14795 | InitStatInfo(outInfo&: pStats->memoryHeap[i]); |
14796 | |
14797 | // Process default pools. |
14798 | for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) |
14799 | { |
14800 | VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; |
14801 | VMA_ASSERT(pBlockVector); |
14802 | pBlockVector->AddStats(pStats); |
14803 | } |
14804 | |
14805 | // Process custom pools. |
14806 | { |
14807 | VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); |
14808 | for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex) |
14809 | { |
14810 | m_Pools[poolIndex]->m_BlockVector.AddStats(pStats); |
14811 | } |
14812 | } |
14813 | |
14814 | // Process dedicated allocations. |
14815 | for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) |
14816 | { |
14817 | const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); |
14818 | VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); |
14819 | AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex]; |
14820 | VMA_ASSERT(pDedicatedAllocVector); |
14821 | for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex) |
14822 | { |
14823 | VmaStatInfo allocationStatInfo; |
14824 | (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(outInfo&: allocationStatInfo); |
14825 | VmaAddStatInfo(inoutInfo&: pStats->total, srcInfo: allocationStatInfo); |
14826 | VmaAddStatInfo(inoutInfo&: pStats->memoryType[memTypeIndex], srcInfo: allocationStatInfo); |
14827 | VmaAddStatInfo(inoutInfo&: pStats->memoryHeap[memHeapIndex], srcInfo: allocationStatInfo); |
14828 | } |
14829 | } |
14830 | |
14831 | // Postprocess. |
14832 | VmaPostprocessCalcStatInfo(inoutInfo&: pStats->total); |
14833 | for(size_t i = 0; i < GetMemoryTypeCount(); ++i) |
14834 | VmaPostprocessCalcStatInfo(inoutInfo&: pStats->memoryType[i]); |
14835 | for(size_t i = 0; i < GetMemoryHeapCount(); ++i) |
14836 | VmaPostprocessCalcStatInfo(inoutInfo&: pStats->memoryHeap[i]); |
14837 | } |
14838 | |
14839 | static const uint32_t VMA_VENDOR_ID_AMD = 4098; |
14840 | |
14841 | VkResult VmaAllocator_T::DefragmentationBegin( |
14842 | const VmaDefragmentationInfo2& info, |
14843 | VmaDefragmentationStats* pStats, |
14844 | VmaDefragmentationContext* pContext) |
14845 | { |
14846 | if(info.pAllocationsChanged != VMA_NULL) |
14847 | { |
14848 | memset(s: info.pAllocationsChanged, c: 0, n: info.allocationCount * sizeof(VkBool32)); |
14849 | } |
14850 | |
14851 | *pContext = vma_new(this, VmaDefragmentationContext_T)( |
14852 | this, m_CurrentFrameIndex.load(), info.flags, pStats); |
14853 | |
14854 | (*pContext)->AddPools(poolCount: info.poolCount, pPools: info.pPools); |
14855 | (*pContext)->AddAllocations( |
14856 | allocationCount: info.allocationCount, pAllocations: info.pAllocations, pAllocationsChanged: info.pAllocationsChanged); |
14857 | |
14858 | VkResult res = (*pContext)->Defragment( |
14859 | maxCpuBytesToMove: info.maxCpuBytesToMove, maxCpuAllocationsToMove: info.maxCpuAllocationsToMove, |
14860 | maxGpuBytesToMove: info.maxGpuBytesToMove, maxGpuAllocationsToMove: info.maxGpuAllocationsToMove, |
14861 | commandBuffer: info.commandBuffer, pStats); |
14862 | |
14863 | if(res != VK_NOT_READY) |
14864 | { |
14865 | vma_delete(hAllocator: this, ptr: *pContext); |
14866 | *pContext = VMA_NULL; |
14867 | } |
14868 | |
14869 | return res; |
14870 | } |
14871 | |
14872 | VkResult VmaAllocator_T::DefragmentationEnd( |
14873 | VmaDefragmentationContext context) |
14874 | { |
14875 | vma_delete(hAllocator: this, ptr: context); |
14876 | return VK_SUCCESS; |
14877 | } |
14878 | |
14879 | void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo) |
14880 | { |
14881 | if(hAllocation->CanBecomeLost()) |
14882 | { |
14883 | /* |
14884 | Warning: This is a carefully designed algorithm. |
14885 | Do not modify unless you really know what you're doing :) |
14886 | */ |
14887 | const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); |
14888 | uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); |
14889 | for(;;) |
14890 | { |
14891 | if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST) |
14892 | { |
14893 | pAllocationInfo->memoryType = UINT32_MAX; |
14894 | pAllocationInfo->deviceMemory = VK_NULL_HANDLE; |
14895 | pAllocationInfo->offset = 0; |
14896 | pAllocationInfo->size = hAllocation->GetSize(); |
14897 | pAllocationInfo->pMappedData = VMA_NULL; |
14898 | pAllocationInfo->pUserData = hAllocation->GetUserData(); |
14899 | return; |
14900 | } |
14901 | else if(localLastUseFrameIndex == localCurrFrameIndex) |
14902 | { |
14903 | pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); |
14904 | pAllocationInfo->deviceMemory = hAllocation->GetMemory(); |
14905 | pAllocationInfo->offset = hAllocation->GetOffset(); |
14906 | pAllocationInfo->size = hAllocation->GetSize(); |
14907 | pAllocationInfo->pMappedData = VMA_NULL; |
14908 | pAllocationInfo->pUserData = hAllocation->GetUserData(); |
14909 | return; |
14910 | } |
14911 | else // Last use time earlier than current time. |
14912 | { |
14913 | if(hAllocation->CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: localCurrFrameIndex)) |
14914 | { |
14915 | localLastUseFrameIndex = localCurrFrameIndex; |
14916 | } |
14917 | } |
14918 | } |
14919 | } |
14920 | else |
14921 | { |
14922 | #if VMA_STATS_STRING_ENABLED |
14923 | uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); |
14924 | uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); |
14925 | for(;;) |
14926 | { |
14927 | VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST); |
14928 | if(localLastUseFrameIndex == localCurrFrameIndex) |
14929 | { |
14930 | break; |
14931 | } |
14932 | else // Last use time earlier than current time. |
14933 | { |
14934 | if(hAllocation->CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: localCurrFrameIndex)) |
14935 | { |
14936 | localLastUseFrameIndex = localCurrFrameIndex; |
14937 | } |
14938 | } |
14939 | } |
14940 | #endif |
14941 | |
14942 | pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); |
14943 | pAllocationInfo->deviceMemory = hAllocation->GetMemory(); |
14944 | pAllocationInfo->offset = hAllocation->GetOffset(); |
14945 | pAllocationInfo->size = hAllocation->GetSize(); |
14946 | pAllocationInfo->pMappedData = hAllocation->GetMappedData(); |
14947 | pAllocationInfo->pUserData = hAllocation->GetUserData(); |
14948 | } |
14949 | } |
14950 | |
14951 | bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation) |
14952 | { |
14953 | // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo. |
14954 | if(hAllocation->CanBecomeLost()) |
14955 | { |
14956 | uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); |
14957 | uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); |
14958 | for(;;) |
14959 | { |
14960 | if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST) |
14961 | { |
14962 | return false; |
14963 | } |
14964 | else if(localLastUseFrameIndex == localCurrFrameIndex) |
14965 | { |
14966 | return true; |
14967 | } |
14968 | else // Last use time earlier than current time. |
14969 | { |
14970 | if(hAllocation->CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: localCurrFrameIndex)) |
14971 | { |
14972 | localLastUseFrameIndex = localCurrFrameIndex; |
14973 | } |
14974 | } |
14975 | } |
14976 | } |
14977 | else |
14978 | { |
14979 | #if VMA_STATS_STRING_ENABLED |
14980 | uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); |
14981 | uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); |
14982 | for(;;) |
14983 | { |
14984 | VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST); |
14985 | if(localLastUseFrameIndex == localCurrFrameIndex) |
14986 | { |
14987 | break; |
14988 | } |
14989 | else // Last use time earlier than current time. |
14990 | { |
14991 | if(hAllocation->CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: localCurrFrameIndex)) |
14992 | { |
14993 | localLastUseFrameIndex = localCurrFrameIndex; |
14994 | } |
14995 | } |
14996 | } |
14997 | #endif |
14998 | |
14999 | return true; |
15000 | } |
15001 | } |
15002 | |
15003 | VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool) |
15004 | { |
15005 | VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u" , pCreateInfo->memoryTypeIndex, pCreateInfo->flags); |
15006 | |
15007 | VmaPoolCreateInfo newCreateInfo = *pCreateInfo; |
15008 | |
15009 | if(newCreateInfo.maxBlockCount == 0) |
15010 | { |
15011 | newCreateInfo.maxBlockCount = SIZE_MAX; |
15012 | } |
15013 | if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount) |
15014 | { |
15015 | return VK_ERROR_INITIALIZATION_FAILED; |
15016 | } |
15017 | |
15018 | const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex: newCreateInfo.memoryTypeIndex); |
15019 | |
15020 | *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize); |
15021 | |
15022 | VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks(); |
15023 | if(res != VK_SUCCESS) |
15024 | { |
15025 | vma_delete(hAllocator: this, ptr: *pPool); |
15026 | *pPool = VMA_NULL; |
15027 | return res; |
15028 | } |
15029 | |
15030 | // Add to m_Pools. |
15031 | { |
15032 | VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); |
15033 | (*pPool)->SetId(m_NextPoolId++); |
15034 | VmaVectorInsertSorted<VmaPointerLess>(vector&: m_Pools, value: *pPool); |
15035 | } |
15036 | |
15037 | return VK_SUCCESS; |
15038 | } |
15039 | |
15040 | void VmaAllocator_T::DestroyPool(VmaPool pool) |
15041 | { |
15042 | // Remove from m_Pools. |
15043 | { |
15044 | VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); |
15045 | bool success = VmaVectorRemoveSorted<VmaPointerLess>(vector&: m_Pools, value: pool); |
15046 | (void) success; |
15047 | VMA_ASSERT(success && "Pool not found in Allocator." ); |
15048 | } |
15049 | |
15050 | vma_delete(hAllocator: this, ptr: pool); |
15051 | } |
15052 | |
15053 | void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats) |
15054 | { |
15055 | pool->m_BlockVector.GetPoolStats(pStats: pPoolStats); |
15056 | } |
15057 | |
15058 | void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex) |
15059 | { |
15060 | m_CurrentFrameIndex.store(i: frameIndex); |
15061 | } |
15062 | |
15063 | void VmaAllocator_T::MakePoolAllocationsLost( |
15064 | VmaPool hPool, |
15065 | size_t* pLostAllocationCount) |
15066 | { |
15067 | hPool->m_BlockVector.MakePoolAllocationsLost( |
15068 | currentFrameIndex: m_CurrentFrameIndex.load(), |
15069 | pLostAllocationCount); |
15070 | } |
15071 | |
15072 | VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool) |
15073 | { |
15074 | return hPool->m_BlockVector.CheckCorruption(); |
15075 | } |
15076 | |
15077 | VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits) |
15078 | { |
15079 | VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT; |
15080 | |
15081 | // Process default pools. |
15082 | for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) |
15083 | { |
15084 | if(((1u << memTypeIndex) & memoryTypeBits) != 0) |
15085 | { |
15086 | VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; |
15087 | VMA_ASSERT(pBlockVector); |
15088 | VkResult localRes = pBlockVector->CheckCorruption(); |
15089 | switch(localRes) |
15090 | { |
15091 | case VK_ERROR_FEATURE_NOT_PRESENT: |
15092 | break; |
15093 | case VK_SUCCESS: |
15094 | finalRes = VK_SUCCESS; |
15095 | break; |
15096 | default: |
15097 | return localRes; |
15098 | } |
15099 | } |
15100 | } |
15101 | |
15102 | // Process custom pools. |
15103 | { |
15104 | VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); |
15105 | for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex) |
15106 | { |
15107 | if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0) |
15108 | { |
15109 | VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption(); |
15110 | switch(localRes) |
15111 | { |
15112 | case VK_ERROR_FEATURE_NOT_PRESENT: |
15113 | break; |
15114 | case VK_SUCCESS: |
15115 | finalRes = VK_SUCCESS; |
15116 | break; |
15117 | default: |
15118 | return localRes; |
15119 | } |
15120 | } |
15121 | } |
15122 | } |
15123 | |
15124 | return finalRes; |
15125 | } |
15126 | |
15127 | void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation) |
15128 | { |
15129 | *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false); |
15130 | (*pAllocation)->InitLost(); |
15131 | } |
15132 | |
15133 | VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory) |
15134 | { |
15135 | const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex: pAllocateInfo->memoryTypeIndex); |
15136 | |
15137 | VkResult res; |
15138 | if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE) |
15139 | { |
15140 | VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex); |
15141 | if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize) |
15142 | { |
15143 | res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); |
15144 | if(res == VK_SUCCESS) |
15145 | { |
15146 | m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize; |
15147 | } |
15148 | } |
15149 | else |
15150 | { |
15151 | res = VK_ERROR_OUT_OF_DEVICE_MEMORY; |
15152 | } |
15153 | } |
15154 | else |
15155 | { |
15156 | res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); |
15157 | } |
15158 | |
15159 | if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) |
15160 | { |
15161 | (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize); |
15162 | } |
15163 | |
15164 | return res; |
15165 | } |
15166 | |
15167 | void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory) |
15168 | { |
15169 | if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL) |
15170 | { |
15171 | (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size); |
15172 | } |
15173 | |
15174 | (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks()); |
15175 | |
15176 | const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex: memoryType); |
15177 | if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE) |
15178 | { |
15179 | VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex); |
15180 | m_HeapSizeLimit[heapIndex] += size; |
15181 | } |
15182 | } |
15183 | |
15184 | VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData) |
15185 | { |
15186 | if(hAllocation->CanBecomeLost()) |
15187 | { |
15188 | return VK_ERROR_MEMORY_MAP_FAILED; |
15189 | } |
15190 | |
15191 | switch(hAllocation->GetType()) |
15192 | { |
15193 | case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: |
15194 | { |
15195 | VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); |
15196 | char *pBytes = VMA_NULL; |
15197 | VkResult res = pBlock->Map(hAllocator: this, count: 1, ppData: (void**)&pBytes); |
15198 | if(res == VK_SUCCESS) |
15199 | { |
15200 | *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset(); |
15201 | hAllocation->BlockAllocMap(); |
15202 | } |
15203 | return res; |
15204 | } |
15205 | case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: |
15206 | return hAllocation->DedicatedAllocMap(hAllocator: this, ppData); |
15207 | default: |
15208 | VMA_ASSERT(0); |
15209 | return VK_ERROR_MEMORY_MAP_FAILED; |
15210 | } |
15211 | } |
15212 | |
15213 | void VmaAllocator_T::Unmap(VmaAllocation hAllocation) |
15214 | { |
15215 | switch(hAllocation->GetType()) |
15216 | { |
15217 | case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: |
15218 | { |
15219 | VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); |
15220 | hAllocation->BlockAllocUnmap(); |
15221 | pBlock->Unmap(hAllocator: this, count: 1); |
15222 | } |
15223 | break; |
15224 | case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: |
15225 | hAllocation->DedicatedAllocUnmap(hAllocator: this); |
15226 | break; |
15227 | default: |
15228 | VMA_ASSERT(0); |
15229 | } |
15230 | } |
15231 | |
15232 | VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer) |
15233 | { |
15234 | VkResult res = VK_SUCCESS; |
15235 | switch(hAllocation->GetType()) |
15236 | { |
15237 | case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: |
15238 | res = GetVulkanFunctions().vkBindBufferMemory( |
15239 | m_hDevice, |
15240 | hBuffer, |
15241 | hAllocation->GetMemory(), |
15242 | 0); //memoryOffset |
15243 | break; |
15244 | case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: |
15245 | { |
15246 | VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); |
15247 | VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?" ); |
15248 | res = pBlock->BindBufferMemory(hAllocator: this, hAllocation, hBuffer); |
15249 | break; |
15250 | } |
15251 | default: |
15252 | VMA_ASSERT(0); |
15253 | } |
15254 | return res; |
15255 | } |
15256 | |
15257 | VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage) |
15258 | { |
15259 | VkResult res = VK_SUCCESS; |
15260 | switch(hAllocation->GetType()) |
15261 | { |
15262 | case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: |
15263 | res = GetVulkanFunctions().vkBindImageMemory( |
15264 | m_hDevice, |
15265 | hImage, |
15266 | hAllocation->GetMemory(), |
15267 | 0); //memoryOffset |
15268 | break; |
15269 | case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: |
15270 | { |
15271 | VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); |
15272 | VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?" ); |
15273 | res = pBlock->BindImageMemory(hAllocator: this, hAllocation, hImage); |
15274 | break; |
15275 | } |
15276 | default: |
15277 | VMA_ASSERT(0); |
15278 | } |
15279 | return res; |
15280 | } |
15281 | |
15282 | void VmaAllocator_T::FlushOrInvalidateAllocation( |
15283 | VmaAllocation hAllocation, |
15284 | VkDeviceSize offset, VkDeviceSize size, |
15285 | VMA_CACHE_OPERATION op) |
15286 | { |
15287 | const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex(); |
15288 | if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex)) |
15289 | { |
15290 | const VkDeviceSize allocationSize = hAllocation->GetSize(); |
15291 | VMA_ASSERT(offset <= allocationSize); |
15292 | |
15293 | const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; |
15294 | |
15295 | VkMappedMemoryRange memRange = {}; |
15296 | memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; |
15297 | memRange.memory = hAllocation->GetMemory(); |
15298 | |
15299 | switch(hAllocation->GetType()) |
15300 | { |
15301 | case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: |
15302 | memRange.offset = VmaAlignDown(val: offset, align: nonCoherentAtomSize); |
15303 | if(size == VK_WHOLE_SIZE) |
15304 | { |
15305 | memRange.size = allocationSize - memRange.offset; |
15306 | } |
15307 | else |
15308 | { |
15309 | VMA_ASSERT(offset + size <= allocationSize); |
15310 | memRange.size = VMA_MIN( |
15311 | VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize), |
15312 | allocationSize - memRange.offset); |
15313 | } |
15314 | break; |
15315 | |
15316 | case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: |
15317 | { |
15318 | // 1. Still within this allocation. |
15319 | memRange.offset = VmaAlignDown(val: offset, align: nonCoherentAtomSize); |
15320 | if(size == VK_WHOLE_SIZE) |
15321 | { |
15322 | size = allocationSize - offset; |
15323 | } |
15324 | else |
15325 | { |
15326 | VMA_ASSERT(offset + size <= allocationSize); |
15327 | } |
15328 | memRange.size = VmaAlignUp(val: size + (offset - memRange.offset), align: nonCoherentAtomSize); |
15329 | |
15330 | // 2. Adjust to whole block. |
15331 | const VkDeviceSize allocationOffset = hAllocation->GetOffset(); |
15332 | VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0); |
15333 | const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize(); |
15334 | memRange.offset += allocationOffset; |
15335 | memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset); |
15336 | |
15337 | break; |
15338 | } |
15339 | |
15340 | default: |
15341 | VMA_ASSERT(0); |
15342 | } |
15343 | |
15344 | switch(op) |
15345 | { |
15346 | case VMA_CACHE_FLUSH: |
15347 | (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange); |
15348 | break; |
15349 | case VMA_CACHE_INVALIDATE: |
15350 | (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange); |
15351 | break; |
15352 | default: |
15353 | VMA_ASSERT(0); |
15354 | } |
15355 | } |
15356 | // else: Just ignore this call. |
15357 | } |
15358 | |
15359 | void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation) |
15360 | { |
15361 | VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); |
15362 | |
15363 | const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); |
15364 | { |
15365 | VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); |
15366 | AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; |
15367 | VMA_ASSERT(pDedicatedAllocations); |
15368 | bool success = VmaVectorRemoveSorted<VmaPointerLess>(vector&: *pDedicatedAllocations, value: allocation); |
15369 | (void) success; |
15370 | VMA_ASSERT(success); |
15371 | } |
15372 | |
15373 | VkDeviceMemory hMemory = allocation->GetMemory(); |
15374 | |
15375 | /* |
15376 | There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory |
15377 | before vkFreeMemory. |
15378 | |
15379 | if(allocation->GetMappedData() != VMA_NULL) |
15380 | { |
15381 | (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); |
15382 | } |
15383 | */ |
15384 | |
15385 | FreeVulkanMemory(memoryType: memTypeIndex, size: allocation->GetSize(), hMemory); |
15386 | |
15387 | VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u" , memTypeIndex); |
15388 | } |
15389 | |
15390 | void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern) |
15391 | { |
15392 | if(VMA_DEBUG_INITIALIZE_ALLOCATIONS && |
15393 | !hAllocation->CanBecomeLost() && |
15394 | (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) |
15395 | { |
15396 | void* pData = VMA_NULL; |
15397 | VkResult res = Map(hAllocation, ppData: &pData); |
15398 | if(res == VK_SUCCESS) |
15399 | { |
15400 | memset(s: pData, c: (int)pattern, n: (size_t)hAllocation->GetSize()); |
15401 | FlushOrInvalidateAllocation(hAllocation, offset: 0, VK_WHOLE_SIZE, op: VMA_CACHE_FLUSH); |
15402 | Unmap(hAllocation); |
15403 | } |
15404 | else |
15405 | { |
15406 | VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation." ); |
15407 | } |
15408 | } |
15409 | } |
15410 | |
15411 | #if VMA_STATS_STRING_ENABLED |
15412 | |
15413 | void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) |
15414 | { |
15415 | bool dedicatedAllocationsStarted = false; |
15416 | for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) |
15417 | { |
15418 | VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); |
15419 | AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex]; |
15420 | VMA_ASSERT(pDedicatedAllocVector); |
15421 | if(pDedicatedAllocVector->empty() == false) |
15422 | { |
15423 | if(dedicatedAllocationsStarted == false) |
15424 | { |
15425 | dedicatedAllocationsStarted = true; |
15426 | json.WriteString(pStr: "DedicatedAllocations" ); |
15427 | json.BeginObject(); |
15428 | } |
15429 | |
15430 | json.BeginString(pStr: "Type " ); |
15431 | json.ContinueString(n: memTypeIndex); |
15432 | json.EndString(); |
15433 | |
15434 | json.BeginArray(); |
15435 | |
15436 | for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i) |
15437 | { |
15438 | json.BeginObject(singleLine: true); |
15439 | const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i]; |
15440 | hAlloc->PrintParameters(json); |
15441 | json.EndObject(); |
15442 | } |
15443 | |
15444 | json.EndArray(); |
15445 | } |
15446 | } |
15447 | if(dedicatedAllocationsStarted) |
15448 | { |
15449 | json.EndObject(); |
15450 | } |
15451 | |
15452 | { |
15453 | bool allocationsStarted = false; |
15454 | for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) |
15455 | { |
15456 | if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false) |
15457 | { |
15458 | if(allocationsStarted == false) |
15459 | { |
15460 | allocationsStarted = true; |
15461 | json.WriteString(pStr: "DefaultPools" ); |
15462 | json.BeginObject(); |
15463 | } |
15464 | |
15465 | json.BeginString(pStr: "Type " ); |
15466 | json.ContinueString(n: memTypeIndex); |
15467 | json.EndString(); |
15468 | |
15469 | m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json); |
15470 | } |
15471 | } |
15472 | if(allocationsStarted) |
15473 | { |
15474 | json.EndObject(); |
15475 | } |
15476 | } |
15477 | |
15478 | // Custom pools |
15479 | { |
15480 | VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); |
15481 | const size_t poolCount = m_Pools.size(); |
15482 | if(poolCount > 0) |
15483 | { |
15484 | json.WriteString(pStr: "Pools" ); |
15485 | json.BeginObject(); |
15486 | for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex) |
15487 | { |
15488 | json.BeginString(); |
15489 | json.ContinueString(n: m_Pools[poolIndex]->GetId()); |
15490 | json.EndString(); |
15491 | |
15492 | m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json); |
15493 | } |
15494 | json.EndObject(); |
15495 | } |
15496 | } |
15497 | } |
15498 | |
15499 | #endif // #if VMA_STATS_STRING_ENABLED |
15500 | |
15501 | //////////////////////////////////////////////////////////////////////////////// |
15502 | // Public interface |
15503 | |
15504 | VkResult vmaCreateAllocator( |
15505 | const VmaAllocatorCreateInfo* pCreateInfo, |
15506 | VmaAllocator* pAllocator) |
15507 | { |
15508 | VMA_ASSERT(pCreateInfo && pAllocator); |
15509 | VMA_DEBUG_LOG("vmaCreateAllocator" ); |
15510 | *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo); |
15511 | return (*pAllocator)->Init(pCreateInfo); |
15512 | } |
15513 | |
15514 | void vmaDestroyAllocator( |
15515 | VmaAllocator allocator) |
15516 | { |
15517 | if(allocator != VK_NULL_HANDLE) |
15518 | { |
15519 | VMA_DEBUG_LOG("vmaDestroyAllocator" ); |
15520 | VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; |
15521 | vma_delete(pAllocationCallbacks: &allocationCallbacks, ptr: allocator); |
15522 | } |
15523 | } |
15524 | |
15525 | void vmaGetPhysicalDeviceProperties( |
15526 | VmaAllocator allocator, |
15527 | const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties) |
15528 | { |
15529 | VMA_ASSERT(allocator && ppPhysicalDeviceProperties); |
15530 | *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties; |
15531 | } |
15532 | |
15533 | void vmaGetMemoryProperties( |
15534 | VmaAllocator allocator, |
15535 | const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties) |
15536 | { |
15537 | VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties); |
15538 | *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps; |
15539 | } |
15540 | |
15541 | void vmaGetMemoryTypeProperties( |
15542 | VmaAllocator allocator, |
15543 | uint32_t memoryTypeIndex, |
15544 | VkMemoryPropertyFlags* pFlags) |
15545 | { |
15546 | VMA_ASSERT(allocator && pFlags); |
15547 | VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount()); |
15548 | *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags; |
15549 | } |
15550 | |
15551 | void vmaSetCurrentFrameIndex( |
15552 | VmaAllocator allocator, |
15553 | uint32_t frameIndex) |
15554 | { |
15555 | VMA_ASSERT(allocator); |
15556 | VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST); |
15557 | |
15558 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15559 | |
15560 | allocator->SetCurrentFrameIndex(frameIndex); |
15561 | } |
15562 | |
15563 | void vmaCalculateStats( |
15564 | VmaAllocator allocator, |
15565 | VmaStats* pStats) |
15566 | { |
15567 | VMA_ASSERT(allocator && pStats); |
15568 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15569 | allocator->CalculateStats(pStats); |
15570 | } |
15571 | |
15572 | #if VMA_STATS_STRING_ENABLED |
15573 | |
15574 | void vmaBuildStatsString( |
15575 | VmaAllocator allocator, |
15576 | char** ppStatsString, |
15577 | VkBool32 detailedMap) |
15578 | { |
15579 | VMA_ASSERT(allocator && ppStatsString); |
15580 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15581 | |
15582 | VmaStringBuilder sb(allocator); |
15583 | { |
15584 | VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb); |
15585 | json.BeginObject(); |
15586 | |
15587 | VmaStats stats; |
15588 | allocator->CalculateStats(pStats: &stats); |
15589 | |
15590 | json.WriteString(pStr: "Total" ); |
15591 | VmaPrintStatInfo(json, stat: stats.total); |
15592 | |
15593 | for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex) |
15594 | { |
15595 | json.BeginString(pStr: "Heap " ); |
15596 | json.ContinueString(n: heapIndex); |
15597 | json.EndString(); |
15598 | json.BeginObject(); |
15599 | |
15600 | json.WriteString(pStr: "Size" ); |
15601 | json.WriteNumber(n: allocator->m_MemProps.memoryHeaps[heapIndex].size); |
15602 | |
15603 | json.WriteString(pStr: "Flags" ); |
15604 | json.BeginArray(singleLine: true); |
15605 | if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) |
15606 | { |
15607 | json.WriteString(pStr: "DEVICE_LOCAL" ); |
15608 | } |
15609 | json.EndArray(); |
15610 | |
15611 | if(stats.memoryHeap[heapIndex].blockCount > 0) |
15612 | { |
15613 | json.WriteString(pStr: "Stats" ); |
15614 | VmaPrintStatInfo(json, stat: stats.memoryHeap[heapIndex]); |
15615 | } |
15616 | |
15617 | for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex) |
15618 | { |
15619 | if(allocator->MemoryTypeIndexToHeapIndex(memTypeIndex: typeIndex) == heapIndex) |
15620 | { |
15621 | json.BeginString(pStr: "Type " ); |
15622 | json.ContinueString(n: typeIndex); |
15623 | json.EndString(); |
15624 | |
15625 | json.BeginObject(); |
15626 | |
15627 | json.WriteString(pStr: "Flags" ); |
15628 | json.BeginArray(singleLine: true); |
15629 | VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags; |
15630 | if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) |
15631 | { |
15632 | json.WriteString(pStr: "DEVICE_LOCAL" ); |
15633 | } |
15634 | if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) |
15635 | { |
15636 | json.WriteString(pStr: "HOST_VISIBLE" ); |
15637 | } |
15638 | if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0) |
15639 | { |
15640 | json.WriteString(pStr: "HOST_COHERENT" ); |
15641 | } |
15642 | if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) |
15643 | { |
15644 | json.WriteString(pStr: "HOST_CACHED" ); |
15645 | } |
15646 | if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0) |
15647 | { |
15648 | json.WriteString(pStr: "LAZILY_ALLOCATED" ); |
15649 | } |
15650 | json.EndArray(); |
15651 | |
15652 | if(stats.memoryType[typeIndex].blockCount > 0) |
15653 | { |
15654 | json.WriteString(pStr: "Stats" ); |
15655 | VmaPrintStatInfo(json, stat: stats.memoryType[typeIndex]); |
15656 | } |
15657 | |
15658 | json.EndObject(); |
15659 | } |
15660 | } |
15661 | |
15662 | json.EndObject(); |
15663 | } |
15664 | if(detailedMap == VK_TRUE) |
15665 | { |
15666 | allocator->PrintDetailedMap(json); |
15667 | } |
15668 | |
15669 | json.EndObject(); |
15670 | } |
15671 | |
15672 | const size_t len = sb.GetLength(); |
15673 | char* const pChars = vma_new_array(allocator, char, len + 1); |
15674 | if(len > 0) |
15675 | { |
15676 | memcpy(dest: pChars, src: sb.GetData(), n: len); |
15677 | } |
15678 | pChars[len] = '\0'; |
15679 | *ppStatsString = pChars; |
15680 | } |
15681 | |
15682 | void vmaFreeStatsString( |
15683 | VmaAllocator allocator, |
15684 | char* pStatsString) |
15685 | { |
15686 | if(pStatsString != VMA_NULL) |
15687 | { |
15688 | VMA_ASSERT(allocator); |
15689 | size_t len = strlen(s: pStatsString); |
15690 | vma_delete_array(hAllocator: allocator, ptr: pStatsString, count: len + 1); |
15691 | } |
15692 | } |
15693 | |
15694 | #endif // #if VMA_STATS_STRING_ENABLED |
15695 | |
15696 | /* |
15697 | This function is not protected by any mutex because it just reads immutable data. |
15698 | */ |
15699 | VkResult vmaFindMemoryTypeIndex( |
15700 | VmaAllocator allocator, |
15701 | uint32_t memoryTypeBits, |
15702 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
15703 | uint32_t* pMemoryTypeIndex) |
15704 | { |
15705 | VMA_ASSERT(allocator != VK_NULL_HANDLE); |
15706 | VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); |
15707 | VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); |
15708 | |
15709 | if(pAllocationCreateInfo->memoryTypeBits != 0) |
15710 | { |
15711 | memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits; |
15712 | } |
15713 | |
15714 | uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags; |
15715 | uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags; |
15716 | |
15717 | const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; |
15718 | if(mapped) |
15719 | { |
15720 | preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
15721 | } |
15722 | |
15723 | // Convert usage to requiredFlags and preferredFlags. |
15724 | switch(pAllocationCreateInfo->usage) |
15725 | { |
15726 | case VMA_MEMORY_USAGE_UNKNOWN: |
15727 | break; |
15728 | case VMA_MEMORY_USAGE_GPU_ONLY: |
15729 | if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) |
15730 | { |
15731 | preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
15732 | } |
15733 | break; |
15734 | case VMA_MEMORY_USAGE_CPU_ONLY: |
15735 | requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
15736 | break; |
15737 | case VMA_MEMORY_USAGE_CPU_TO_GPU: |
15738 | requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
15739 | if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) |
15740 | { |
15741 | preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
15742 | } |
15743 | break; |
15744 | case VMA_MEMORY_USAGE_GPU_TO_CPU: |
15745 | requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
15746 | preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; |
15747 | break; |
15748 | default: |
15749 | break; |
15750 | } |
15751 | |
15752 | *pMemoryTypeIndex = UINT32_MAX; |
15753 | uint32_t minCost = UINT32_MAX; |
15754 | for(uint32_t memTypeIndex = 0, memTypeBit = 1; |
15755 | memTypeIndex < allocator->GetMemoryTypeCount(); |
15756 | ++memTypeIndex, memTypeBit <<= 1) |
15757 | { |
15758 | // This memory type is acceptable according to memoryTypeBits bitmask. |
15759 | if((memTypeBit & memoryTypeBits) != 0) |
15760 | { |
15761 | const VkMemoryPropertyFlags currFlags = |
15762 | allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags; |
15763 | // This memory type contains requiredFlags. |
15764 | if((requiredFlags & ~currFlags) == 0) |
15765 | { |
15766 | // Calculate cost as number of bits from preferredFlags not present in this memory type. |
15767 | uint32_t currCost = VmaCountBitsSet(v: preferredFlags & ~currFlags); |
15768 | // Remember memory type with lowest cost. |
15769 | if(currCost < minCost) |
15770 | { |
15771 | *pMemoryTypeIndex = memTypeIndex; |
15772 | if(currCost == 0) |
15773 | { |
15774 | return VK_SUCCESS; |
15775 | } |
15776 | minCost = currCost; |
15777 | } |
15778 | } |
15779 | } |
15780 | } |
15781 | return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT; |
15782 | } |
15783 | |
15784 | VkResult vmaFindMemoryTypeIndexForBufferInfo( |
15785 | VmaAllocator allocator, |
15786 | const VkBufferCreateInfo* pBufferCreateInfo, |
15787 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
15788 | uint32_t* pMemoryTypeIndex) |
15789 | { |
15790 | VMA_ASSERT(allocator != VK_NULL_HANDLE); |
15791 | VMA_ASSERT(pBufferCreateInfo != VMA_NULL); |
15792 | VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); |
15793 | VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); |
15794 | |
15795 | const VkDevice hDev = allocator->m_hDevice; |
15796 | VkBuffer hBuffer = VK_NULL_HANDLE; |
15797 | VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer( |
15798 | hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer); |
15799 | if(res == VK_SUCCESS) |
15800 | { |
15801 | VkMemoryRequirements memReq = {}; |
15802 | allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements( |
15803 | hDev, hBuffer, &memReq); |
15804 | |
15805 | res = vmaFindMemoryTypeIndex( |
15806 | allocator, |
15807 | memoryTypeBits: memReq.memoryTypeBits, |
15808 | pAllocationCreateInfo, |
15809 | pMemoryTypeIndex); |
15810 | |
15811 | allocator->GetVulkanFunctions().vkDestroyBuffer( |
15812 | hDev, hBuffer, allocator->GetAllocationCallbacks()); |
15813 | } |
15814 | return res; |
15815 | } |
15816 | |
15817 | VkResult vmaFindMemoryTypeIndexForImageInfo( |
15818 | VmaAllocator allocator, |
15819 | const VkImageCreateInfo* pImageCreateInfo, |
15820 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
15821 | uint32_t* pMemoryTypeIndex) |
15822 | { |
15823 | VMA_ASSERT(allocator != VK_NULL_HANDLE); |
15824 | VMA_ASSERT(pImageCreateInfo != VMA_NULL); |
15825 | VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); |
15826 | VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); |
15827 | |
15828 | const VkDevice hDev = allocator->m_hDevice; |
15829 | VkImage hImage = VK_NULL_HANDLE; |
15830 | VkResult res = allocator->GetVulkanFunctions().vkCreateImage( |
15831 | hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage); |
15832 | if(res == VK_SUCCESS) |
15833 | { |
15834 | VkMemoryRequirements memReq = {}; |
15835 | allocator->GetVulkanFunctions().vkGetImageMemoryRequirements( |
15836 | hDev, hImage, &memReq); |
15837 | |
15838 | res = vmaFindMemoryTypeIndex( |
15839 | allocator, |
15840 | memoryTypeBits: memReq.memoryTypeBits, |
15841 | pAllocationCreateInfo, |
15842 | pMemoryTypeIndex); |
15843 | |
15844 | allocator->GetVulkanFunctions().vkDestroyImage( |
15845 | hDev, hImage, allocator->GetAllocationCallbacks()); |
15846 | } |
15847 | return res; |
15848 | } |
15849 | |
15850 | VkResult vmaCreatePool( |
15851 | VmaAllocator allocator, |
15852 | const VmaPoolCreateInfo* pCreateInfo, |
15853 | VmaPool* pPool) |
15854 | { |
15855 | VMA_ASSERT(allocator && pCreateInfo && pPool); |
15856 | |
15857 | VMA_DEBUG_LOG("vmaCreatePool" ); |
15858 | |
15859 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15860 | |
15861 | VkResult res = allocator->CreatePool(pCreateInfo, pPool); |
15862 | |
15863 | #if VMA_RECORDING_ENABLED |
15864 | if(allocator->GetRecorder() != VMA_NULL) |
15865 | { |
15866 | allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool); |
15867 | } |
15868 | #endif |
15869 | |
15870 | return res; |
15871 | } |
15872 | |
15873 | void vmaDestroyPool( |
15874 | VmaAllocator allocator, |
15875 | VmaPool pool) |
15876 | { |
15877 | VMA_ASSERT(allocator); |
15878 | |
15879 | if(pool == VK_NULL_HANDLE) |
15880 | { |
15881 | return; |
15882 | } |
15883 | |
15884 | VMA_DEBUG_LOG("vmaDestroyPool" ); |
15885 | |
15886 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15887 | |
15888 | #if VMA_RECORDING_ENABLED |
15889 | if(allocator->GetRecorder() != VMA_NULL) |
15890 | { |
15891 | allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool); |
15892 | } |
15893 | #endif |
15894 | |
15895 | allocator->DestroyPool(pool); |
15896 | } |
15897 | |
15898 | void vmaGetPoolStats( |
15899 | VmaAllocator allocator, |
15900 | VmaPool pool, |
15901 | VmaPoolStats* pPoolStats) |
15902 | { |
15903 | VMA_ASSERT(allocator && pool && pPoolStats); |
15904 | |
15905 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15906 | |
15907 | allocator->GetPoolStats(pool, pPoolStats); |
15908 | } |
15909 | |
15910 | void vmaMakePoolAllocationsLost( |
15911 | VmaAllocator allocator, |
15912 | VmaPool pool, |
15913 | size_t* pLostAllocationCount) |
15914 | { |
15915 | VMA_ASSERT(allocator && pool); |
15916 | |
15917 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15918 | |
15919 | #if VMA_RECORDING_ENABLED |
15920 | if(allocator->GetRecorder() != VMA_NULL) |
15921 | { |
15922 | allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool); |
15923 | } |
15924 | #endif |
15925 | |
15926 | allocator->MakePoolAllocationsLost(hPool: pool, pLostAllocationCount); |
15927 | } |
15928 | |
15929 | VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool) |
15930 | { |
15931 | VMA_ASSERT(allocator && pool); |
15932 | |
15933 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15934 | |
15935 | VMA_DEBUG_LOG("vmaCheckPoolCorruption" ); |
15936 | |
15937 | return allocator->CheckPoolCorruption(hPool: pool); |
15938 | } |
15939 | |
15940 | VkResult vmaAllocateMemory( |
15941 | VmaAllocator allocator, |
15942 | const VkMemoryRequirements* pVkMemoryRequirements, |
15943 | const VmaAllocationCreateInfo* pCreateInfo, |
15944 | VmaAllocation* pAllocation, |
15945 | VmaAllocationInfo* pAllocationInfo) |
15946 | { |
15947 | VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation); |
15948 | |
15949 | VMA_DEBUG_LOG("vmaAllocateMemory" ); |
15950 | |
15951 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
15952 | |
15953 | VkResult result = allocator->AllocateMemory( |
15954 | vkMemReq: *pVkMemoryRequirements, |
15955 | requiresDedicatedAllocation: false, // requiresDedicatedAllocation |
15956 | prefersDedicatedAllocation: false, // prefersDedicatedAllocation |
15957 | VK_NULL_HANDLE, // dedicatedBuffer |
15958 | VK_NULL_HANDLE, // dedicatedImage |
15959 | createInfo: *pCreateInfo, |
15960 | suballocType: VMA_SUBALLOCATION_TYPE_UNKNOWN, |
15961 | allocationCount: 1, // allocationCount |
15962 | pAllocations: pAllocation); |
15963 | |
15964 | #if VMA_RECORDING_ENABLED |
15965 | if(allocator->GetRecorder() != VMA_NULL) |
15966 | { |
15967 | allocator->GetRecorder()->RecordAllocateMemory( |
15968 | allocator->GetCurrentFrameIndex(), |
15969 | *pVkMemoryRequirements, |
15970 | *pCreateInfo, |
15971 | *pAllocation); |
15972 | } |
15973 | #endif |
15974 | |
15975 | if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) |
15976 | { |
15977 | allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo); |
15978 | } |
15979 | |
15980 | return result; |
15981 | } |
15982 | |
15983 | VkResult vmaAllocateMemoryPages( |
15984 | VmaAllocator allocator, |
15985 | const VkMemoryRequirements* pVkMemoryRequirements, |
15986 | const VmaAllocationCreateInfo* pCreateInfo, |
15987 | size_t allocationCount, |
15988 | VmaAllocation* pAllocations, |
15989 | VmaAllocationInfo* pAllocationInfo) |
15990 | { |
15991 | if(allocationCount == 0) |
15992 | { |
15993 | return VK_SUCCESS; |
15994 | } |
15995 | |
15996 | VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations); |
15997 | |
15998 | VMA_DEBUG_LOG("vmaAllocateMemoryPages" ); |
15999 | |
16000 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16001 | |
16002 | VkResult result = allocator->AllocateMemory( |
16003 | vkMemReq: *pVkMemoryRequirements, |
16004 | requiresDedicatedAllocation: false, // requiresDedicatedAllocation |
16005 | prefersDedicatedAllocation: false, // prefersDedicatedAllocation |
16006 | VK_NULL_HANDLE, // dedicatedBuffer |
16007 | VK_NULL_HANDLE, // dedicatedImage |
16008 | createInfo: *pCreateInfo, |
16009 | suballocType: VMA_SUBALLOCATION_TYPE_UNKNOWN, |
16010 | allocationCount, |
16011 | pAllocations); |
16012 | |
16013 | #if VMA_RECORDING_ENABLED |
16014 | if(allocator->GetRecorder() != VMA_NULL) |
16015 | { |
16016 | allocator->GetRecorder()->RecordAllocateMemoryPages( |
16017 | allocator->GetCurrentFrameIndex(), |
16018 | *pVkMemoryRequirements, |
16019 | *pCreateInfo, |
16020 | (uint64_t)allocationCount, |
16021 | pAllocations); |
16022 | } |
16023 | #endif |
16024 | |
16025 | if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) |
16026 | { |
16027 | for(size_t i = 0; i < allocationCount; ++i) |
16028 | { |
16029 | allocator->GetAllocationInfo(hAllocation: pAllocations[i], pAllocationInfo: pAllocationInfo + i); |
16030 | } |
16031 | } |
16032 | |
16033 | return result; |
16034 | } |
16035 | |
16036 | VkResult vmaAllocateMemoryForBuffer( |
16037 | VmaAllocator allocator, |
16038 | VkBuffer buffer, |
16039 | const VmaAllocationCreateInfo* pCreateInfo, |
16040 | VmaAllocation* pAllocation, |
16041 | VmaAllocationInfo* pAllocationInfo) |
16042 | { |
16043 | VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation); |
16044 | |
16045 | VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer" ); |
16046 | |
16047 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16048 | |
16049 | VkMemoryRequirements vkMemReq = {}; |
16050 | bool requiresDedicatedAllocation = false; |
16051 | bool prefersDedicatedAllocation = false; |
16052 | allocator->GetBufferMemoryRequirements(hBuffer: buffer, memReq&: vkMemReq, |
16053 | requiresDedicatedAllocation, |
16054 | prefersDedicatedAllocation); |
16055 | |
16056 | VkResult result = allocator->AllocateMemory( |
16057 | vkMemReq, |
16058 | requiresDedicatedAllocation, |
16059 | prefersDedicatedAllocation, |
16060 | dedicatedBuffer: buffer, // dedicatedBuffer |
16061 | VK_NULL_HANDLE, // dedicatedImage |
16062 | createInfo: *pCreateInfo, |
16063 | suballocType: VMA_SUBALLOCATION_TYPE_BUFFER, |
16064 | allocationCount: 1, // allocationCount |
16065 | pAllocations: pAllocation); |
16066 | |
16067 | #if VMA_RECORDING_ENABLED |
16068 | if(allocator->GetRecorder() != VMA_NULL) |
16069 | { |
16070 | allocator->GetRecorder()->RecordAllocateMemoryForBuffer( |
16071 | allocator->GetCurrentFrameIndex(), |
16072 | vkMemReq, |
16073 | requiresDedicatedAllocation, |
16074 | prefersDedicatedAllocation, |
16075 | *pCreateInfo, |
16076 | *pAllocation); |
16077 | } |
16078 | #endif |
16079 | |
16080 | if(pAllocationInfo && result == VK_SUCCESS) |
16081 | { |
16082 | allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo); |
16083 | } |
16084 | |
16085 | return result; |
16086 | } |
16087 | |
16088 | VkResult vmaAllocateMemoryForImage( |
16089 | VmaAllocator allocator, |
16090 | VkImage image, |
16091 | const VmaAllocationCreateInfo* pCreateInfo, |
16092 | VmaAllocation* pAllocation, |
16093 | VmaAllocationInfo* pAllocationInfo) |
16094 | { |
16095 | VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation); |
16096 | |
16097 | VMA_DEBUG_LOG("vmaAllocateMemoryForImage" ); |
16098 | |
16099 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16100 | |
16101 | VkMemoryRequirements vkMemReq = {}; |
16102 | bool requiresDedicatedAllocation = false; |
16103 | bool prefersDedicatedAllocation = false; |
16104 | allocator->GetImageMemoryRequirements(hImage: image, memReq&: vkMemReq, |
16105 | requiresDedicatedAllocation, prefersDedicatedAllocation); |
16106 | |
16107 | VkResult result = allocator->AllocateMemory( |
16108 | vkMemReq, |
16109 | requiresDedicatedAllocation, |
16110 | prefersDedicatedAllocation, |
16111 | VK_NULL_HANDLE, // dedicatedBuffer |
16112 | dedicatedImage: image, // dedicatedImage |
16113 | createInfo: *pCreateInfo, |
16114 | suballocType: VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, |
16115 | allocationCount: 1, // allocationCount |
16116 | pAllocations: pAllocation); |
16117 | |
16118 | #if VMA_RECORDING_ENABLED |
16119 | if(allocator->GetRecorder() != VMA_NULL) |
16120 | { |
16121 | allocator->GetRecorder()->RecordAllocateMemoryForImage( |
16122 | allocator->GetCurrentFrameIndex(), |
16123 | vkMemReq, |
16124 | requiresDedicatedAllocation, |
16125 | prefersDedicatedAllocation, |
16126 | *pCreateInfo, |
16127 | *pAllocation); |
16128 | } |
16129 | #endif |
16130 | |
16131 | if(pAllocationInfo && result == VK_SUCCESS) |
16132 | { |
16133 | allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo); |
16134 | } |
16135 | |
16136 | return result; |
16137 | } |
16138 | |
16139 | void vmaFreeMemory( |
16140 | VmaAllocator allocator, |
16141 | VmaAllocation allocation) |
16142 | { |
16143 | VMA_ASSERT(allocator); |
16144 | |
16145 | if(allocation == VK_NULL_HANDLE) |
16146 | { |
16147 | return; |
16148 | } |
16149 | |
16150 | VMA_DEBUG_LOG("vmaFreeMemory" ); |
16151 | |
16152 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16153 | |
16154 | #if VMA_RECORDING_ENABLED |
16155 | if(allocator->GetRecorder() != VMA_NULL) |
16156 | { |
16157 | allocator->GetRecorder()->RecordFreeMemory( |
16158 | allocator->GetCurrentFrameIndex(), |
16159 | allocation); |
16160 | } |
16161 | #endif |
16162 | |
16163 | allocator->FreeMemory( |
16164 | allocationCount: 1, // allocationCount |
16165 | pAllocations: &allocation); |
16166 | } |
16167 | |
16168 | void vmaFreeMemoryPages( |
16169 | VmaAllocator allocator, |
16170 | size_t allocationCount, |
16171 | VmaAllocation* pAllocations) |
16172 | { |
16173 | if(allocationCount == 0) |
16174 | { |
16175 | return; |
16176 | } |
16177 | |
16178 | VMA_ASSERT(allocator); |
16179 | |
16180 | VMA_DEBUG_LOG("vmaFreeMemoryPages" ); |
16181 | |
16182 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16183 | |
16184 | #if VMA_RECORDING_ENABLED |
16185 | if(allocator->GetRecorder() != VMA_NULL) |
16186 | { |
16187 | allocator->GetRecorder()->RecordFreeMemoryPages( |
16188 | allocator->GetCurrentFrameIndex(), |
16189 | (uint64_t)allocationCount, |
16190 | pAllocations); |
16191 | } |
16192 | #endif |
16193 | |
16194 | allocator->FreeMemory(allocationCount, pAllocations); |
16195 | } |
16196 | |
16197 | VkResult vmaResizeAllocation( |
16198 | VmaAllocator allocator, |
16199 | VmaAllocation allocation, |
16200 | VkDeviceSize newSize) |
16201 | { |
16202 | VMA_ASSERT(allocator && allocation); |
16203 | |
16204 | VMA_DEBUG_LOG("vmaResizeAllocation" ); |
16205 | |
16206 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16207 | |
16208 | #if VMA_RECORDING_ENABLED |
16209 | if(allocator->GetRecorder() != VMA_NULL) |
16210 | { |
16211 | allocator->GetRecorder()->RecordResizeAllocation( |
16212 | allocator->GetCurrentFrameIndex(), |
16213 | allocation, |
16214 | newSize); |
16215 | } |
16216 | #endif |
16217 | |
16218 | return allocator->ResizeAllocation(alloc: allocation, newSize); |
16219 | } |
16220 | |
16221 | void vmaGetAllocationInfo( |
16222 | VmaAllocator allocator, |
16223 | VmaAllocation allocation, |
16224 | VmaAllocationInfo* pAllocationInfo) |
16225 | { |
16226 | VMA_ASSERT(allocator && allocation && pAllocationInfo); |
16227 | |
16228 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16229 | |
16230 | #if VMA_RECORDING_ENABLED |
16231 | if(allocator->GetRecorder() != VMA_NULL) |
16232 | { |
16233 | allocator->GetRecorder()->RecordGetAllocationInfo( |
16234 | allocator->GetCurrentFrameIndex(), |
16235 | allocation); |
16236 | } |
16237 | #endif |
16238 | |
16239 | allocator->GetAllocationInfo(hAllocation: allocation, pAllocationInfo); |
16240 | } |
16241 | |
16242 | VkBool32 vmaTouchAllocation( |
16243 | VmaAllocator allocator, |
16244 | VmaAllocation allocation) |
16245 | { |
16246 | VMA_ASSERT(allocator && allocation); |
16247 | |
16248 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16249 | |
16250 | #if VMA_RECORDING_ENABLED |
16251 | if(allocator->GetRecorder() != VMA_NULL) |
16252 | { |
16253 | allocator->GetRecorder()->RecordTouchAllocation( |
16254 | allocator->GetCurrentFrameIndex(), |
16255 | allocation); |
16256 | } |
16257 | #endif |
16258 | |
16259 | return allocator->TouchAllocation(hAllocation: allocation); |
16260 | } |
16261 | |
16262 | void vmaSetAllocationUserData( |
16263 | VmaAllocator allocator, |
16264 | VmaAllocation allocation, |
16265 | void* pUserData) |
16266 | { |
16267 | VMA_ASSERT(allocator && allocation); |
16268 | |
16269 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16270 | |
16271 | allocation->SetUserData(hAllocator: allocator, pUserData); |
16272 | |
16273 | #if VMA_RECORDING_ENABLED |
16274 | if(allocator->GetRecorder() != VMA_NULL) |
16275 | { |
16276 | allocator->GetRecorder()->RecordSetAllocationUserData( |
16277 | allocator->GetCurrentFrameIndex(), |
16278 | allocation, |
16279 | pUserData); |
16280 | } |
16281 | #endif |
16282 | } |
16283 | |
16284 | void vmaCreateLostAllocation( |
16285 | VmaAllocator allocator, |
16286 | VmaAllocation* pAllocation) |
16287 | { |
16288 | VMA_ASSERT(allocator && pAllocation); |
16289 | |
16290 | VMA_DEBUG_GLOBAL_MUTEX_LOCK; |
16291 | |
16292 | allocator->CreateLostAllocation(pAllocation); |
16293 | |
16294 | #if VMA_RECORDING_ENABLED |
16295 | if(allocator->GetRecorder() != VMA_NULL) |
16296 | { |
16297 | allocator->GetRecorder()->RecordCreateLostAllocation( |
16298 | allocator->GetCurrentFrameIndex(), |
16299 | *pAllocation); |
16300 | } |
16301 | #endif |
16302 | } |
16303 | |
16304 | VkResult vmaMapMemory( |
16305 | VmaAllocator allocator, |
16306 | VmaAllocation allocation, |
16307 | void** ppData) |
16308 | { |
16309 | VMA_ASSERT(allocator && allocation && ppData); |
16310 | |
16311 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16312 | |
16313 | VkResult res = allocator->Map(hAllocation: allocation, ppData); |
16314 | |
16315 | #if VMA_RECORDING_ENABLED |
16316 | if(allocator->GetRecorder() != VMA_NULL) |
16317 | { |
16318 | allocator->GetRecorder()->RecordMapMemory( |
16319 | allocator->GetCurrentFrameIndex(), |
16320 | allocation); |
16321 | } |
16322 | #endif |
16323 | |
16324 | return res; |
16325 | } |
16326 | |
16327 | void vmaUnmapMemory( |
16328 | VmaAllocator allocator, |
16329 | VmaAllocation allocation) |
16330 | { |
16331 | VMA_ASSERT(allocator && allocation); |
16332 | |
16333 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16334 | |
16335 | #if VMA_RECORDING_ENABLED |
16336 | if(allocator->GetRecorder() != VMA_NULL) |
16337 | { |
16338 | allocator->GetRecorder()->RecordUnmapMemory( |
16339 | allocator->GetCurrentFrameIndex(), |
16340 | allocation); |
16341 | } |
16342 | #endif |
16343 | |
16344 | allocator->Unmap(hAllocation: allocation); |
16345 | } |
16346 | |
16347 | void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) |
16348 | { |
16349 | VMA_ASSERT(allocator && allocation); |
16350 | |
16351 | VMA_DEBUG_LOG("vmaFlushAllocation" ); |
16352 | |
16353 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16354 | |
16355 | allocator->FlushOrInvalidateAllocation(hAllocation: allocation, offset, size, op: VMA_CACHE_FLUSH); |
16356 | |
16357 | #if VMA_RECORDING_ENABLED |
16358 | if(allocator->GetRecorder() != VMA_NULL) |
16359 | { |
16360 | allocator->GetRecorder()->RecordFlushAllocation( |
16361 | allocator->GetCurrentFrameIndex(), |
16362 | allocation, offset, size); |
16363 | } |
16364 | #endif |
16365 | } |
16366 | |
16367 | void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) |
16368 | { |
16369 | VMA_ASSERT(allocator && allocation); |
16370 | |
16371 | VMA_DEBUG_LOG("vmaInvalidateAllocation" ); |
16372 | |
16373 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16374 | |
16375 | allocator->FlushOrInvalidateAllocation(hAllocation: allocation, offset, size, op: VMA_CACHE_INVALIDATE); |
16376 | |
16377 | #if VMA_RECORDING_ENABLED |
16378 | if(allocator->GetRecorder() != VMA_NULL) |
16379 | { |
16380 | allocator->GetRecorder()->RecordInvalidateAllocation( |
16381 | allocator->GetCurrentFrameIndex(), |
16382 | allocation, offset, size); |
16383 | } |
16384 | #endif |
16385 | } |
16386 | |
16387 | VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits) |
16388 | { |
16389 | VMA_ASSERT(allocator); |
16390 | |
16391 | VMA_DEBUG_LOG("vmaCheckCorruption" ); |
16392 | |
16393 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16394 | |
16395 | return allocator->CheckCorruption(memoryTypeBits); |
16396 | } |
16397 | |
16398 | VkResult vmaDefragment( |
16399 | VmaAllocator allocator, |
16400 | VmaAllocation* pAllocations, |
16401 | size_t allocationCount, |
16402 | VkBool32* pAllocationsChanged, |
16403 | const VmaDefragmentationInfo *pDefragmentationInfo, |
16404 | VmaDefragmentationStats* pDefragmentationStats) |
16405 | { |
16406 | // Deprecated interface, reimplemented using new one. |
16407 | |
16408 | VmaDefragmentationInfo2 info2 = {}; |
16409 | info2.allocationCount = (uint32_t)allocationCount; |
16410 | info2.pAllocations = pAllocations; |
16411 | info2.pAllocationsChanged = pAllocationsChanged; |
16412 | if(pDefragmentationInfo != VMA_NULL) |
16413 | { |
16414 | info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove; |
16415 | info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove; |
16416 | } |
16417 | else |
16418 | { |
16419 | info2.maxCpuAllocationsToMove = UINT32_MAX; |
16420 | info2.maxCpuBytesToMove = VK_WHOLE_SIZE; |
16421 | } |
16422 | // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero. |
16423 | |
16424 | VmaDefragmentationContext ctx; |
16425 | VkResult res = vmaDefragmentationBegin(allocator, pInfo: &info2, pStats: pDefragmentationStats, pContext: &ctx); |
16426 | if(res == VK_NOT_READY) |
16427 | { |
16428 | res = vmaDefragmentationEnd( allocator, context: ctx); |
16429 | } |
16430 | return res; |
16431 | } |
16432 | |
16433 | VkResult vmaDefragmentationBegin( |
16434 | VmaAllocator allocator, |
16435 | const VmaDefragmentationInfo2* pInfo, |
16436 | VmaDefragmentationStats* pStats, |
16437 | VmaDefragmentationContext *pContext) |
16438 | { |
16439 | VMA_ASSERT(allocator && pInfo && pContext); |
16440 | |
16441 | // Degenerate case: Nothing to defragment. |
16442 | if(pInfo->allocationCount == 0 && pInfo->poolCount == 0) |
16443 | { |
16444 | return VK_SUCCESS; |
16445 | } |
16446 | |
16447 | VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL); |
16448 | VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL); |
16449 | VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations)); |
16450 | VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools)); |
16451 | |
16452 | VMA_DEBUG_LOG("vmaDefragmentationBegin" ); |
16453 | |
16454 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16455 | |
16456 | VkResult res = allocator->DefragmentationBegin(info: *pInfo, pStats, pContext); |
16457 | |
16458 | #if VMA_RECORDING_ENABLED |
16459 | if(allocator->GetRecorder() != VMA_NULL) |
16460 | { |
16461 | allocator->GetRecorder()->RecordDefragmentationBegin( |
16462 | allocator->GetCurrentFrameIndex(), *pInfo, *pContext); |
16463 | } |
16464 | #endif |
16465 | |
16466 | return res; |
16467 | } |
16468 | |
16469 | VkResult vmaDefragmentationEnd( |
16470 | VmaAllocator allocator, |
16471 | VmaDefragmentationContext context) |
16472 | { |
16473 | VMA_ASSERT(allocator); |
16474 | |
16475 | VMA_DEBUG_LOG("vmaDefragmentationEnd" ); |
16476 | |
16477 | if(context != VK_NULL_HANDLE) |
16478 | { |
16479 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16480 | |
16481 | #if VMA_RECORDING_ENABLED |
16482 | if(allocator->GetRecorder() != VMA_NULL) |
16483 | { |
16484 | allocator->GetRecorder()->RecordDefragmentationEnd( |
16485 | allocator->GetCurrentFrameIndex(), context); |
16486 | } |
16487 | #endif |
16488 | |
16489 | return allocator->DefragmentationEnd(context); |
16490 | } |
16491 | else |
16492 | { |
16493 | return VK_SUCCESS; |
16494 | } |
16495 | } |
16496 | |
16497 | VkResult vmaBindBufferMemory( |
16498 | VmaAllocator allocator, |
16499 | VmaAllocation allocation, |
16500 | VkBuffer buffer) |
16501 | { |
16502 | VMA_ASSERT(allocator && allocation && buffer); |
16503 | |
16504 | VMA_DEBUG_LOG("vmaBindBufferMemory" ); |
16505 | |
16506 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16507 | |
16508 | return allocator->BindBufferMemory(hAllocation: allocation, hBuffer: buffer); |
16509 | } |
16510 | |
16511 | VkResult vmaBindImageMemory( |
16512 | VmaAllocator allocator, |
16513 | VmaAllocation allocation, |
16514 | VkImage image) |
16515 | { |
16516 | VMA_ASSERT(allocator && allocation && image); |
16517 | |
16518 | VMA_DEBUG_LOG("vmaBindImageMemory" ); |
16519 | |
16520 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16521 | |
16522 | return allocator->BindImageMemory(hAllocation: allocation, hImage: image); |
16523 | } |
16524 | |
16525 | VkResult vmaCreateBuffer( |
16526 | VmaAllocator allocator, |
16527 | const VkBufferCreateInfo* pBufferCreateInfo, |
16528 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
16529 | VkBuffer* pBuffer, |
16530 | VmaAllocation* pAllocation, |
16531 | VmaAllocationInfo* pAllocationInfo) |
16532 | { |
16533 | VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation); |
16534 | |
16535 | if(pBufferCreateInfo->size == 0) |
16536 | { |
16537 | return VK_ERROR_VALIDATION_FAILED_EXT; |
16538 | } |
16539 | |
16540 | VMA_DEBUG_LOG("vmaCreateBuffer" ); |
16541 | |
16542 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16543 | |
16544 | *pBuffer = VK_NULL_HANDLE; |
16545 | *pAllocation = VK_NULL_HANDLE; |
16546 | |
16547 | // 1. Create VkBuffer. |
16548 | VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( |
16549 | allocator->m_hDevice, |
16550 | pBufferCreateInfo, |
16551 | allocator->GetAllocationCallbacks(), |
16552 | pBuffer); |
16553 | if(res >= 0) |
16554 | { |
16555 | // 2. vkGetBufferMemoryRequirements. |
16556 | VkMemoryRequirements vkMemReq = {}; |
16557 | bool requiresDedicatedAllocation = false; |
16558 | bool prefersDedicatedAllocation = false; |
16559 | allocator->GetBufferMemoryRequirements(hBuffer: *pBuffer, memReq&: vkMemReq, |
16560 | requiresDedicatedAllocation, prefersDedicatedAllocation); |
16561 | |
16562 | // Make sure alignment requirements for specific buffer usages reported |
16563 | // in Physical Device Properties are included in alignment reported by memory requirements. |
16564 | if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0) |
16565 | { |
16566 | VMA_ASSERT(vkMemReq.alignment % |
16567 | allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0); |
16568 | } |
16569 | if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0) |
16570 | { |
16571 | VMA_ASSERT(vkMemReq.alignment % |
16572 | allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0); |
16573 | } |
16574 | if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0) |
16575 | { |
16576 | VMA_ASSERT(vkMemReq.alignment % |
16577 | allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0); |
16578 | } |
16579 | |
16580 | // 3. Allocate memory using allocator. |
16581 | res = allocator->AllocateMemory( |
16582 | vkMemReq, |
16583 | requiresDedicatedAllocation, |
16584 | prefersDedicatedAllocation, |
16585 | dedicatedBuffer: *pBuffer, // dedicatedBuffer |
16586 | VK_NULL_HANDLE, // dedicatedImage |
16587 | createInfo: *pAllocationCreateInfo, |
16588 | suballocType: VMA_SUBALLOCATION_TYPE_BUFFER, |
16589 | allocationCount: 1, // allocationCount |
16590 | pAllocations: pAllocation); |
16591 | |
16592 | #if VMA_RECORDING_ENABLED |
16593 | if(allocator->GetRecorder() != VMA_NULL) |
16594 | { |
16595 | allocator->GetRecorder()->RecordCreateBuffer( |
16596 | allocator->GetCurrentFrameIndex(), |
16597 | *pBufferCreateInfo, |
16598 | *pAllocationCreateInfo, |
16599 | *pAllocation); |
16600 | } |
16601 | #endif |
16602 | |
16603 | if(res >= 0) |
16604 | { |
16605 | // 3. Bind buffer with memory. |
16606 | res = allocator->BindBufferMemory(hAllocation: *pAllocation, hBuffer: *pBuffer); |
16607 | if(res >= 0) |
16608 | { |
16609 | // All steps succeeded. |
16610 | #if VMA_STATS_STRING_ENABLED |
16611 | (*pAllocation)->InitBufferImageUsage(bufferImageUsage: pBufferCreateInfo->usage); |
16612 | #endif |
16613 | if(pAllocationInfo != VMA_NULL) |
16614 | { |
16615 | allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo); |
16616 | } |
16617 | |
16618 | return VK_SUCCESS; |
16619 | } |
16620 | allocator->FreeMemory( |
16621 | allocationCount: 1, // allocationCount |
16622 | pAllocations: pAllocation); |
16623 | *pAllocation = VK_NULL_HANDLE; |
16624 | (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); |
16625 | *pBuffer = VK_NULL_HANDLE; |
16626 | return res; |
16627 | } |
16628 | (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); |
16629 | *pBuffer = VK_NULL_HANDLE; |
16630 | return res; |
16631 | } |
16632 | return res; |
16633 | } |
16634 | |
16635 | void vmaDestroyBuffer( |
16636 | VmaAllocator allocator, |
16637 | VkBuffer buffer, |
16638 | VmaAllocation allocation) |
16639 | { |
16640 | VMA_ASSERT(allocator); |
16641 | |
16642 | if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) |
16643 | { |
16644 | return; |
16645 | } |
16646 | |
16647 | VMA_DEBUG_LOG("vmaDestroyBuffer" ); |
16648 | |
16649 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16650 | |
16651 | #if VMA_RECORDING_ENABLED |
16652 | if(allocator->GetRecorder() != VMA_NULL) |
16653 | { |
16654 | allocator->GetRecorder()->RecordDestroyBuffer( |
16655 | allocator->GetCurrentFrameIndex(), |
16656 | allocation); |
16657 | } |
16658 | #endif |
16659 | |
16660 | if(buffer != VK_NULL_HANDLE) |
16661 | { |
16662 | (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks()); |
16663 | } |
16664 | |
16665 | if(allocation != VK_NULL_HANDLE) |
16666 | { |
16667 | allocator->FreeMemory( |
16668 | allocationCount: 1, // allocationCount |
16669 | pAllocations: &allocation); |
16670 | } |
16671 | } |
16672 | |
16673 | VkResult vmaCreateImage( |
16674 | VmaAllocator allocator, |
16675 | const VkImageCreateInfo* pImageCreateInfo, |
16676 | const VmaAllocationCreateInfo* pAllocationCreateInfo, |
16677 | VkImage* pImage, |
16678 | VmaAllocation* pAllocation, |
16679 | VmaAllocationInfo* pAllocationInfo) |
16680 | { |
16681 | VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation); |
16682 | |
16683 | if(pImageCreateInfo->extent.width == 0 || |
16684 | pImageCreateInfo->extent.height == 0 || |
16685 | pImageCreateInfo->extent.depth == 0 || |
16686 | pImageCreateInfo->mipLevels == 0 || |
16687 | pImageCreateInfo->arrayLayers == 0) |
16688 | { |
16689 | return VK_ERROR_VALIDATION_FAILED_EXT; |
16690 | } |
16691 | |
16692 | VMA_DEBUG_LOG("vmaCreateImage" ); |
16693 | |
16694 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16695 | |
16696 | *pImage = VK_NULL_HANDLE; |
16697 | *pAllocation = VK_NULL_HANDLE; |
16698 | |
16699 | // 1. Create VkImage. |
16700 | VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( |
16701 | allocator->m_hDevice, |
16702 | pImageCreateInfo, |
16703 | allocator->GetAllocationCallbacks(), |
16704 | pImage); |
16705 | if(res >= 0) |
16706 | { |
16707 | VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? |
16708 | VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : |
16709 | VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; |
16710 | |
16711 | // 2. Allocate memory using allocator. |
16712 | VkMemoryRequirements vkMemReq = {}; |
16713 | bool requiresDedicatedAllocation = false; |
16714 | bool prefersDedicatedAllocation = false; |
16715 | allocator->GetImageMemoryRequirements(hImage: *pImage, memReq&: vkMemReq, |
16716 | requiresDedicatedAllocation, prefersDedicatedAllocation); |
16717 | |
16718 | res = allocator->AllocateMemory( |
16719 | vkMemReq, |
16720 | requiresDedicatedAllocation, |
16721 | prefersDedicatedAllocation, |
16722 | VK_NULL_HANDLE, // dedicatedBuffer |
16723 | dedicatedImage: *pImage, // dedicatedImage |
16724 | createInfo: *pAllocationCreateInfo, |
16725 | suballocType, |
16726 | allocationCount: 1, // allocationCount |
16727 | pAllocations: pAllocation); |
16728 | |
16729 | #if VMA_RECORDING_ENABLED |
16730 | if(allocator->GetRecorder() != VMA_NULL) |
16731 | { |
16732 | allocator->GetRecorder()->RecordCreateImage( |
16733 | allocator->GetCurrentFrameIndex(), |
16734 | *pImageCreateInfo, |
16735 | *pAllocationCreateInfo, |
16736 | *pAllocation); |
16737 | } |
16738 | #endif |
16739 | |
16740 | if(res >= 0) |
16741 | { |
16742 | // 3. Bind image with memory. |
16743 | res = allocator->BindImageMemory(hAllocation: *pAllocation, hImage: *pImage); |
16744 | if(res >= 0) |
16745 | { |
16746 | // All steps succeeded. |
16747 | #if VMA_STATS_STRING_ENABLED |
16748 | (*pAllocation)->InitBufferImageUsage(bufferImageUsage: pImageCreateInfo->usage); |
16749 | #endif |
16750 | if(pAllocationInfo != VMA_NULL) |
16751 | { |
16752 | allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo); |
16753 | } |
16754 | |
16755 | return VK_SUCCESS; |
16756 | } |
16757 | allocator->FreeMemory( |
16758 | allocationCount: 1, // allocationCount |
16759 | pAllocations: pAllocation); |
16760 | *pAllocation = VK_NULL_HANDLE; |
16761 | (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); |
16762 | *pImage = VK_NULL_HANDLE; |
16763 | return res; |
16764 | } |
16765 | (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); |
16766 | *pImage = VK_NULL_HANDLE; |
16767 | return res; |
16768 | } |
16769 | return res; |
16770 | } |
16771 | |
16772 | void vmaDestroyImage( |
16773 | VmaAllocator allocator, |
16774 | VkImage image, |
16775 | VmaAllocation allocation) |
16776 | { |
16777 | VMA_ASSERT(allocator); |
16778 | |
16779 | if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) |
16780 | { |
16781 | return; |
16782 | } |
16783 | |
16784 | VMA_DEBUG_LOG("vmaDestroyImage" ); |
16785 | |
16786 | VMA_DEBUG_GLOBAL_MUTEX_LOCK |
16787 | |
16788 | #if VMA_RECORDING_ENABLED |
16789 | if(allocator->GetRecorder() != VMA_NULL) |
16790 | { |
16791 | allocator->GetRecorder()->RecordDestroyImage( |
16792 | allocator->GetCurrentFrameIndex(), |
16793 | allocation); |
16794 | } |
16795 | #endif |
16796 | |
16797 | if(image != VK_NULL_HANDLE) |
16798 | { |
16799 | (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks()); |
16800 | } |
16801 | if(allocation != VK_NULL_HANDLE) |
16802 | { |
16803 | allocator->FreeMemory( |
16804 | allocationCount: 1, // allocationCount |
16805 | pAllocations: &allocation); |
16806 | } |
16807 | } |
16808 | |
16809 | #endif // #ifdef VMA_IMPLEMENTATION |
16810 | |