blob: e5b373d5d8d382e8eca69614b51c5953105badb2 [file] [log] [blame]
Adam Sawickiae5c4662019-01-02 10:23:35 +01001//
2// Copyright (c) 2017-2019 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
Adam Sawickif1a793c2018-03-13 15:42:22 +010023#include "Tests.h"
24#include "VmaUsage.h"
25#include "Common.h"
Adam Sawickib8333fb2018-03-13 16:15:53 +010026#include <atomic>
27#include <thread>
28#include <mutex>
Adam Sawicki94ce3d72019-04-17 14:59:25 +020029#include <functional>
Adam Sawickif1a793c2018-03-13 15:42:22 +010030
31#ifdef _WIN32
32
Adam Sawicki33d2ce72018-08-27 13:59:13 +020033static const char* CODE_DESCRIPTION = "Foo";
34
Adam Sawickif2975342018-10-16 13:49:02 +020035extern VkCommandBuffer g_hTemporaryCommandBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +020036extern const VkAllocationCallbacks* g_Allocs;
Adam Sawickif2975342018-10-16 13:49:02 +020037void BeginSingleTimeCommands();
38void EndSingleTimeCommands();
39
Adam Sawickibdb89a92018-12-13 11:56:30 +010040#ifndef VMA_DEBUG_MARGIN
41 #define VMA_DEBUG_MARGIN 0
42#endif
43
Adam Sawicki0a607132018-08-24 11:18:41 +020044enum CONFIG_TYPE {
45 CONFIG_TYPE_MINIMUM,
46 CONFIG_TYPE_SMALL,
47 CONFIG_TYPE_AVERAGE,
48 CONFIG_TYPE_LARGE,
49 CONFIG_TYPE_MAXIMUM,
50 CONFIG_TYPE_COUNT
51};
52
Adam Sawickif2975342018-10-16 13:49:02 +020053static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
54//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
Adam Sawicki0a607132018-08-24 11:18:41 +020055
Adam Sawickib8333fb2018-03-13 16:15:53 +010056enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
57
Adam Sawicki0667e332018-08-24 17:26:44 +020058static const char* FREE_ORDER_NAMES[] = {
59 "FORWARD",
60 "BACKWARD",
61 "RANDOM",
Adam Sawicki0a607132018-08-24 11:18:41 +020062};
63
Adam Sawicki80927152018-09-07 17:27:23 +020064// Copy of internal VmaAlgorithmToStr.
65static const char* AlgorithmToStr(uint32_t algorithm)
66{
67 switch(algorithm)
68 {
69 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
70 return "Linear";
71 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
72 return "Buddy";
73 case 0:
74 return "Default";
75 default:
76 assert(0);
77 return "";
78 }
79}
80
Adam Sawickib8333fb2018-03-13 16:15:53 +010081struct AllocationSize
82{
83 uint32_t Probability;
84 VkDeviceSize BufferSizeMin, BufferSizeMax;
85 uint32_t ImageSizeMin, ImageSizeMax;
86};
87
88struct Config
89{
90 uint32_t RandSeed;
91 VkDeviceSize BeginBytesToAllocate;
92 uint32_t AdditionalOperationCount;
93 VkDeviceSize MaxBytesToAllocate;
94 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
95 std::vector<AllocationSize> AllocationSizes;
96 uint32_t ThreadCount;
97 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
98 FREE_ORDER FreeOrder;
Adam Sawicki0667e332018-08-24 17:26:44 +020099 VmaAllocationCreateFlags AllocationStrategy; // For VMA_ALLOCATION_CREATE_STRATEGY_*
Adam Sawickib8333fb2018-03-13 16:15:53 +0100100};
101
102struct Result
103{
104 duration TotalTime;
105 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
106 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
107 VkDeviceSize TotalMemoryAllocated;
108 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
109};
110
111void TestDefragmentationSimple();
112void TestDefragmentationFull();
113
114struct PoolTestConfig
115{
116 uint32_t RandSeed;
117 uint32_t ThreadCount;
118 VkDeviceSize PoolSize;
119 uint32_t FrameCount;
120 uint32_t TotalItemCount;
121 // Range for number of items used in each frame.
122 uint32_t UsedItemCountMin, UsedItemCountMax;
123 // Percent of items to make unused, and possibly make some others used in each frame.
124 uint32_t ItemsToMakeUnusedPercent;
125 std::vector<AllocationSize> AllocationSizes;
126
127 VkDeviceSize CalcAvgResourceSize() const
128 {
129 uint32_t probabilitySum = 0;
130 VkDeviceSize sizeSum = 0;
131 for(size_t i = 0; i < AllocationSizes.size(); ++i)
132 {
133 const AllocationSize& allocSize = AllocationSizes[i];
134 if(allocSize.BufferSizeMax > 0)
135 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
136 else
137 {
138 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
139 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
140 }
141 probabilitySum += allocSize.Probability;
142 }
143 return sizeSum / probabilitySum;
144 }
145
146 bool UsesBuffers() const
147 {
148 for(size_t i = 0; i < AllocationSizes.size(); ++i)
149 if(AllocationSizes[i].BufferSizeMax > 0)
150 return true;
151 return false;
152 }
153
154 bool UsesImages() const
155 {
156 for(size_t i = 0; i < AllocationSizes.size(); ++i)
157 if(AllocationSizes[i].ImageSizeMax > 0)
158 return true;
159 return false;
160 }
161};
162
163struct PoolTestResult
164{
165 duration TotalTime;
166 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
167 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
168 size_t LostAllocationCount, LostAllocationTotalSize;
169 size_t FailedAllocationCount, FailedAllocationTotalSize;
170};
171
172static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
173
Adam Sawicki51fa9662018-10-03 13:44:29 +0200174uint32_t g_FrameIndex = 0;
Adam Sawicki8cfe05f2018-08-22 16:48:17 +0200175
Adam Sawickib8333fb2018-03-13 16:15:53 +0100176struct BufferInfo
177{
178 VkBuffer Buffer = VK_NULL_HANDLE;
179 VmaAllocation Allocation = VK_NULL_HANDLE;
180};
181
Adam Sawicki40ffe982019-10-11 15:56:02 +0200182static uint32_t MemoryTypeToHeap(uint32_t memoryTypeIndex)
183{
184 const VkPhysicalDeviceMemoryProperties* props;
185 vmaGetMemoryProperties(g_hAllocator, &props);
186 return props->memoryTypes[memoryTypeIndex].heapIndex;
187}
188
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +0200189static uint32_t GetAllocationStrategyCount()
190{
191 uint32_t strategyCount = 0;
192 switch(ConfigType)
193 {
194 case CONFIG_TYPE_MINIMUM: strategyCount = 1; break;
195 case CONFIG_TYPE_SMALL: strategyCount = 1; break;
196 case CONFIG_TYPE_AVERAGE: strategyCount = 2; break;
197 case CONFIG_TYPE_LARGE: strategyCount = 2; break;
198 case CONFIG_TYPE_MAXIMUM: strategyCount = 3; break;
199 default: assert(0);
200 }
201 return strategyCount;
202}
203
204static const char* GetAllocationStrategyName(VmaAllocationCreateFlags allocStrategy)
205{
206 switch(allocStrategy)
207 {
208 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT: return "BEST_FIT"; break;
209 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT: return "WORST_FIT"; break;
210 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT: return "FIRST_FIT"; break;
211 case 0: return "Default"; break;
212 default: assert(0); return "";
213 }
214}
215
Adam Sawickib8333fb2018-03-13 16:15:53 +0100216static void InitResult(Result& outResult)
217{
218 outResult.TotalTime = duration::zero();
219 outResult.AllocationTimeMin = duration::max();
220 outResult.AllocationTimeAvg = duration::zero();
221 outResult.AllocationTimeMax = duration::min();
222 outResult.DeallocationTimeMin = duration::max();
223 outResult.DeallocationTimeAvg = duration::zero();
224 outResult.DeallocationTimeMax = duration::min();
225 outResult.TotalMemoryAllocated = 0;
226 outResult.FreeRangeSizeAvg = 0;
227 outResult.FreeRangeSizeMax = 0;
228}
229
230class TimeRegisterObj
231{
232public:
233 TimeRegisterObj(duration& min, duration& sum, duration& max) :
234 m_Min(min),
235 m_Sum(sum),
236 m_Max(max),
237 m_TimeBeg(std::chrono::high_resolution_clock::now())
238 {
239 }
240
241 ~TimeRegisterObj()
242 {
243 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
244 m_Sum += d;
245 if(d < m_Min) m_Min = d;
246 if(d > m_Max) m_Max = d;
247 }
248
249private:
250 duration& m_Min;
251 duration& m_Sum;
252 duration& m_Max;
253 time_point m_TimeBeg;
254};
255
256struct PoolTestThreadResult
257{
258 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
259 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
260 size_t AllocationCount, DeallocationCount;
261 size_t LostAllocationCount, LostAllocationTotalSize;
262 size_t FailedAllocationCount, FailedAllocationTotalSize;
263};
264
265class AllocationTimeRegisterObj : public TimeRegisterObj
266{
267public:
268 AllocationTimeRegisterObj(Result& result) :
269 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
270 {
271 }
272};
273
274class DeallocationTimeRegisterObj : public TimeRegisterObj
275{
276public:
277 DeallocationTimeRegisterObj(Result& result) :
278 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
279 {
280 }
281};
282
283class PoolAllocationTimeRegisterObj : public TimeRegisterObj
284{
285public:
286 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
287 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
288 {
289 }
290};
291
292class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
293{
294public:
295 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
296 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
297 {
298 }
299};
300
Adam Sawicki33d2ce72018-08-27 13:59:13 +0200301static void CurrentTimeToStr(std::string& out)
302{
303 time_t rawTime; time(&rawTime);
304 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
305 char timeStr[128];
306 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
307 out = timeStr;
308}
309
Adam Sawickib8333fb2018-03-13 16:15:53 +0100310VkResult MainTest(Result& outResult, const Config& config)
311{
312 assert(config.ThreadCount > 0);
313
314 InitResult(outResult);
315
316 RandomNumberGenerator mainRand{config.RandSeed};
317
318 time_point timeBeg = std::chrono::high_resolution_clock::now();
319
320 std::atomic<size_t> allocationCount = 0;
321 VkResult res = VK_SUCCESS;
322
323 uint32_t memUsageProbabilitySum =
324 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
325 config.MemUsageProbability[2] + config.MemUsageProbability[3];
326 assert(memUsageProbabilitySum > 0);
327
328 uint32_t allocationSizeProbabilitySum = std::accumulate(
329 config.AllocationSizes.begin(),
330 config.AllocationSizes.end(),
331 0u,
332 [](uint32_t sum, const AllocationSize& allocSize) {
333 return sum + allocSize.Probability;
334 });
335
336 struct Allocation
337 {
338 VkBuffer Buffer;
339 VkImage Image;
340 VmaAllocation Alloc;
341 };
342
343 std::vector<Allocation> commonAllocations;
344 std::mutex commonAllocationsMutex;
345
346 auto Allocate = [&](
347 VkDeviceSize bufferSize,
348 const VkExtent2D imageExtent,
349 RandomNumberGenerator& localRand,
350 VkDeviceSize& totalAllocatedBytes,
351 std::vector<Allocation>& allocations) -> VkResult
352 {
353 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
354
355 uint32_t memUsageIndex = 0;
356 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
357 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
358 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
359
360 VmaAllocationCreateInfo memReq = {};
361 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
Adam Sawicki0667e332018-08-24 17:26:44 +0200362 memReq.flags |= config.AllocationStrategy;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100363
364 Allocation allocation = {};
365 VmaAllocationInfo allocationInfo;
366
367 // Buffer
368 if(bufferSize > 0)
369 {
370 assert(imageExtent.width == 0);
371 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
372 bufferInfo.size = bufferSize;
373 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
374
375 {
376 AllocationTimeRegisterObj timeRegisterObj{outResult};
377 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
378 }
379 }
380 // Image
381 else
382 {
383 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
384 imageInfo.imageType = VK_IMAGE_TYPE_2D;
385 imageInfo.extent.width = imageExtent.width;
386 imageInfo.extent.height = imageExtent.height;
387 imageInfo.extent.depth = 1;
388 imageInfo.mipLevels = 1;
389 imageInfo.arrayLayers = 1;
390 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
391 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
392 VK_IMAGE_TILING_OPTIMAL :
393 VK_IMAGE_TILING_LINEAR;
394 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
395 switch(memReq.usage)
396 {
397 case VMA_MEMORY_USAGE_GPU_ONLY:
398 switch(localRand.Generate() % 3)
399 {
400 case 0:
401 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
402 break;
403 case 1:
404 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
405 break;
406 case 2:
407 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
408 break;
409 }
410 break;
411 case VMA_MEMORY_USAGE_CPU_ONLY:
412 case VMA_MEMORY_USAGE_CPU_TO_GPU:
413 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
414 break;
415 case VMA_MEMORY_USAGE_GPU_TO_CPU:
416 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
417 break;
418 }
419 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
420 imageInfo.flags = 0;
421
422 {
423 AllocationTimeRegisterObj timeRegisterObj{outResult};
424 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
425 }
426 }
427
428 if(res == VK_SUCCESS)
429 {
430 ++allocationCount;
431 totalAllocatedBytes += allocationInfo.size;
432 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
433 if(useCommonAllocations)
434 {
435 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
436 commonAllocations.push_back(allocation);
437 }
438 else
439 allocations.push_back(allocation);
440 }
441 else
442 {
Adam Sawickib8d34d52018-10-03 17:41:20 +0200443 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100444 }
445 return res;
446 };
447
448 auto GetNextAllocationSize = [&](
449 VkDeviceSize& outBufSize,
450 VkExtent2D& outImageSize,
451 RandomNumberGenerator& localRand)
452 {
453 outBufSize = 0;
454 outImageSize = {0, 0};
455
456 uint32_t allocSizeIndex = 0;
457 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
458 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
459 r -= config.AllocationSizes[allocSizeIndex++].Probability;
460
461 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
462 if(allocSize.BufferSizeMax > 0)
463 {
464 assert(allocSize.ImageSizeMax == 0);
465 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
466 outBufSize = allocSize.BufferSizeMin;
467 else
468 {
469 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
470 outBufSize = outBufSize / 16 * 16;
471 }
472 }
473 else
474 {
475 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
476 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
477 else
478 {
479 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
480 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
481 }
482 }
483 };
484
485 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
486 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
487
488 auto ThreadProc = [&](uint32_t randSeed) -> void
489 {
490 RandomNumberGenerator threadRand(randSeed);
491 VkDeviceSize threadTotalAllocatedBytes = 0;
492 std::vector<Allocation> threadAllocations;
493 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
494 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
495 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
496
497 // BEGIN ALLOCATIONS
498 for(;;)
499 {
500 VkDeviceSize bufferSize = 0;
501 VkExtent2D imageExtent = {};
502 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
503 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
504 threadBeginBytesToAllocate)
505 {
506 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
507 break;
508 }
509 else
510 break;
511 }
512
513 // ADDITIONAL ALLOCATIONS AND FREES
514 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
515 {
516 VkDeviceSize bufferSize = 0;
517 VkExtent2D imageExtent = {};
518 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
519
520 // true = allocate, false = free
521 bool allocate = threadRand.Generate() % 2 != 0;
522
523 if(allocate)
524 {
525 if(threadTotalAllocatedBytes +
526 bufferSize +
527 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
528 threadMaxBytesToAllocate)
529 {
530 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
531 break;
532 }
533 }
534 else
535 {
536 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
537 if(useCommonAllocations)
538 {
539 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
540 if(!commonAllocations.empty())
541 {
542 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
543 VmaAllocationInfo allocationInfo;
544 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
545 if(threadTotalAllocatedBytes >= allocationInfo.size)
546 {
547 DeallocationTimeRegisterObj timeRegisterObj{outResult};
548 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
549 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
550 else
551 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
552 threadTotalAllocatedBytes -= allocationInfo.size;
553 commonAllocations.erase(commonAllocations.begin() + indexToFree);
554 }
555 }
556 }
557 else
558 {
559 if(!threadAllocations.empty())
560 {
561 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
562 VmaAllocationInfo allocationInfo;
563 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
564 if(threadTotalAllocatedBytes >= allocationInfo.size)
565 {
566 DeallocationTimeRegisterObj timeRegisterObj{outResult};
567 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
568 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
569 else
570 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
571 threadTotalAllocatedBytes -= allocationInfo.size;
572 threadAllocations.erase(threadAllocations.begin() + indexToFree);
573 }
574 }
575 }
576 }
577 }
578
579 ++numThreadsReachedMaxAllocations;
580
581 WaitForSingleObject(threadsFinishEvent, INFINITE);
582
583 // DEALLOCATION
584 while(!threadAllocations.empty())
585 {
586 size_t indexToFree = 0;
587 switch(config.FreeOrder)
588 {
589 case FREE_ORDER::FORWARD:
590 indexToFree = 0;
591 break;
592 case FREE_ORDER::BACKWARD:
593 indexToFree = threadAllocations.size() - 1;
594 break;
595 case FREE_ORDER::RANDOM:
596 indexToFree = mainRand.Generate() % threadAllocations.size();
597 break;
598 }
599
600 {
601 DeallocationTimeRegisterObj timeRegisterObj{outResult};
602 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
603 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
604 else
605 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
606 }
607 threadAllocations.erase(threadAllocations.begin() + indexToFree);
608 }
609 };
610
611 uint32_t threadRandSeed = mainRand.Generate();
612 std::vector<std::thread> bkgThreads;
613 for(size_t i = 0; i < config.ThreadCount; ++i)
614 {
615 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
616 }
617
618 // Wait for threads reached max allocations
619 while(numThreadsReachedMaxAllocations < config.ThreadCount)
620 Sleep(0);
621
622 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
623 VmaStats vmaStats = {};
624 vmaCalculateStats(g_hAllocator, &vmaStats);
625 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
626 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
627 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
628
629 // Signal threads to deallocate
630 SetEvent(threadsFinishEvent);
631
632 // Wait for threads finished
633 for(size_t i = 0; i < bkgThreads.size(); ++i)
634 bkgThreads[i].join();
635 bkgThreads.clear();
636
637 CloseHandle(threadsFinishEvent);
638
639 // Deallocate remaining common resources
640 while(!commonAllocations.empty())
641 {
642 size_t indexToFree = 0;
643 switch(config.FreeOrder)
644 {
645 case FREE_ORDER::FORWARD:
646 indexToFree = 0;
647 break;
648 case FREE_ORDER::BACKWARD:
649 indexToFree = commonAllocations.size() - 1;
650 break;
651 case FREE_ORDER::RANDOM:
652 indexToFree = mainRand.Generate() % commonAllocations.size();
653 break;
654 }
655
656 {
657 DeallocationTimeRegisterObj timeRegisterObj{outResult};
658 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
659 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
660 else
661 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
662 }
663 commonAllocations.erase(commonAllocations.begin() + indexToFree);
664 }
665
666 if(allocationCount)
667 {
668 outResult.AllocationTimeAvg /= allocationCount;
669 outResult.DeallocationTimeAvg /= allocationCount;
670 }
671
672 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
673
674 return res;
675}
676
Adam Sawicki51fa9662018-10-03 13:44:29 +0200677void SaveAllocatorStatsToFile(const wchar_t* filePath)
Adam Sawickib8333fb2018-03-13 16:15:53 +0100678{
Adam Sawicki4d844e22019-01-24 16:21:05 +0100679 wprintf(L"Saving JSON dump to file \"%s\"\n", filePath);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100680 char* stats;
Adam Sawickie44c6262018-06-15 14:30:39 +0200681 vmaBuildStatsString(g_hAllocator, &stats, VK_TRUE);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100682 SaveFile(filePath, stats, strlen(stats));
Adam Sawickie44c6262018-06-15 14:30:39 +0200683 vmaFreeStatsString(g_hAllocator, stats);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100684}
685
686struct AllocInfo
687{
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200688 VmaAllocation m_Allocation = VK_NULL_HANDLE;
689 VkBuffer m_Buffer = VK_NULL_HANDLE;
690 VkImage m_Image = VK_NULL_HANDLE;
Adam Sawickia52012d2019-12-23 15:28:51 +0100691 VkImageLayout m_ImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200692 uint32_t m_StartValue = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100693 union
694 {
695 VkBufferCreateInfo m_BufferInfo;
696 VkImageCreateInfo m_ImageInfo;
697 };
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200698
699 void CreateBuffer(
700 const VkBufferCreateInfo& bufCreateInfo,
701 const VmaAllocationCreateInfo& allocCreateInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +0100702 void CreateImage(
703 const VkImageCreateInfo& imageCreateInfo,
704 const VmaAllocationCreateInfo& allocCreateInfo,
705 VkImageLayout layout);
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200706 void Destroy();
Adam Sawickib8333fb2018-03-13 16:15:53 +0100707};
708
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200709void AllocInfo::CreateBuffer(
710 const VkBufferCreateInfo& bufCreateInfo,
711 const VmaAllocationCreateInfo& allocCreateInfo)
712{
713 m_BufferInfo = bufCreateInfo;
714 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &m_Buffer, &m_Allocation, nullptr);
715 TEST(res == VK_SUCCESS);
716}
Adam Sawickia52012d2019-12-23 15:28:51 +0100717void AllocInfo::CreateImage(
718 const VkImageCreateInfo& imageCreateInfo,
719 const VmaAllocationCreateInfo& allocCreateInfo,
720 VkImageLayout layout)
721{
722 m_ImageInfo = imageCreateInfo;
723 m_ImageLayout = layout;
724 VkResult res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &m_Image, &m_Allocation, nullptr);
725 TEST(res == VK_SUCCESS);
726}
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200727
728void AllocInfo::Destroy()
729{
730 if(m_Image)
731 {
Adam Sawicki1f84f622019-07-02 13:40:01 +0200732 vkDestroyImage(g_hDevice, m_Image, g_Allocs);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100733 m_Image = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200734 }
735 if(m_Buffer)
736 {
Adam Sawicki1f84f622019-07-02 13:40:01 +0200737 vkDestroyBuffer(g_hDevice, m_Buffer, g_Allocs);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100738 m_Buffer = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200739 }
740 if(m_Allocation)
741 {
742 vmaFreeMemory(g_hAllocator, m_Allocation);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100743 m_Allocation = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200744 }
745}
746
Adam Sawickif2975342018-10-16 13:49:02 +0200747class StagingBufferCollection
748{
749public:
750 StagingBufferCollection() { }
751 ~StagingBufferCollection();
752 // Returns false if maximum total size of buffers would be exceeded.
753 bool AcquireBuffer(VkDeviceSize size, VkBuffer& outBuffer, void*& outMappedPtr);
754 void ReleaseAllBuffers();
755
756private:
757 static const VkDeviceSize MAX_TOTAL_SIZE = 256ull * 1024 * 1024;
758 struct BufInfo
759 {
760 VmaAllocation Allocation = VK_NULL_HANDLE;
761 VkBuffer Buffer = VK_NULL_HANDLE;
762 VkDeviceSize Size = VK_WHOLE_SIZE;
763 void* MappedPtr = nullptr;
764 bool Used = false;
765 };
766 std::vector<BufInfo> m_Bufs;
767 // Including both used and unused.
768 VkDeviceSize m_TotalSize = 0;
769};
770
771StagingBufferCollection::~StagingBufferCollection()
772{
773 for(size_t i = m_Bufs.size(); i--; )
774 {
775 vmaDestroyBuffer(g_hAllocator, m_Bufs[i].Buffer, m_Bufs[i].Allocation);
776 }
777}
778
779bool StagingBufferCollection::AcquireBuffer(VkDeviceSize size, VkBuffer& outBuffer, void*& outMappedPtr)
780{
781 assert(size <= MAX_TOTAL_SIZE);
782
783 // Try to find existing unused buffer with best size.
784 size_t bestIndex = SIZE_MAX;
785 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
786 {
787 BufInfo& currBufInfo = m_Bufs[i];
788 if(!currBufInfo.Used && currBufInfo.Size >= size &&
789 (bestIndex == SIZE_MAX || currBufInfo.Size < m_Bufs[bestIndex].Size))
790 {
791 bestIndex = i;
792 }
793 }
794
795 if(bestIndex != SIZE_MAX)
796 {
797 m_Bufs[bestIndex].Used = true;
798 outBuffer = m_Bufs[bestIndex].Buffer;
799 outMappedPtr = m_Bufs[bestIndex].MappedPtr;
800 return true;
801 }
802
803 // Allocate new buffer with requested size.
804 if(m_TotalSize + size <= MAX_TOTAL_SIZE)
805 {
806 BufInfo bufInfo;
807 bufInfo.Size = size;
808 bufInfo.Used = true;
809
810 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
811 bufCreateInfo.size = size;
812 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
813
814 VmaAllocationCreateInfo allocCreateInfo = {};
815 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
816 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
817
818 VmaAllocationInfo allocInfo;
819 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
820 bufInfo.MappedPtr = allocInfo.pMappedData;
821 TEST(res == VK_SUCCESS && bufInfo.MappedPtr);
822
823 outBuffer = bufInfo.Buffer;
824 outMappedPtr = bufInfo.MappedPtr;
825
826 m_Bufs.push_back(std::move(bufInfo));
827
828 m_TotalSize += size;
829
830 return true;
831 }
832
833 // There are some unused but smaller buffers: Free them and try again.
834 bool hasUnused = false;
835 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
836 {
837 if(!m_Bufs[i].Used)
838 {
839 hasUnused = true;
840 break;
841 }
842 }
843 if(hasUnused)
844 {
845 for(size_t i = m_Bufs.size(); i--; )
846 {
847 if(!m_Bufs[i].Used)
848 {
849 m_TotalSize -= m_Bufs[i].Size;
850 vmaDestroyBuffer(g_hAllocator, m_Bufs[i].Buffer, m_Bufs[i].Allocation);
851 m_Bufs.erase(m_Bufs.begin() + i);
852 }
853 }
854
855 return AcquireBuffer(size, outBuffer, outMappedPtr);
856 }
857
858 return false;
859}
860
861void StagingBufferCollection::ReleaseAllBuffers()
862{
863 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
864 {
865 m_Bufs[i].Used = false;
866 }
867}
868
869static void UploadGpuData(const AllocInfo* allocInfo, size_t allocInfoCount)
870{
871 StagingBufferCollection stagingBufs;
872
873 bool cmdBufferStarted = false;
874 for(size_t allocInfoIndex = 0; allocInfoIndex < allocInfoCount; ++allocInfoIndex)
875 {
876 const AllocInfo& currAllocInfo = allocInfo[allocInfoIndex];
877 if(currAllocInfo.m_Buffer)
878 {
879 const VkDeviceSize size = currAllocInfo.m_BufferInfo.size;
880
881 VkBuffer stagingBuf = VK_NULL_HANDLE;
882 void* stagingBufMappedPtr = nullptr;
883 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
884 {
885 TEST(cmdBufferStarted);
886 EndSingleTimeCommands();
887 stagingBufs.ReleaseAllBuffers();
888 cmdBufferStarted = false;
889
890 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
891 TEST(ok);
892 }
893
894 // Fill staging buffer.
895 {
896 assert(size % sizeof(uint32_t) == 0);
897 uint32_t* stagingValPtr = (uint32_t*)stagingBufMappedPtr;
898 uint32_t val = currAllocInfo.m_StartValue;
899 for(size_t i = 0; i < size / sizeof(uint32_t); ++i)
900 {
901 *stagingValPtr = val;
902 ++stagingValPtr;
903 ++val;
904 }
905 }
906
907 // Issue copy command from staging buffer to destination buffer.
908 if(!cmdBufferStarted)
909 {
910 cmdBufferStarted = true;
911 BeginSingleTimeCommands();
912 }
913
914 VkBufferCopy copy = {};
915 copy.srcOffset = 0;
916 copy.dstOffset = 0;
917 copy.size = size;
918 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingBuf, currAllocInfo.m_Buffer, 1, &copy);
919 }
920 else
921 {
Adam Sawickia52012d2019-12-23 15:28:51 +0100922 TEST(currAllocInfo.m_ImageInfo.format == VK_FORMAT_R8G8B8A8_UNORM && "Only RGBA8 images are currently supported.");
923 TEST(currAllocInfo.m_ImageInfo.mipLevels == 1 && "Only single mip images are currently supported.");
924
925 const VkDeviceSize size = currAllocInfo.m_ImageInfo.extent.width * currAllocInfo.m_ImageInfo.extent.height * sizeof(uint32_t);
926
927 VkBuffer stagingBuf = VK_NULL_HANDLE;
928 void* stagingBufMappedPtr = nullptr;
929 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
930 {
931 TEST(cmdBufferStarted);
932 EndSingleTimeCommands();
933 stagingBufs.ReleaseAllBuffers();
934 cmdBufferStarted = false;
935
936 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
937 TEST(ok);
938 }
939
940 // Fill staging buffer.
941 {
942 assert(size % sizeof(uint32_t) == 0);
943 uint32_t *stagingValPtr = (uint32_t *)stagingBufMappedPtr;
944 uint32_t val = currAllocInfo.m_StartValue;
945 for(size_t i = 0; i < size / sizeof(uint32_t); ++i)
946 {
947 *stagingValPtr = val;
948 ++stagingValPtr;
949 ++val;
950 }
951 }
952
953 // Issue copy command from staging buffer to destination buffer.
954 if(!cmdBufferStarted)
955 {
956 cmdBufferStarted = true;
957 BeginSingleTimeCommands();
958 }
959
960
961 // Transfer to transfer dst layout
962 VkImageSubresourceRange subresourceRange = {
963 VK_IMAGE_ASPECT_COLOR_BIT,
964 0, VK_REMAINING_MIP_LEVELS,
965 0, VK_REMAINING_ARRAY_LAYERS
966 };
967
968 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
969 barrier.srcAccessMask = 0;
970 barrier.dstAccessMask = 0;
971 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
972 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
973 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
974 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
975 barrier.image = currAllocInfo.m_Image;
976 barrier.subresourceRange = subresourceRange;
977
978 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
979 0, nullptr,
980 0, nullptr,
981 1, &barrier);
982
983 // Copy image date
984 VkBufferImageCopy copy = {};
985 copy.bufferOffset = 0;
986 copy.bufferRowLength = 0;
987 copy.bufferImageHeight = 0;
988 copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
989 copy.imageSubresource.layerCount = 1;
990 copy.imageExtent = currAllocInfo.m_ImageInfo.extent;
991
992 vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, stagingBuf, currAllocInfo.m_Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
993
994 // Transfer to desired layout
995 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
996 barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
997 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
998 barrier.newLayout = currAllocInfo.m_ImageLayout;
999
1000 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
1001 0, nullptr,
1002 0, nullptr,
1003 1, &barrier);
Adam Sawickif2975342018-10-16 13:49:02 +02001004 }
1005 }
1006
1007 if(cmdBufferStarted)
1008 {
1009 EndSingleTimeCommands();
1010 stagingBufs.ReleaseAllBuffers();
1011 }
1012}
1013
1014static void ValidateGpuData(const AllocInfo* allocInfo, size_t allocInfoCount)
1015{
1016 StagingBufferCollection stagingBufs;
1017
1018 bool cmdBufferStarted = false;
1019 size_t validateAllocIndexOffset = 0;
1020 std::vector<void*> validateStagingBuffers;
1021 for(size_t allocInfoIndex = 0; allocInfoIndex < allocInfoCount; ++allocInfoIndex)
1022 {
1023 const AllocInfo& currAllocInfo = allocInfo[allocInfoIndex];
1024 if(currAllocInfo.m_Buffer)
1025 {
1026 const VkDeviceSize size = currAllocInfo.m_BufferInfo.size;
1027
1028 VkBuffer stagingBuf = VK_NULL_HANDLE;
1029 void* stagingBufMappedPtr = nullptr;
1030 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
1031 {
1032 TEST(cmdBufferStarted);
1033 EndSingleTimeCommands();
1034 cmdBufferStarted = false;
1035
1036 for(size_t validateIndex = 0;
1037 validateIndex < validateStagingBuffers.size();
1038 ++validateIndex)
1039 {
1040 const size_t validateAllocIndex = validateIndex + validateAllocIndexOffset;
1041 const VkDeviceSize validateSize = allocInfo[validateAllocIndex].m_BufferInfo.size;
1042 TEST(validateSize % sizeof(uint32_t) == 0);
1043 const uint32_t* stagingValPtr = (const uint32_t*)validateStagingBuffers[validateIndex];
1044 uint32_t val = allocInfo[validateAllocIndex].m_StartValue;
1045 bool valid = true;
1046 for(size_t i = 0; i < validateSize / sizeof(uint32_t); ++i)
1047 {
1048 if(*stagingValPtr != val)
1049 {
1050 valid = false;
1051 break;
1052 }
1053 ++stagingValPtr;
1054 ++val;
1055 }
1056 TEST(valid);
1057 }
1058
1059 stagingBufs.ReleaseAllBuffers();
1060
1061 validateAllocIndexOffset = allocInfoIndex;
1062 validateStagingBuffers.clear();
1063
1064 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
1065 TEST(ok);
1066 }
1067
1068 // Issue copy command from staging buffer to destination buffer.
1069 if(!cmdBufferStarted)
1070 {
1071 cmdBufferStarted = true;
1072 BeginSingleTimeCommands();
1073 }
1074
1075 VkBufferCopy copy = {};
1076 copy.srcOffset = 0;
1077 copy.dstOffset = 0;
1078 copy.size = size;
1079 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, currAllocInfo.m_Buffer, stagingBuf, 1, &copy);
1080
1081 // Sava mapped pointer for later validation.
1082 validateStagingBuffers.push_back(stagingBufMappedPtr);
1083 }
1084 else
1085 {
1086 TEST(0 && "Images not currently supported.");
1087 }
1088 }
1089
1090 if(cmdBufferStarted)
1091 {
1092 EndSingleTimeCommands();
1093
1094 for(size_t validateIndex = 0;
1095 validateIndex < validateStagingBuffers.size();
1096 ++validateIndex)
1097 {
1098 const size_t validateAllocIndex = validateIndex + validateAllocIndexOffset;
1099 const VkDeviceSize validateSize = allocInfo[validateAllocIndex].m_BufferInfo.size;
1100 TEST(validateSize % sizeof(uint32_t) == 0);
1101 const uint32_t* stagingValPtr = (const uint32_t*)validateStagingBuffers[validateIndex];
1102 uint32_t val = allocInfo[validateAllocIndex].m_StartValue;
1103 bool valid = true;
1104 for(size_t i = 0; i < validateSize / sizeof(uint32_t); ++i)
1105 {
1106 if(*stagingValPtr != val)
1107 {
1108 valid = false;
1109 break;
1110 }
1111 ++stagingValPtr;
1112 ++val;
1113 }
1114 TEST(valid);
1115 }
1116
1117 stagingBufs.ReleaseAllBuffers();
1118 }
1119}
1120
Adam Sawickib8333fb2018-03-13 16:15:53 +01001121static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
1122{
1123 outMemReq = {};
1124 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
1125 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
1126}
1127
1128static void CreateBuffer(
1129 VmaPool pool,
1130 const VkBufferCreateInfo& bufCreateInfo,
1131 bool persistentlyMapped,
1132 AllocInfo& outAllocInfo)
1133{
1134 outAllocInfo = {};
1135 outAllocInfo.m_BufferInfo = bufCreateInfo;
1136
1137 VmaAllocationCreateInfo allocCreateInfo = {};
1138 allocCreateInfo.pool = pool;
1139 if(persistentlyMapped)
1140 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1141
1142 VmaAllocationInfo vmaAllocInfo = {};
1143 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
1144
1145 // Setup StartValue and fill.
1146 {
1147 outAllocInfo.m_StartValue = (uint32_t)rand();
1148 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001149 TEST((data != nullptr) == persistentlyMapped);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001150 if(!persistentlyMapped)
1151 {
1152 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
1153 }
1154
1155 uint32_t value = outAllocInfo.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001156 TEST(bufCreateInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001157 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
1158 data[i] = value++;
1159
1160 if(!persistentlyMapped)
1161 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
1162 }
1163}
1164
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001165static void CreateAllocation(AllocInfo& outAllocation)
Adam Sawickib8333fb2018-03-13 16:15:53 +01001166{
1167 outAllocation.m_Allocation = nullptr;
1168 outAllocation.m_Buffer = nullptr;
1169 outAllocation.m_Image = nullptr;
1170 outAllocation.m_StartValue = (uint32_t)rand();
1171
1172 VmaAllocationCreateInfo vmaMemReq;
1173 GetMemReq(vmaMemReq);
1174
1175 VmaAllocationInfo allocInfo;
1176
1177 const bool isBuffer = true;//(rand() & 0x1) != 0;
1178 const bool isLarge = (rand() % 16) == 0;
1179 if(isBuffer)
1180 {
1181 const uint32_t bufferSize = isLarge ?
1182 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
1183 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
1184
1185 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1186 bufferInfo.size = bufferSize;
1187 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1188
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001189 VkResult res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001190 outAllocation.m_BufferInfo = bufferInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001191 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001192 }
1193 else
1194 {
1195 const uint32_t imageSizeX = isLarge ?
1196 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
1197 rand() % 1024 + 1; // 1 ... 1024
1198 const uint32_t imageSizeY = isLarge ?
1199 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
1200 rand() % 1024 + 1; // 1 ... 1024
1201
1202 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1203 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1204 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
1205 imageInfo.extent.width = imageSizeX;
1206 imageInfo.extent.height = imageSizeY;
1207 imageInfo.extent.depth = 1;
1208 imageInfo.mipLevels = 1;
1209 imageInfo.arrayLayers = 1;
1210 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1211 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1212 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
1213 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1214
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001215 VkResult res = vmaCreateImage(g_hAllocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001216 outAllocation.m_ImageInfo = imageInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001217 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001218 }
1219
1220 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
1221 if(allocInfo.pMappedData == nullptr)
1222 {
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001223 VkResult res = vmaMapMemory(g_hAllocator, outAllocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001224 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001225 }
1226
1227 uint32_t value = outAllocation.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001228 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001229 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
1230 data[i] = value++;
1231
1232 if(allocInfo.pMappedData == nullptr)
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001233 vmaUnmapMemory(g_hAllocator, outAllocation.m_Allocation);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001234}
1235
1236static void DestroyAllocation(const AllocInfo& allocation)
1237{
1238 if(allocation.m_Buffer)
1239 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
1240 else
1241 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
1242}
1243
1244static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
1245{
1246 for(size_t i = allocations.size(); i--; )
1247 DestroyAllocation(allocations[i]);
1248 allocations.clear();
1249}
1250
1251static void ValidateAllocationData(const AllocInfo& allocation)
1252{
1253 VmaAllocationInfo allocInfo;
1254 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
1255
1256 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
1257 if(allocInfo.pMappedData == nullptr)
1258 {
1259 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001260 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001261 }
1262
1263 uint32_t value = allocation.m_StartValue;
1264 bool ok = true;
1265 size_t i;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001266 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001267 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
1268 {
1269 if(data[i] != value++)
1270 {
1271 ok = false;
1272 break;
1273 }
1274 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02001275 TEST(ok);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001276
1277 if(allocInfo.pMappedData == nullptr)
1278 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
1279}
1280
1281static void RecreateAllocationResource(AllocInfo& allocation)
1282{
1283 VmaAllocationInfo allocInfo;
1284 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
1285
1286 if(allocation.m_Buffer)
1287 {
Adam Sawicki1f84f622019-07-02 13:40:01 +02001288 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001289
Adam Sawicki1f84f622019-07-02 13:40:01 +02001290 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, g_Allocs, &allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001291 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001292
1293 // Just to silence validation layer warnings.
1294 VkMemoryRequirements vkMemReq;
1295 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
Adam Sawicki2af57d72018-12-06 15:35:05 +01001296 TEST(vkMemReq.size >= allocation.m_BufferInfo.size);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001297
Adam Sawickiaf88c1b2019-07-02 12:34:26 +02001298 res = vmaBindBufferMemory(g_hAllocator, allocation.m_Allocation, allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001299 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001300 }
1301 else
1302 {
Adam Sawicki1f84f622019-07-02 13:40:01 +02001303 vkDestroyImage(g_hDevice, allocation.m_Image, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001304
Adam Sawicki1f84f622019-07-02 13:40:01 +02001305 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, g_Allocs, &allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001306 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001307
1308 // Just to silence validation layer warnings.
1309 VkMemoryRequirements vkMemReq;
1310 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
1311
Adam Sawickiaf88c1b2019-07-02 12:34:26 +02001312 res = vmaBindImageMemory(g_hAllocator, allocation.m_Allocation, allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001313 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001314 }
1315}
1316
1317static void Defragment(AllocInfo* allocs, size_t allocCount,
1318 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
1319 VmaDefragmentationStats* defragmentationStats = nullptr)
1320{
1321 std::vector<VmaAllocation> vmaAllocs(allocCount);
1322 for(size_t i = 0; i < allocCount; ++i)
1323 vmaAllocs[i] = allocs[i].m_Allocation;
1324
1325 std::vector<VkBool32> allocChanged(allocCount);
1326
1327 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
1328 defragmentationInfo, defragmentationStats) );
1329
1330 for(size_t i = 0; i < allocCount; ++i)
1331 {
1332 if(allocChanged[i])
1333 {
1334 RecreateAllocationResource(allocs[i]);
1335 }
1336 }
1337}
1338
1339static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
1340{
1341 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
1342 ValidateAllocationData(allocInfo);
1343 });
1344}
1345
1346void TestDefragmentationSimple()
1347{
1348 wprintf(L"Test defragmentation simple\n");
1349
1350 RandomNumberGenerator rand(667);
1351
1352 const VkDeviceSize BUF_SIZE = 0x10000;
1353 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
1354
1355 const VkDeviceSize MIN_BUF_SIZE = 32;
1356 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
1357 auto RandomBufSize = [&]() -> VkDeviceSize {
1358 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
1359 };
1360
1361 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1362 bufCreateInfo.size = BUF_SIZE;
1363 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1364
1365 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
1366 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1367
1368 uint32_t memTypeIndex = UINT32_MAX;
1369 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
1370
1371 VmaPoolCreateInfo poolCreateInfo = {};
1372 poolCreateInfo.blockSize = BLOCK_SIZE;
1373 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1374
1375 VmaPool pool;
1376 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
1377
Adam Sawickie1681912018-11-23 17:50:12 +01001378 // Defragmentation of empty pool.
1379 {
1380 VmaDefragmentationInfo2 defragInfo = {};
1381 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
1382 defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
1383 defragInfo.poolCount = 1;
1384 defragInfo.pPools = &pool;
1385
1386 VmaDefragmentationStats defragStats = {};
1387 VmaDefragmentationContext defragCtx = nullptr;
1388 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &defragStats, &defragCtx);
1389 TEST(res >= VK_SUCCESS);
1390 vmaDefragmentationEnd(g_hAllocator, defragCtx);
1391 TEST(defragStats.allocationsMoved == 0 && defragStats.bytesFreed == 0 &&
1392 defragStats.bytesMoved == 0 && defragStats.deviceMemoryBlocksFreed == 0);
1393 }
1394
Adam Sawickib8333fb2018-03-13 16:15:53 +01001395 std::vector<AllocInfo> allocations;
1396
1397 // persistentlyMappedOption = 0 - not persistently mapped.
1398 // persistentlyMappedOption = 1 - persistently mapped.
1399 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
1400 {
1401 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
1402 const bool persistentlyMapped = persistentlyMappedOption != 0;
1403
1404 // # Test 1
1405 // Buffers of fixed size.
1406 // Fill 2 blocks. Remove odd buffers. Defragment everything.
1407 // Expected result: at least 1 block freed.
1408 {
1409 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1410 {
1411 AllocInfo allocInfo;
1412 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1413 allocations.push_back(allocInfo);
1414 }
1415
1416 for(size_t i = 1; i < allocations.size(); ++i)
1417 {
1418 DestroyAllocation(allocations[i]);
1419 allocations.erase(allocations.begin() + i);
1420 }
1421
1422 VmaDefragmentationStats defragStats;
1423 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001424 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
1425 TEST(defragStats.deviceMemoryBlocksFreed >= 1);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001426
1427 ValidateAllocationsData(allocations.data(), allocations.size());
1428
1429 DestroyAllAllocations(allocations);
1430 }
1431
1432 // # Test 2
1433 // Buffers of fixed size.
1434 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
1435 // Expected result: Each of 4 interations makes some progress.
1436 {
1437 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1438 {
1439 AllocInfo allocInfo;
1440 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1441 allocations.push_back(allocInfo);
1442 }
1443
1444 for(size_t i = 1; i < allocations.size(); ++i)
1445 {
1446 DestroyAllocation(allocations[i]);
1447 allocations.erase(allocations.begin() + i);
1448 }
1449
1450 VmaDefragmentationInfo defragInfo = {};
1451 defragInfo.maxAllocationsToMove = 1;
1452 defragInfo.maxBytesToMove = BUF_SIZE;
1453
1454 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
1455 {
1456 VmaDefragmentationStats defragStats;
1457 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001458 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001459 }
1460
1461 ValidateAllocationsData(allocations.data(), allocations.size());
1462
1463 DestroyAllAllocations(allocations);
1464 }
1465
1466 // # Test 3
1467 // Buffers of variable size.
1468 // Create a number of buffers. Remove some percent of them.
1469 // Defragment while having some percent of them unmovable.
1470 // Expected result: Just simple validation.
1471 {
1472 for(size_t i = 0; i < 100; ++i)
1473 {
1474 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
1475 localBufCreateInfo.size = RandomBufSize();
1476
1477 AllocInfo allocInfo;
1478 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1479 allocations.push_back(allocInfo);
1480 }
1481
1482 const uint32_t percentToDelete = 60;
1483 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
1484 for(size_t i = 0; i < numberToDelete; ++i)
1485 {
1486 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
1487 DestroyAllocation(allocations[indexToDelete]);
1488 allocations.erase(allocations.begin() + indexToDelete);
1489 }
1490
1491 // Non-movable allocations will be at the beginning of allocations array.
1492 const uint32_t percentNonMovable = 20;
1493 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
1494 for(size_t i = 0; i < numberNonMovable; ++i)
1495 {
1496 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
1497 if(indexNonMovable != i)
1498 std::swap(allocations[i], allocations[indexNonMovable]);
1499 }
1500
1501 VmaDefragmentationStats defragStats;
1502 Defragment(
1503 allocations.data() + numberNonMovable,
1504 allocations.size() - numberNonMovable,
1505 nullptr, &defragStats);
1506
1507 ValidateAllocationsData(allocations.data(), allocations.size());
1508
1509 DestroyAllAllocations(allocations);
1510 }
1511 }
1512
Adam Sawicki647cf242018-11-23 17:58:00 +01001513 /*
1514 Allocation that must be move to an overlapping place using memmove().
1515 Create 2 buffers, second slightly bigger than the first. Delete first. Then defragment.
1516 */
Adam Sawickibdb89a92018-12-13 11:56:30 +01001517 if(VMA_DEBUG_MARGIN == 0) // FAST algorithm works only when DEBUG_MARGIN disabled.
Adam Sawicki647cf242018-11-23 17:58:00 +01001518 {
1519 AllocInfo allocInfo[2];
1520
1521 bufCreateInfo.size = BUF_SIZE;
1522 CreateBuffer(pool, bufCreateInfo, false, allocInfo[0]);
1523 const VkDeviceSize biggerBufSize = BUF_SIZE + BUF_SIZE / 256;
1524 bufCreateInfo.size = biggerBufSize;
1525 CreateBuffer(pool, bufCreateInfo, false, allocInfo[1]);
1526
1527 DestroyAllocation(allocInfo[0]);
1528
1529 VmaDefragmentationStats defragStats;
1530 Defragment(&allocInfo[1], 1, nullptr, &defragStats);
1531 // If this fails, it means we couldn't do memmove with overlapping regions.
1532 TEST(defragStats.allocationsMoved == 1 && defragStats.bytesMoved > 0);
1533
1534 ValidateAllocationsData(&allocInfo[1], 1);
1535 DestroyAllocation(allocInfo[1]);
1536 }
1537
Adam Sawickib8333fb2018-03-13 16:15:53 +01001538 vmaDestroyPool(g_hAllocator, pool);
1539}
1540
Adam Sawicki52076eb2018-11-22 16:14:50 +01001541void TestDefragmentationWholePool()
1542{
1543 wprintf(L"Test defragmentation whole pool\n");
1544
1545 RandomNumberGenerator rand(668);
1546
1547 const VkDeviceSize BUF_SIZE = 0x10000;
1548 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
1549
1550 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1551 bufCreateInfo.size = BUF_SIZE;
1552 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1553
1554 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
1555 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1556
1557 uint32_t memTypeIndex = UINT32_MAX;
1558 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
1559
1560 VmaPoolCreateInfo poolCreateInfo = {};
1561 poolCreateInfo.blockSize = BLOCK_SIZE;
1562 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1563
1564 VmaDefragmentationStats defragStats[2];
1565 for(size_t caseIndex = 0; caseIndex < 2; ++caseIndex)
1566 {
1567 VmaPool pool;
1568 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
1569
1570 std::vector<AllocInfo> allocations;
1571
1572 // Buffers of fixed size.
1573 // Fill 2 blocks. Remove odd buffers. Defragment all of them.
1574 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1575 {
1576 AllocInfo allocInfo;
1577 CreateBuffer(pool, bufCreateInfo, false, allocInfo);
1578 allocations.push_back(allocInfo);
1579 }
1580
1581 for(size_t i = 1; i < allocations.size(); ++i)
1582 {
1583 DestroyAllocation(allocations[i]);
1584 allocations.erase(allocations.begin() + i);
1585 }
1586
1587 VmaDefragmentationInfo2 defragInfo = {};
1588 defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
1589 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
1590 std::vector<VmaAllocation> allocationsToDefrag;
1591 if(caseIndex == 0)
1592 {
1593 defragInfo.poolCount = 1;
1594 defragInfo.pPools = &pool;
1595 }
1596 else
1597 {
1598 const size_t allocCount = allocations.size();
1599 allocationsToDefrag.resize(allocCount);
1600 std::transform(
1601 allocations.begin(), allocations.end(),
1602 allocationsToDefrag.begin(),
1603 [](const AllocInfo& allocInfo) { return allocInfo.m_Allocation; });
1604 defragInfo.allocationCount = (uint32_t)allocCount;
1605 defragInfo.pAllocations = allocationsToDefrag.data();
1606 }
1607
1608 VmaDefragmentationContext defragCtx = VK_NULL_HANDLE;
1609 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &defragStats[caseIndex], &defragCtx);
1610 TEST(res >= VK_SUCCESS);
1611 vmaDefragmentationEnd(g_hAllocator, defragCtx);
1612
1613 TEST(defragStats[caseIndex].allocationsMoved > 0 && defragStats[caseIndex].bytesMoved > 0);
1614
1615 ValidateAllocationsData(allocations.data(), allocations.size());
1616
1617 DestroyAllAllocations(allocations);
1618
1619 vmaDestroyPool(g_hAllocator, pool);
1620 }
1621
1622 TEST(defragStats[0].bytesMoved == defragStats[1].bytesMoved);
1623 TEST(defragStats[0].allocationsMoved == defragStats[1].allocationsMoved);
1624 TEST(defragStats[0].bytesFreed == defragStats[1].bytesFreed);
1625 TEST(defragStats[0].deviceMemoryBlocksFreed == defragStats[1].deviceMemoryBlocksFreed);
1626}
1627
Adam Sawickib8333fb2018-03-13 16:15:53 +01001628void TestDefragmentationFull()
1629{
1630 std::vector<AllocInfo> allocations;
1631
1632 // Create initial allocations.
1633 for(size_t i = 0; i < 400; ++i)
1634 {
1635 AllocInfo allocation;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001636 CreateAllocation(allocation);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001637 allocations.push_back(allocation);
1638 }
1639
1640 // Delete random allocations
1641 const size_t allocationsToDeletePercent = 80;
1642 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
1643 for(size_t i = 0; i < allocationsToDelete; ++i)
1644 {
1645 size_t index = (size_t)rand() % allocations.size();
1646 DestroyAllocation(allocations[index]);
1647 allocations.erase(allocations.begin() + index);
1648 }
1649
1650 for(size_t i = 0; i < allocations.size(); ++i)
1651 ValidateAllocationData(allocations[i]);
1652
Adam Sawicki0667e332018-08-24 17:26:44 +02001653 //SaveAllocatorStatsToFile(L"Before.csv");
Adam Sawickib8333fb2018-03-13 16:15:53 +01001654
1655 {
1656 std::vector<VmaAllocation> vmaAllocations(allocations.size());
1657 for(size_t i = 0; i < allocations.size(); ++i)
1658 vmaAllocations[i] = allocations[i].m_Allocation;
1659
1660 const size_t nonMovablePercent = 0;
1661 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
1662 for(size_t i = 0; i < nonMovableCount; ++i)
1663 {
1664 size_t index = (size_t)rand() % vmaAllocations.size();
1665 vmaAllocations.erase(vmaAllocations.begin() + index);
1666 }
1667
1668 const uint32_t defragCount = 1;
1669 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1670 {
1671 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1672
1673 VmaDefragmentationInfo defragmentationInfo;
1674 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1675 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1676
1677 wprintf(L"Defragmentation #%u\n", defragIndex);
1678
1679 time_point begTime = std::chrono::high_resolution_clock::now();
1680
1681 VmaDefragmentationStats stats;
1682 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001683 TEST(res >= 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001684
1685 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1686
1687 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1688 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1689 wprintf(L"Time: %.2f s\n", defragmentDuration);
1690
1691 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1692 {
1693 if(allocationsChanged[i])
1694 {
1695 RecreateAllocationResource(allocations[i]);
1696 }
1697 }
1698
1699 for(size_t i = 0; i < allocations.size(); ++i)
1700 ValidateAllocationData(allocations[i]);
1701
Adam Sawicki0667e332018-08-24 17:26:44 +02001702 //wchar_t fileName[MAX_PATH];
1703 //swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
1704 //SaveAllocatorStatsToFile(fileName);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001705 }
1706 }
1707
1708 // Destroy all remaining allocations.
1709 DestroyAllAllocations(allocations);
1710}
1711
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001712static void TestDefragmentationGpu()
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001713{
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001714 wprintf(L"Test defragmentation GPU\n");
Adam Sawicki05704002018-11-08 16:07:29 +01001715 g_MemoryAliasingWarningEnabled = false;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001716
1717 std::vector<AllocInfo> allocations;
1718
1719 // Create that many allocations to surely fill 3 new blocks of 256 MB.
Adam Sawickic6ede152018-11-16 17:04:14 +01001720 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
1721 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001722 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
Adam Sawickic6ede152018-11-16 17:04:14 +01001723 const size_t bufCount = (size_t)(totalSize / bufSizeMin);
1724 const size_t percentToLeave = 30;
1725 const size_t percentNonMovable = 3;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001726 RandomNumberGenerator rand = { 234522 };
1727
1728 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001729
1730 VmaAllocationCreateInfo allocCreateInfo = {};
1731 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
Adam Sawickic6ede152018-11-16 17:04:14 +01001732 allocCreateInfo.flags = 0;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001733
1734 // Create all intended buffers.
1735 for(size_t i = 0; i < bufCount; ++i)
1736 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001737 bufCreateInfo.size = align_up(rand.Generate() % (bufSizeMax - bufSizeMin) + bufSizeMin, 32ull);
1738
1739 if(rand.Generate() % 100 < percentNonMovable)
1740 {
1741 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
1742 VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1743 VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1744 allocCreateInfo.pUserData = (void*)(uintptr_t)2;
1745 }
1746 else
1747 {
1748 // Different usage just to see different color in output from VmaDumpVis.
1749 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
1750 VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1751 VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1752 // And in JSON dump.
1753 allocCreateInfo.pUserData = (void*)(uintptr_t)1;
1754 }
1755
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001756 AllocInfo alloc;
1757 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
1758 alloc.m_StartValue = rand.Generate();
1759 allocations.push_back(alloc);
1760 }
1761
1762 // Destroy some percentage of them.
1763 {
1764 const size_t buffersToDestroy = round_div<size_t>(bufCount * (100 - percentToLeave), 100);
1765 for(size_t i = 0; i < buffersToDestroy; ++i)
1766 {
1767 const size_t index = rand.Generate() % allocations.size();
1768 allocations[index].Destroy();
1769 allocations.erase(allocations.begin() + index);
1770 }
1771 }
1772
1773 // Fill them with meaningful data.
1774 UploadGpuData(allocations.data(), allocations.size());
1775
Adam Sawickic6ede152018-11-16 17:04:14 +01001776 wchar_t fileName[MAX_PATH];
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001777 swprintf_s(fileName, L"GPU_defragmentation_A_before.json");
Adam Sawickic6ede152018-11-16 17:04:14 +01001778 SaveAllocatorStatsToFile(fileName);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001779
1780 // Defragment using GPU only.
1781 {
1782 const size_t allocCount = allocations.size();
Adam Sawicki440307e2018-10-18 15:05:19 +02001783
Adam Sawickic6ede152018-11-16 17:04:14 +01001784 std::vector<VmaAllocation> allocationPtrs;
1785 std::vector<VkBool32> allocationChanged;
1786 std::vector<size_t> allocationOriginalIndex;
1787
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001788 for(size_t i = 0; i < allocCount; ++i)
1789 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001790 VmaAllocationInfo allocInfo = {};
1791 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
1792 if((uintptr_t)allocInfo.pUserData == 1) // Movable
1793 {
1794 allocationPtrs.push_back(allocations[i].m_Allocation);
1795 allocationChanged.push_back(VK_FALSE);
1796 allocationOriginalIndex.push_back(i);
1797 }
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001798 }
Adam Sawickic6ede152018-11-16 17:04:14 +01001799
1800 const size_t movableAllocCount = allocationPtrs.size();
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001801
1802 BeginSingleTimeCommands();
1803
1804 VmaDefragmentationInfo2 defragInfo = {};
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001805 defragInfo.flags = 0;
Adam Sawickic6ede152018-11-16 17:04:14 +01001806 defragInfo.allocationCount = (uint32_t)movableAllocCount;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001807 defragInfo.pAllocations = allocationPtrs.data();
Adam Sawicki440307e2018-10-18 15:05:19 +02001808 defragInfo.pAllocationsChanged = allocationChanged.data();
1809 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001810 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
1811 defragInfo.commandBuffer = g_hTemporaryCommandBuffer;
1812
1813 VmaDefragmentationStats stats = {};
1814 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
1815 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
1816 TEST(res >= VK_SUCCESS);
1817
1818 EndSingleTimeCommands();
1819
1820 vmaDefragmentationEnd(g_hAllocator, ctx);
1821
Adam Sawickic6ede152018-11-16 17:04:14 +01001822 for(size_t i = 0; i < movableAllocCount; ++i)
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001823 {
1824 if(allocationChanged[i])
1825 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001826 const size_t origAllocIndex = allocationOriginalIndex[i];
1827 RecreateAllocationResource(allocations[origAllocIndex]);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001828 }
1829 }
1830
Adam Sawicki4d844e22019-01-24 16:21:05 +01001831 // If corruption detection is enabled, GPU defragmentation may not work on
1832 // memory types that have this detection active, e.g. on Intel.
Adam Sawickia1f727c2019-01-24 16:25:11 +01001833 #if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
Adam Sawicki4d844e22019-01-24 16:21:05 +01001834 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
1835 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
Adam Sawickia1f727c2019-01-24 16:25:11 +01001836 #endif
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001837 }
1838
1839 ValidateGpuData(allocations.data(), allocations.size());
1840
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001841 swprintf_s(fileName, L"GPU_defragmentation_B_after.json");
Adam Sawickic6ede152018-11-16 17:04:14 +01001842 SaveAllocatorStatsToFile(fileName);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001843
1844 // Destroy all remaining buffers.
1845 for(size_t i = allocations.size(); i--; )
1846 {
1847 allocations[i].Destroy();
1848 }
Adam Sawicki05704002018-11-08 16:07:29 +01001849
1850 g_MemoryAliasingWarningEnabled = true;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001851}
1852
Adam Sawickia52012d2019-12-23 15:28:51 +01001853static void ProcessDefragmentationStepInfo(VmaDefragmentationStepInfo &stepInfo)
1854{
1855 std::vector<VkImageMemoryBarrier> beginImageBarriers;
1856 std::vector<VkImageMemoryBarrier> finalizeImageBarriers;
1857
1858 VkPipelineStageFlags beginSrcStageMask = 0;
1859 VkPipelineStageFlags beginDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1860
1861 VkPipelineStageFlags finalizeSrcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1862 VkPipelineStageFlags finalizeDstStageMask = 0;
1863
1864 bool wantsMemoryBarrier = false;
1865
1866 VkMemoryBarrier beginMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
1867 VkMemoryBarrier finalizeMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
1868
1869 std::vector<void *> newHandles;
1870
1871 for(uint32_t i = 0; i < stepInfo.moveCount; ++ i)
1872 {
1873 VmaAllocationInfo info;
1874 vmaGetAllocationInfo(g_hAllocator, stepInfo.pMoves[i].allocation, &info);
1875
1876 AllocInfo *allocInfo = (AllocInfo *)info.pUserData;
1877
1878 if(allocInfo->m_Image)
1879 {
1880 VkImage newImage;
1881
1882 const VkResult result = vkCreateImage(g_hDevice, &allocInfo->m_ImageInfo, g_Allocs, &newImage);
1883 TEST(result >= VK_SUCCESS);
1884
1885 vkBindImageMemory(g_hDevice, newImage, stepInfo.pMoves[i].memory, stepInfo.pMoves[i].offset);
1886 newHandles.push_back(newImage);
1887
1888 // Keep track of our pipeline stages that we need to wait/signal on
1889 beginSrcStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1890 finalizeDstStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1891
1892 // We need one pipeline barrier and two image layout transitions here
1893 // First we'll have to turn our newly created image into VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
1894 // And the second one is turning the old image into VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
1895
1896 VkImageSubresourceRange subresourceRange = {
1897 VK_IMAGE_ASPECT_COLOR_BIT,
1898 0, VK_REMAINING_MIP_LEVELS,
1899 0, VK_REMAINING_ARRAY_LAYERS
1900 };
1901
1902 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
1903 barrier.srcAccessMask = 0;
1904 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1905 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1906 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1907 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1908 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1909 barrier.image = newImage;
1910 barrier.subresourceRange = subresourceRange;
1911
1912 beginImageBarriers.push_back(barrier);
1913
1914 // Second barrier to convert the existing image. This one actually needs a real barrier
1915 barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
1916 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1917 barrier.oldLayout = allocInfo->m_ImageLayout;
1918 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1919 barrier.image = allocInfo->m_Image;
1920
1921 beginImageBarriers.push_back(barrier);
1922
1923 // And lastly we need a barrier that turns our new image into the layout of the old one
1924 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1925 barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1926 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1927 barrier.newLayout = allocInfo->m_ImageLayout;
1928 barrier.image = newImage;
1929
1930 finalizeImageBarriers.push_back(barrier);
1931 }
1932 else if(allocInfo->m_Buffer)
1933 {
1934 VkBuffer newBuffer;
1935
1936 const VkResult result = vkCreateBuffer(g_hDevice, &allocInfo->m_BufferInfo, g_Allocs, &newBuffer);
1937 TEST(result >= VK_SUCCESS);
1938
1939 vkBindBufferMemory(g_hDevice, newBuffer, stepInfo.pMoves[i].memory, stepInfo.pMoves[i].offset);
1940 newHandles.push_back(newBuffer);
1941
1942 // Keep track of our pipeline stages that we need to wait/signal on
1943 beginSrcStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1944 finalizeDstStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1945
1946 beginMemoryBarrier.srcAccessMask |= VK_ACCESS_MEMORY_WRITE_BIT;
1947 beginMemoryBarrier.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
1948
1949 finalizeMemoryBarrier.srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
1950 finalizeMemoryBarrier.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT;
1951
1952 wantsMemoryBarrier = true;
1953 }
1954 }
1955
1956 if(!beginImageBarriers.empty() || wantsMemoryBarrier)
1957 {
1958 const uint32_t memoryBarrierCount = wantsMemoryBarrier ? 1 : 0;
1959
1960 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, beginSrcStageMask, beginDstStageMask, 0,
1961 memoryBarrierCount, &beginMemoryBarrier,
1962 0, nullptr,
1963 (uint32_t)beginImageBarriers.size(), beginImageBarriers.data());
1964 }
1965
1966 for(uint32_t i = 0; i < stepInfo.moveCount; ++ i)
1967 {
1968 VmaAllocationInfo info;
1969 vmaGetAllocationInfo(g_hAllocator, stepInfo.pMoves[i].allocation, &info);
1970
1971 AllocInfo *allocInfo = (AllocInfo *)info.pUserData;
1972
1973 if(allocInfo->m_Image)
1974 {
1975 std::vector<VkImageCopy> imageCopies;
1976
1977 // Copy all mips of the source image into the target image
1978 VkOffset3D offset = { 0, 0, 0 };
1979 VkExtent3D extent = allocInfo->m_ImageInfo.extent;
1980
1981 VkImageSubresourceLayers subresourceLayers = {
1982 VK_IMAGE_ASPECT_COLOR_BIT,
1983 0,
1984 0, 1
1985 };
1986
1987 for(uint32_t mip = 0; mip < allocInfo->m_ImageInfo.mipLevels; ++ mip)
1988 {
1989 subresourceLayers.mipLevel = mip;
1990
1991 VkImageCopy imageCopy{
1992 subresourceLayers,
1993 offset,
1994 subresourceLayers,
1995 offset,
1996 extent
1997 };
1998
1999 imageCopies.push_back(imageCopy);
2000
2001 extent.width = std::max(uint32_t(1), extent.width >> 1);
2002 extent.height = std::max(uint32_t(1), extent.height >> 1);
2003 extent.depth = std::max(uint32_t(1), extent.depth >> 1);
2004 }
2005
2006 vkCmdCopyImage(
2007 g_hTemporaryCommandBuffer,
2008 allocInfo->m_Image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2009 (VkImage)newHandles[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
2010 (uint32_t)imageCopies.size(), imageCopies.data());
2011
2012 imageCopies.clear();
2013
2014 // Update our alloc info with the new resource to be used
2015 allocInfo->m_Image = (VkImage)newHandles[i];
2016 }
2017 else if(allocInfo->m_Buffer)
2018 {
2019 VkBufferCopy region = {
2020 0,
2021 0,
2022 allocInfo->m_BufferInfo.size };
2023
2024 vkCmdCopyBuffer(g_hTemporaryCommandBuffer,
2025 allocInfo->m_Buffer, (VkBuffer)newHandles[i],
2026 1, &region);
2027
2028
2029 // Update our alloc info with the new resource to be used
2030 allocInfo->m_Buffer = (VkBuffer)newHandles[i];
2031 }
2032 }
2033
2034
2035 if(!finalizeImageBarriers.empty() || wantsMemoryBarrier)
2036 {
2037 const uint32_t memoryBarrierCount = wantsMemoryBarrier ? 1 : 0;
2038
2039 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, finalizeSrcStageMask, finalizeDstStageMask, 0,
2040 memoryBarrierCount, &finalizeMemoryBarrier,
2041 0, nullptr,
2042 (uint32_t)finalizeImageBarriers.size(), finalizeImageBarriers.data());
2043 }
2044}
2045
2046
2047static void TestDefragmentationIncrementalBasic()
2048{
2049 wprintf(L"Test defragmentation incremental basic\n");
2050 g_MemoryAliasingWarningEnabled = false;
2051
2052 std::vector<AllocInfo> allocations;
2053
2054 // Create that many allocations to surely fill 3 new blocks of 256 MB.
2055 const std::array<uint32_t, 3> imageSizes = { 256, 512, 1024 };
2056 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
2057 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
2058 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
2059 const size_t imageCount = (size_t)(totalSize / (imageSizes[0] * imageSizes[0] * 4)) / 2;
2060 const size_t bufCount = (size_t)(totalSize / bufSizeMin) / 2;
2061 const size_t percentToLeave = 30;
2062 RandomNumberGenerator rand = { 234522 };
2063
2064 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2065 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2066 imageInfo.extent.depth = 1;
2067 imageInfo.mipLevels = 1;
2068 imageInfo.arrayLayers = 1;
2069 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2070 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
2071 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2072 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2073 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2074
2075 VmaAllocationCreateInfo allocCreateInfo = {};
2076 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2077 allocCreateInfo.flags = 0;
2078
2079 // Create all intended images.
2080 for(size_t i = 0; i < imageCount; ++i)
2081 {
2082 const uint32_t size = imageSizes[rand.Generate() % 3];
2083
2084 imageInfo.extent.width = size;
2085 imageInfo.extent.height = size;
2086
2087 AllocInfo alloc;
2088 alloc.CreateImage(imageInfo, allocCreateInfo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
2089 alloc.m_StartValue = 0;
2090
2091 allocations.push_back(alloc);
2092 }
2093
2094 // And all buffers
2095 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2096
2097 for(size_t i = 0; i < bufCount; ++i)
2098 {
2099 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
2100 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2101
2102 AllocInfo alloc;
2103 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
2104 alloc.m_StartValue = 0;
2105
2106 allocations.push_back(alloc);
2107 }
2108
2109 // Destroy some percentage of them.
2110 {
2111 const size_t allocationsToDestroy = round_div<size_t>((imageCount + bufCount) * (100 - percentToLeave), 100);
2112 for(size_t i = 0; i < allocationsToDestroy; ++i)
2113 {
2114 const size_t index = rand.Generate() % allocations.size();
2115 allocations[index].Destroy();
2116 allocations.erase(allocations.begin() + index);
2117 }
2118 }
2119
2120 {
2121 // Set our user data pointers. A real application should probably be more clever here
2122 const size_t allocationCount = allocations.size();
2123 for(size_t i = 0; i < allocationCount; ++i)
2124 {
2125 AllocInfo &alloc = allocations[i];
2126 vmaSetAllocationUserData(g_hAllocator, alloc.m_Allocation, &alloc);
2127 }
2128 }
2129
2130 // Fill them with meaningful data.
2131 UploadGpuData(allocations.data(), allocations.size());
2132
2133 wchar_t fileName[MAX_PATH];
2134 swprintf_s(fileName, L"GPU_defragmentation_incremental_basic_A_before.json");
2135 SaveAllocatorStatsToFile(fileName);
2136
2137 // Defragment using GPU only.
2138 {
2139 const size_t allocCount = allocations.size();
2140
2141 std::vector<VmaAllocation> allocationPtrs;
2142
2143 for(size_t i = 0; i < allocCount; ++i)
2144 {
2145 VmaAllocationInfo allocInfo = {};
2146 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
2147
2148 allocationPtrs.push_back(allocations[i].m_Allocation);
2149 }
2150
2151 const size_t movableAllocCount = allocationPtrs.size();
2152
2153 VmaDefragmentationInfo2 defragInfo = {};
2154 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_INCREMENTAL;
2155 defragInfo.allocationCount = (uint32_t)movableAllocCount;
2156 defragInfo.pAllocations = allocationPtrs.data();
2157 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2158 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2159
2160 VmaDefragmentationStats stats = {};
2161 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
2162 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
2163 TEST(res >= VK_SUCCESS);
2164
2165 res = VK_NOT_READY;
2166
2167 std::vector<VmaDefragmentationStepMoveInfo> moveInfo;
2168 moveInfo.resize(movableAllocCount);
2169
2170 while(res == VK_NOT_READY)
2171 {
2172 VmaDefragmentationStepInfo stepInfo = {};
2173 stepInfo.pMoves = moveInfo.data();
2174 stepInfo.moveCount = (uint32_t)moveInfo.size();
2175
2176 res = vmaDefragmentationStepBegin(g_hAllocator, &stepInfo, ctx);
2177 TEST(res >= VK_SUCCESS);
2178
2179 BeginSingleTimeCommands();
2180 ProcessDefragmentationStepInfo(stepInfo);
2181 EndSingleTimeCommands();
2182
2183 res = vmaDefragmentationStepEnd(g_hAllocator, ctx);
2184 }
2185
2186 TEST(res >= VK_SUCCESS);
2187 vmaDefragmentationEnd(g_hAllocator, ctx);
2188
2189 // If corruption detection is enabled, GPU defragmentation may not work on
2190 // memory types that have this detection active, e.g. on Intel.
2191#if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
2192 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
2193 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
2194#endif
2195 }
2196
2197 //ValidateGpuData(allocations.data(), allocations.size());
2198
2199 swprintf_s(fileName, L"GPU_defragmentation_incremental_basic_B_after.json");
2200 SaveAllocatorStatsToFile(fileName);
2201
2202 // Destroy all remaining buffers.
2203 for(size_t i = allocations.size(); i--; )
2204 {
2205 allocations[i].Destroy();
2206 }
2207
2208 g_MemoryAliasingWarningEnabled = true;
2209}
2210
2211void TestDefragmentationIncrementalComplex()
2212{
2213 wprintf(L"Test defragmentation incremental complex\n");
2214 g_MemoryAliasingWarningEnabled = false;
2215
2216 std::vector<AllocInfo> allocations;
2217
2218 // Create that many allocations to surely fill 3 new blocks of 256 MB.
2219 const std::array<uint32_t, 3> imageSizes = { 256, 512, 1024 };
2220 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
2221 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
2222 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
2223 const size_t imageCount = (size_t)(totalSize / (imageSizes[0] * imageSizes[0] * 4)) / 2;
2224 const size_t bufCount = (size_t)(totalSize / bufSizeMin) / 2;
2225 const size_t percentToLeave = 30;
2226 RandomNumberGenerator rand = { 234522 };
2227
2228 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2229 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2230 imageInfo.extent.depth = 1;
2231 imageInfo.mipLevels = 1;
2232 imageInfo.arrayLayers = 1;
2233 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2234 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
2235 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2236 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2237 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2238
2239 VmaAllocationCreateInfo allocCreateInfo = {};
2240 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2241 allocCreateInfo.flags = 0;
2242
2243 // Create all intended images.
2244 for(size_t i = 0; i < imageCount; ++i)
2245 {
2246 const uint32_t size = imageSizes[rand.Generate() % 3];
2247
2248 imageInfo.extent.width = size;
2249 imageInfo.extent.height = size;
2250
2251 AllocInfo alloc;
2252 alloc.CreateImage(imageInfo, allocCreateInfo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
2253 alloc.m_StartValue = 0;
2254
2255 allocations.push_back(alloc);
2256 }
2257
2258 // And all buffers
2259 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2260
2261 for(size_t i = 0; i < bufCount; ++i)
2262 {
2263 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
2264 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2265
2266 AllocInfo alloc;
2267 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
2268 alloc.m_StartValue = 0;
2269
2270 allocations.push_back(alloc);
2271 }
2272
2273 // Destroy some percentage of them.
2274 {
2275 const size_t allocationsToDestroy = round_div<size_t>((imageCount + bufCount) * (100 - percentToLeave), 100);
2276 for(size_t i = 0; i < allocationsToDestroy; ++i)
2277 {
2278 const size_t index = rand.Generate() % allocations.size();
2279 allocations[index].Destroy();
2280 allocations.erase(allocations.begin() + index);
2281 }
2282 }
2283
2284 {
2285 // Set our user data pointers. A real application should probably be more clever here
2286 const size_t allocationCount = allocations.size();
2287 for(size_t i = 0; i < allocationCount; ++i)
2288 {
2289 AllocInfo &alloc = allocations[i];
2290 vmaSetAllocationUserData(g_hAllocator, alloc.m_Allocation, &alloc);
2291 }
2292 }
2293
2294 // Fill them with meaningful data.
2295 UploadGpuData(allocations.data(), allocations.size());
2296
2297 wchar_t fileName[MAX_PATH];
2298 swprintf_s(fileName, L"GPU_defragmentation_incremental_complex_A_before.json");
2299 SaveAllocatorStatsToFile(fileName);
2300
2301 std::vector<AllocInfo> additionalAllocations;
2302
2303#define MakeAdditionalAllocation() \
2304 do { \
2305 { \
2306 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16); \
2307 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; \
2308 \
2309 AllocInfo alloc; \
2310 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo); \
2311 \
2312 additionalAllocations.push_back(alloc); \
2313 } \
2314 } while(0)
2315
2316 // Defragment using GPU only.
2317 {
2318 const size_t allocCount = allocations.size();
2319
2320 std::vector<VmaAllocation> allocationPtrs;
2321
2322 for(size_t i = 0; i < allocCount; ++i)
2323 {
2324 VmaAllocationInfo allocInfo = {};
2325 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
2326
2327 allocationPtrs.push_back(allocations[i].m_Allocation);
2328 }
2329
2330 const size_t movableAllocCount = allocationPtrs.size();
2331
2332 VmaDefragmentationInfo2 defragInfo = {};
2333 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_INCREMENTAL;
2334 defragInfo.allocationCount = (uint32_t)movableAllocCount;
2335 defragInfo.pAllocations = allocationPtrs.data();
2336 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2337 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2338
2339 VmaDefragmentationStats stats = {};
2340 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
2341 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
2342 TEST(res >= VK_SUCCESS);
2343
2344 res = VK_NOT_READY;
2345
2346 std::vector<VmaDefragmentationStepMoveInfo> moveInfo;
2347 moveInfo.resize(movableAllocCount);
2348
2349 MakeAdditionalAllocation();
2350
2351 while(res == VK_NOT_READY)
2352 {
2353 VmaDefragmentationStepInfo stepInfo = {};
2354 stepInfo.pMoves = moveInfo.data();
2355 stepInfo.moveCount = (uint32_t)moveInfo.size();
2356
2357 res = vmaDefragmentationStepBegin(g_hAllocator, &stepInfo, ctx);
2358 TEST(res >= VK_SUCCESS);
2359
2360 MakeAdditionalAllocation();
2361
2362 BeginSingleTimeCommands();
2363 ProcessDefragmentationStepInfo(stepInfo);
2364 EndSingleTimeCommands();
2365
2366 res = vmaDefragmentationStepEnd(g_hAllocator, ctx);
2367
2368 MakeAdditionalAllocation();
2369 }
2370
2371 TEST(res >= VK_SUCCESS);
2372 vmaDefragmentationEnd(g_hAllocator, ctx);
2373
2374 // If corruption detection is enabled, GPU defragmentation may not work on
2375 // memory types that have this detection active, e.g. on Intel.
2376#if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
2377 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
2378 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
2379#endif
2380 }
2381
2382 //ValidateGpuData(allocations.data(), allocations.size());
2383
2384 swprintf_s(fileName, L"GPU_defragmentation_incremental_complex_B_after.json");
2385 SaveAllocatorStatsToFile(fileName);
2386
2387 // Destroy all remaining buffers.
2388 for(size_t i = allocations.size(); i--; )
2389 {
2390 allocations[i].Destroy();
2391 }
2392
2393 for(size_t i = additionalAllocations.size(); i--; )
2394 {
2395 additionalAllocations[i].Destroy();
2396 }
2397
2398 g_MemoryAliasingWarningEnabled = true;
2399}
2400
2401
Adam Sawickib8333fb2018-03-13 16:15:53 +01002402static void TestUserData()
2403{
2404 VkResult res;
2405
2406 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2407 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
2408 bufCreateInfo.size = 0x10000;
2409
2410 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
2411 {
2412 // Opaque pointer
2413 {
2414
2415 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
2416 void* pointerToSomething = &res;
2417
2418 VmaAllocationCreateInfo allocCreateInfo = {};
2419 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2420 allocCreateInfo.pUserData = numberAsPointer;
2421 if(testIndex == 1)
2422 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2423
2424 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2425 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002426 TEST(res == VK_SUCCESS);
2427 TEST(allocInfo.pUserData = numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002428
2429 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002430 TEST(allocInfo.pUserData == numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002431
2432 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
2433 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002434 TEST(allocInfo.pUserData == pointerToSomething);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002435
2436 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2437 }
2438
2439 // String
2440 {
2441 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
2442 const char* name2 = "2";
2443 const size_t name1Len = strlen(name1);
2444
2445 char* name1Buf = new char[name1Len + 1];
2446 strcpy_s(name1Buf, name1Len + 1, name1);
2447
2448 VmaAllocationCreateInfo allocCreateInfo = {};
2449 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2450 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
2451 allocCreateInfo.pUserData = name1Buf;
2452 if(testIndex == 1)
2453 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2454
2455 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2456 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002457 TEST(res == VK_SUCCESS);
2458 TEST(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
2459 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002460
2461 delete[] name1Buf;
2462
2463 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002464 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002465
2466 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
2467 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002468 TEST(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002469
2470 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
2471 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002472 TEST(allocInfo.pUserData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002473
2474 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2475 }
2476 }
2477}
2478
Adam Sawicki370ab182018-11-08 16:31:00 +01002479static void TestInvalidAllocations()
2480{
2481 VkResult res;
2482
2483 VmaAllocationCreateInfo allocCreateInfo = {};
2484 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2485
2486 // Try to allocate 0 bytes.
2487 {
2488 VkMemoryRequirements memReq = {};
2489 memReq.size = 0; // !!!
2490 memReq.alignment = 4;
2491 memReq.memoryTypeBits = UINT32_MAX;
2492 VmaAllocation alloc = VK_NULL_HANDLE;
2493 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2494 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && alloc == VK_NULL_HANDLE);
2495 }
2496
2497 // Try to create buffer with size = 0.
2498 {
2499 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2500 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2501 bufCreateInfo.size = 0; // !!!
2502 VkBuffer buf = VK_NULL_HANDLE;
2503 VmaAllocation alloc = VK_NULL_HANDLE;
2504 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
2505 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && buf == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
2506 }
2507
2508 // Try to create image with one dimension = 0.
2509 {
2510 VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2511 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
2512 imageCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
2513 imageCreateInfo.extent.width = 128;
2514 imageCreateInfo.extent.height = 0; // !!!
2515 imageCreateInfo.extent.depth = 1;
2516 imageCreateInfo.mipLevels = 1;
2517 imageCreateInfo.arrayLayers = 1;
2518 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2519 imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
2520 imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
2521 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2522 VkImage image = VK_NULL_HANDLE;
2523 VmaAllocation alloc = VK_NULL_HANDLE;
2524 res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &image, &alloc, nullptr);
2525 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && image == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
2526 }
2527}
2528
Adam Sawickib8333fb2018-03-13 16:15:53 +01002529static void TestMemoryRequirements()
2530{
2531 VkResult res;
2532 VkBuffer buf;
2533 VmaAllocation alloc;
2534 VmaAllocationInfo allocInfo;
2535
2536 const VkPhysicalDeviceMemoryProperties* memProps;
2537 vmaGetMemoryProperties(g_hAllocator, &memProps);
2538
2539 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2540 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2541 bufInfo.size = 128;
2542
2543 VmaAllocationCreateInfo allocCreateInfo = {};
2544
2545 // No requirements.
2546 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002547 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002548 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2549
2550 // Usage.
2551 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2552 allocCreateInfo.requiredFlags = 0;
2553 allocCreateInfo.preferredFlags = 0;
2554 allocCreateInfo.memoryTypeBits = UINT32_MAX;
2555
2556 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002557 TEST(res == VK_SUCCESS);
2558 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002559 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2560
2561 // Required flags, preferred flags.
2562 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
2563 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2564 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
2565 allocCreateInfo.memoryTypeBits = 0;
2566
2567 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002568 TEST(res == VK_SUCCESS);
2569 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
2570 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002571 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2572
2573 // memoryTypeBits.
2574 const uint32_t memType = allocInfo.memoryType;
2575 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2576 allocCreateInfo.requiredFlags = 0;
2577 allocCreateInfo.preferredFlags = 0;
2578 allocCreateInfo.memoryTypeBits = 1u << memType;
2579
2580 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002581 TEST(res == VK_SUCCESS);
2582 TEST(allocInfo.memoryType == memType);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002583 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2584
2585}
2586
2587static void TestBasics()
2588{
2589 VkResult res;
2590
2591 TestMemoryRequirements();
2592
2593 // Lost allocation
2594 {
2595 VmaAllocation alloc = VK_NULL_HANDLE;
2596 vmaCreateLostAllocation(g_hAllocator, &alloc);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002597 TEST(alloc != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002598
2599 VmaAllocationInfo allocInfo;
2600 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002601 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
2602 TEST(allocInfo.size == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002603
2604 vmaFreeMemory(g_hAllocator, alloc);
2605 }
2606
2607 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
2608 {
2609 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2610 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
2611 bufCreateInfo.size = 128;
2612
2613 VmaAllocationCreateInfo allocCreateInfo = {};
2614 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2615 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2616
2617 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2618 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002619 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002620
2621 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2622
2623 // Same with OWN_MEMORY.
2624 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2625
2626 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002627 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002628
2629 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2630 }
2631
2632 TestUserData();
Adam Sawicki370ab182018-11-08 16:31:00 +01002633
2634 TestInvalidAllocations();
Adam Sawickib8333fb2018-03-13 16:15:53 +01002635}
2636
Adam Sawickiddcbf8c2019-11-22 15:22:42 +01002637static void TestPool_MinBlockCount()
2638{
2639#if defined(VMA_DEBUG_MARGIN) && VMA_DEBUG_MARGIN > 0
2640 return;
2641#endif
2642
2643 wprintf(L"Test Pool MinBlockCount\n");
2644 VkResult res;
2645
2646 static const VkDeviceSize ALLOC_SIZE = 512ull * 1024;
2647 static const VkDeviceSize BLOCK_SIZE = ALLOC_SIZE * 2; // Each block can fit 2 allocations.
2648
2649 VmaAllocationCreateInfo allocCreateInfo = {};
2650 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_COPY;
2651
2652 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2653 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2654 bufCreateInfo.size = ALLOC_SIZE;
2655
2656 VmaPoolCreateInfo poolCreateInfo = {};
2657 poolCreateInfo.blockSize = BLOCK_SIZE;
2658 poolCreateInfo.minBlockCount = 2; // At least 2 blocks always present.
2659 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2660 TEST(res == VK_SUCCESS);
2661
2662 VmaPool pool = VK_NULL_HANDLE;
2663 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2664 TEST(res == VK_SUCCESS && pool != VK_NULL_HANDLE);
2665
2666 // Check that there are 2 blocks preallocated as requested.
2667 VmaPoolStats begPoolStats = {};
2668 vmaGetPoolStats(g_hAllocator, pool, &begPoolStats);
2669 TEST(begPoolStats.blockCount == 2 && begPoolStats.allocationCount == 0 && begPoolStats.size == BLOCK_SIZE * 2);
2670
2671 // Allocate 5 buffers to create 3 blocks.
2672 static const uint32_t BUF_COUNT = 5;
2673 allocCreateInfo.pool = pool;
2674 std::vector<AllocInfo> allocs(BUF_COUNT);
2675 for(uint32_t i = 0; i < BUF_COUNT; ++i)
2676 {
2677 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &allocs[i].m_Buffer, &allocs[i].m_Allocation, nullptr);
2678 TEST(res == VK_SUCCESS && allocs[i].m_Buffer != VK_NULL_HANDLE && allocs[i].m_Allocation != VK_NULL_HANDLE);
2679 }
2680
2681 // Check that there are really 3 blocks.
2682 VmaPoolStats poolStats2 = {};
2683 vmaGetPoolStats(g_hAllocator, pool, &poolStats2);
2684 TEST(poolStats2.blockCount == 3 && poolStats2.allocationCount == BUF_COUNT && poolStats2.size == BLOCK_SIZE * 3);
2685
2686 // Free two first allocations to make one block empty.
2687 allocs[0].Destroy();
2688 allocs[1].Destroy();
2689
2690 // Check that there are still 3 blocks due to hysteresis.
2691 VmaPoolStats poolStats3 = {};
2692 vmaGetPoolStats(g_hAllocator, pool, &poolStats3);
2693 TEST(poolStats3.blockCount == 3 && poolStats3.allocationCount == BUF_COUNT - 2 && poolStats2.size == BLOCK_SIZE * 3);
2694
2695 // Free the last allocation to make second block empty.
2696 allocs[BUF_COUNT - 1].Destroy();
2697
2698 // Check that there are now 2 blocks only.
2699 VmaPoolStats poolStats4 = {};
2700 vmaGetPoolStats(g_hAllocator, pool, &poolStats4);
2701 TEST(poolStats4.blockCount == 2 && poolStats4.allocationCount == BUF_COUNT - 3 && poolStats4.size == BLOCK_SIZE * 2);
2702
2703 // Cleanup.
2704 for(size_t i = allocs.size(); i--; )
2705 {
2706 allocs[i].Destroy();
2707 }
2708 vmaDestroyPool(g_hAllocator, pool);
2709}
2710
Adam Sawickib8333fb2018-03-13 16:15:53 +01002711void TestHeapSizeLimit()
2712{
Adam Sawickib3f51102019-11-18 13:05:56 +01002713 const VkDeviceSize HEAP_SIZE_LIMIT = 200ull * 1024 * 1024; // 200 MB
2714 const VkDeviceSize BLOCK_SIZE = 20ull * 1024 * 1024; // 20 MB
Adam Sawickib8333fb2018-03-13 16:15:53 +01002715
2716 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
2717 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
2718 {
2719 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
2720 }
2721
2722 VmaAllocatorCreateInfo allocatorCreateInfo = {};
2723 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
2724 allocatorCreateInfo.device = g_hDevice;
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002725 allocatorCreateInfo.instance = g_hVulkanInstance;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002726 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
2727
2728 VmaAllocator hAllocator;
2729 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002730 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002731
2732 struct Item
2733 {
2734 VkBuffer hBuf;
2735 VmaAllocation hAlloc;
2736 };
2737 std::vector<Item> items;
2738
2739 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2740 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2741
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002742 // 1. Allocate two blocks of dedicated memory, half the size of BLOCK_SIZE.
2743 VmaAllocationInfo dedicatedAllocInfo;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002744 {
2745 VmaAllocationCreateInfo allocCreateInfo = {};
2746 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2747 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2748
2749 bufCreateInfo.size = BLOCK_SIZE / 2;
2750
2751 for(size_t i = 0; i < 2; ++i)
2752 {
2753 Item item;
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002754 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &dedicatedAllocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002755 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002756 items.push_back(item);
2757 }
2758 }
2759
2760 // Create pool to make sure allocations must be out of this memory type.
2761 VmaPoolCreateInfo poolCreateInfo = {};
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002762 poolCreateInfo.memoryTypeIndex = dedicatedAllocInfo.memoryType;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002763 poolCreateInfo.blockSize = BLOCK_SIZE;
2764
2765 VmaPool hPool;
2766 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002767 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002768
2769 // 2. Allocate normal buffers from all the remaining memory.
2770 {
2771 VmaAllocationCreateInfo allocCreateInfo = {};
2772 allocCreateInfo.pool = hPool;
2773
2774 bufCreateInfo.size = BLOCK_SIZE / 2;
2775
2776 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
2777 for(size_t i = 0; i < bufCount; ++i)
2778 {
2779 Item item;
2780 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002781 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002782 items.push_back(item);
2783 }
2784 }
2785
2786 // 3. Allocation of one more (even small) buffer should fail.
2787 {
2788 VmaAllocationCreateInfo allocCreateInfo = {};
2789 allocCreateInfo.pool = hPool;
2790
2791 bufCreateInfo.size = 128;
2792
2793 VkBuffer hBuf;
2794 VmaAllocation hAlloc;
2795 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002796 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002797 }
2798
2799 // Destroy everything.
2800 for(size_t i = items.size(); i--; )
2801 {
2802 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
2803 }
2804
2805 vmaDestroyPool(hAllocator, hPool);
2806
2807 vmaDestroyAllocator(hAllocator);
2808}
2809
Adam Sawicki212a4a62018-06-14 15:44:45 +02002810#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02002811static void TestDebugMargin()
2812{
2813 if(VMA_DEBUG_MARGIN == 0)
2814 {
2815 return;
2816 }
2817
2818 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02002819 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02002820
2821 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02002822 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02002823
2824 // Create few buffers of different size.
2825 const size_t BUF_COUNT = 10;
2826 BufferInfo buffers[BUF_COUNT];
2827 VmaAllocationInfo allocInfo[BUF_COUNT];
2828 for(size_t i = 0; i < 10; ++i)
2829 {
2830 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02002831 // Last one will be mapped.
2832 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02002833
2834 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002835 TEST(res == VK_SUCCESS);
Adam Sawicki73b16652018-06-11 16:39:25 +02002836 // Margin is preserved also at the beginning of a block.
Adam Sawickib8d34d52018-10-03 17:41:20 +02002837 TEST(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002838
2839 if(i == BUF_COUNT - 1)
2840 {
2841 // Fill with data.
Adam Sawickib8d34d52018-10-03 17:41:20 +02002842 TEST(allocInfo[i].pMappedData != nullptr);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002843 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
2844 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
2845 }
Adam Sawicki73b16652018-06-11 16:39:25 +02002846 }
2847
2848 // Check if their offsets preserve margin between them.
2849 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
2850 {
2851 if(lhs.deviceMemory != rhs.deviceMemory)
2852 {
2853 return lhs.deviceMemory < rhs.deviceMemory;
2854 }
2855 return lhs.offset < rhs.offset;
2856 });
2857 for(size_t i = 1; i < BUF_COUNT; ++i)
2858 {
2859 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
2860 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02002861 TEST(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
Adam Sawicki73b16652018-06-11 16:39:25 +02002862 }
2863 }
2864
Adam Sawicki212a4a62018-06-14 15:44:45 +02002865 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002866 TEST(res == VK_SUCCESS);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002867
Adam Sawicki73b16652018-06-11 16:39:25 +02002868 // Destroy all buffers.
2869 for(size_t i = BUF_COUNT; i--; )
2870 {
2871 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
2872 }
2873}
Adam Sawicki212a4a62018-06-14 15:44:45 +02002874#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02002875
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002876static void TestLinearAllocator()
2877{
2878 wprintf(L"Test linear allocator\n");
2879
2880 RandomNumberGenerator rand{645332};
2881
2882 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2883 sampleBufCreateInfo.size = 1024; // Whatever.
2884 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2885
2886 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2887 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2888
2889 VmaPoolCreateInfo poolCreateInfo = {};
2890 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002891 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002892
Adam Sawickiee082772018-06-20 17:45:49 +02002893 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002894 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2895 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2896
2897 VmaPool pool = nullptr;
2898 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002899 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002900
2901 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
2902
2903 VmaAllocationCreateInfo allocCreateInfo = {};
2904 allocCreateInfo.pool = pool;
2905
2906 constexpr size_t maxBufCount = 100;
2907 std::vector<BufferInfo> bufInfo;
2908
2909 constexpr VkDeviceSize bufSizeMin = 16;
2910 constexpr VkDeviceSize bufSizeMax = 1024;
2911 VmaAllocationInfo allocInfo;
2912 VkDeviceSize prevOffset = 0;
2913
2914 // Test one-time free.
2915 for(size_t i = 0; i < 2; ++i)
2916 {
2917 // Allocate number of buffers of varying size that surely fit into this block.
2918 VkDeviceSize bufSumSize = 0;
2919 for(size_t i = 0; i < maxBufCount; ++i)
2920 {
Adam Sawickifd366b62019-01-24 15:26:43 +01002921 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002922 BufferInfo newBufInfo;
2923 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2924 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002925 TEST(res == VK_SUCCESS);
2926 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002927 bufInfo.push_back(newBufInfo);
2928 prevOffset = allocInfo.offset;
2929 bufSumSize += bufCreateInfo.size;
2930 }
2931
2932 // Validate pool stats.
2933 VmaPoolStats stats;
2934 vmaGetPoolStats(g_hAllocator, pool, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002935 TEST(stats.size == poolCreateInfo.blockSize);
2936 TEST(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
2937 TEST(stats.allocationCount == bufInfo.size());
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002938
2939 // Destroy the buffers in random order.
2940 while(!bufInfo.empty())
2941 {
2942 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
2943 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
2944 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2945 bufInfo.erase(bufInfo.begin() + indexToDestroy);
2946 }
2947 }
2948
2949 // Test stack.
2950 {
2951 // Allocate number of buffers of varying size that surely fit into this block.
2952 for(size_t i = 0; i < maxBufCount; ++i)
2953 {
Adam Sawickifd366b62019-01-24 15:26:43 +01002954 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002955 BufferInfo newBufInfo;
2956 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2957 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002958 TEST(res == VK_SUCCESS);
2959 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002960 bufInfo.push_back(newBufInfo);
2961 prevOffset = allocInfo.offset;
2962 }
2963
2964 // Destroy few buffers from top of the stack.
2965 for(size_t i = 0; i < maxBufCount / 5; ++i)
2966 {
2967 const BufferInfo& currBufInfo = bufInfo.back();
2968 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2969 bufInfo.pop_back();
2970 }
2971
2972 // Create some more
2973 for(size_t i = 0; i < maxBufCount / 5; ++i)
2974 {
Adam Sawickifd366b62019-01-24 15:26:43 +01002975 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002976 BufferInfo newBufInfo;
2977 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2978 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002979 TEST(res == VK_SUCCESS);
2980 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002981 bufInfo.push_back(newBufInfo);
2982 prevOffset = allocInfo.offset;
2983 }
2984
2985 // Destroy the buffers in reverse order.
2986 while(!bufInfo.empty())
2987 {
2988 const BufferInfo& currBufInfo = bufInfo.back();
2989 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2990 bufInfo.pop_back();
2991 }
2992 }
2993
Adam Sawickiee082772018-06-20 17:45:49 +02002994 // Test ring buffer.
2995 {
2996 // Allocate number of buffers that surely fit into this block.
2997 bufCreateInfo.size = bufSizeMax;
2998 for(size_t i = 0; i < maxBufCount; ++i)
2999 {
3000 BufferInfo newBufInfo;
3001 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3002 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003003 TEST(res == VK_SUCCESS);
3004 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawickiee082772018-06-20 17:45:49 +02003005 bufInfo.push_back(newBufInfo);
3006 prevOffset = allocInfo.offset;
3007 }
3008
3009 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
3010 const size_t buffersPerIter = maxBufCount / 10 - 1;
3011 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
3012 for(size_t iter = 0; iter < iterCount; ++iter)
3013 {
3014 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
3015 {
3016 const BufferInfo& currBufInfo = bufInfo.front();
3017 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3018 bufInfo.erase(bufInfo.begin());
3019 }
3020 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
3021 {
3022 BufferInfo newBufInfo;
3023 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3024 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003025 TEST(res == VK_SUCCESS);
Adam Sawickiee082772018-06-20 17:45:49 +02003026 bufInfo.push_back(newBufInfo);
3027 }
3028 }
3029
3030 // Allocate buffers until we reach out-of-memory.
3031 uint32_t debugIndex = 0;
3032 while(res == VK_SUCCESS)
3033 {
3034 BufferInfo newBufInfo;
3035 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3036 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3037 if(res == VK_SUCCESS)
3038 {
3039 bufInfo.push_back(newBufInfo);
3040 }
3041 else
3042 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003043 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickiee082772018-06-20 17:45:49 +02003044 }
3045 ++debugIndex;
3046 }
3047
3048 // Destroy the buffers in random order.
3049 while(!bufInfo.empty())
3050 {
3051 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3052 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3053 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3054 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3055 }
3056 }
3057
Adam Sawicki680b2252018-08-22 14:47:32 +02003058 // Test double stack.
3059 {
3060 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
3061 VkDeviceSize prevOffsetLower = 0;
3062 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
3063 for(size_t i = 0; i < maxBufCount; ++i)
3064 {
3065 const bool upperAddress = (i % 2) != 0;
3066 if(upperAddress)
3067 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3068 else
3069 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003070 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003071 BufferInfo newBufInfo;
3072 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3073 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003074 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02003075 if(upperAddress)
3076 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003077 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003078 prevOffsetUpper = allocInfo.offset;
3079 }
3080 else
3081 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003082 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02003083 prevOffsetLower = allocInfo.offset;
3084 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003085 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003086 bufInfo.push_back(newBufInfo);
3087 }
3088
3089 // Destroy few buffers from top of the stack.
3090 for(size_t i = 0; i < maxBufCount / 5; ++i)
3091 {
3092 const BufferInfo& currBufInfo = bufInfo.back();
3093 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3094 bufInfo.pop_back();
3095 }
3096
3097 // Create some more
3098 for(size_t i = 0; i < maxBufCount / 5; ++i)
3099 {
3100 const bool upperAddress = (i % 2) != 0;
3101 if(upperAddress)
3102 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3103 else
3104 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003105 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003106 BufferInfo newBufInfo;
3107 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3108 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003109 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02003110 bufInfo.push_back(newBufInfo);
3111 }
3112
3113 // Destroy the buffers in reverse order.
3114 while(!bufInfo.empty())
3115 {
3116 const BufferInfo& currBufInfo = bufInfo.back();
3117 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3118 bufInfo.pop_back();
3119 }
3120
3121 // Create buffers on both sides until we reach out of memory.
3122 prevOffsetLower = 0;
3123 prevOffsetUpper = poolCreateInfo.blockSize;
3124 res = VK_SUCCESS;
3125 for(size_t i = 0; res == VK_SUCCESS; ++i)
3126 {
3127 const bool upperAddress = (i % 2) != 0;
3128 if(upperAddress)
3129 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3130 else
3131 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003132 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003133 BufferInfo newBufInfo;
3134 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3135 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3136 if(res == VK_SUCCESS)
3137 {
3138 if(upperAddress)
3139 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003140 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003141 prevOffsetUpper = allocInfo.offset;
3142 }
3143 else
3144 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003145 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02003146 prevOffsetLower = allocInfo.offset;
3147 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003148 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003149 bufInfo.push_back(newBufInfo);
3150 }
3151 }
3152
3153 // Destroy the buffers in random order.
3154 while(!bufInfo.empty())
3155 {
3156 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3157 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3158 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3159 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3160 }
3161
3162 // Create buffers on upper side only, constant size, until we reach out of memory.
3163 prevOffsetUpper = poolCreateInfo.blockSize;
3164 res = VK_SUCCESS;
3165 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3166 bufCreateInfo.size = bufSizeMax;
3167 for(size_t i = 0; res == VK_SUCCESS; ++i)
3168 {
3169 BufferInfo newBufInfo;
3170 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3171 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3172 if(res == VK_SUCCESS)
3173 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003174 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003175 prevOffsetUpper = allocInfo.offset;
3176 bufInfo.push_back(newBufInfo);
3177 }
3178 }
3179
3180 // Destroy the buffers in reverse order.
3181 while(!bufInfo.empty())
3182 {
3183 const BufferInfo& currBufInfo = bufInfo.back();
3184 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3185 bufInfo.pop_back();
3186 }
3187 }
3188
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003189 // Test ring buffer with lost allocations.
3190 {
3191 // Allocate number of buffers until pool is full.
3192 // Notice CAN_BECOME_LOST flag and call to vmaSetCurrentFrameIndex.
3193 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT;
3194 res = VK_SUCCESS;
3195 for(size_t i = 0; res == VK_SUCCESS; ++i)
3196 {
3197 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3198
Adam Sawickifd366b62019-01-24 15:26:43 +01003199 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003200
3201 BufferInfo newBufInfo;
3202 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3203 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3204 if(res == VK_SUCCESS)
3205 bufInfo.push_back(newBufInfo);
3206 }
3207
3208 // Free first half of it.
3209 {
3210 const size_t buffersToDelete = bufInfo.size() / 2;
3211 for(size_t i = 0; i < buffersToDelete; ++i)
3212 {
3213 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3214 }
3215 bufInfo.erase(bufInfo.begin(), bufInfo.begin() + buffersToDelete);
3216 }
3217
3218 // Allocate number of buffers until pool is full again.
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003219 // This way we make sure ring buffers wraps around, front in in the middle.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003220 res = VK_SUCCESS;
3221 for(size_t i = 0; res == VK_SUCCESS; ++i)
3222 {
3223 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3224
Adam Sawickifd366b62019-01-24 15:26:43 +01003225 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003226
3227 BufferInfo newBufInfo;
3228 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3229 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3230 if(res == VK_SUCCESS)
3231 bufInfo.push_back(newBufInfo);
3232 }
3233
3234 VkDeviceSize firstNewOffset;
3235 {
3236 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3237
3238 // Allocate a large buffer with CAN_MAKE_OTHER_LOST.
3239 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
3240 bufCreateInfo.size = bufSizeMax;
3241
3242 BufferInfo newBufInfo;
3243 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3244 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003245 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003246 bufInfo.push_back(newBufInfo);
3247 firstNewOffset = allocInfo.offset;
3248
3249 // Make sure at least one buffer from the beginning became lost.
3250 vmaGetAllocationInfo(g_hAllocator, bufInfo[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003251 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003252 }
3253
Adam Sawickifd366b62019-01-24 15:26:43 +01003254#if 0 // TODO Fix and uncomment. Failing on Intel.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003255 // Allocate more buffers that CAN_MAKE_OTHER_LOST until we wrap-around with this.
3256 size_t newCount = 1;
3257 for(;;)
3258 {
3259 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3260
Adam Sawickifd366b62019-01-24 15:26:43 +01003261 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003262
3263 BufferInfo newBufInfo;
3264 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3265 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickifd366b62019-01-24 15:26:43 +01003266
Adam Sawickib8d34d52018-10-03 17:41:20 +02003267 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003268 bufInfo.push_back(newBufInfo);
3269 ++newCount;
3270 if(allocInfo.offset < firstNewOffset)
3271 break;
3272 }
Adam Sawickifd366b62019-01-24 15:26:43 +01003273#endif
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003274
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003275 // Delete buffers that are lost.
3276 for(size_t i = bufInfo.size(); i--; )
3277 {
3278 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
3279 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3280 {
3281 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3282 bufInfo.erase(bufInfo.begin() + i);
3283 }
3284 }
3285
3286 // Test vmaMakePoolAllocationsLost
3287 {
3288 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3289
Adam Sawicki4d35a5d2019-01-24 15:51:59 +01003290 size_t lostAllocCount = 0;
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003291 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostAllocCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003292 TEST(lostAllocCount > 0);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003293
3294 size_t realLostAllocCount = 0;
3295 for(size_t i = 0; i < bufInfo.size(); ++i)
3296 {
3297 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
3298 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3299 ++realLostAllocCount;
3300 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003301 TEST(realLostAllocCount == lostAllocCount);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003302 }
3303
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003304 // Destroy all the buffers in forward order.
3305 for(size_t i = 0; i < bufInfo.size(); ++i)
3306 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3307 bufInfo.clear();
3308 }
3309
Adam Sawicki70a683e2018-08-24 15:36:32 +02003310 vmaDestroyPool(g_hAllocator, pool);
3311}
Adam Sawickif799c4f2018-08-23 10:40:30 +02003312
Adam Sawicki70a683e2018-08-24 15:36:32 +02003313static void TestLinearAllocatorMultiBlock()
3314{
3315 wprintf(L"Test linear allocator multi block\n");
3316
3317 RandomNumberGenerator rand{345673};
3318
3319 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3320 sampleBufCreateInfo.size = 1024 * 1024;
3321 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3322
3323 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3324 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3325
3326 VmaPoolCreateInfo poolCreateInfo = {};
3327 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3328 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003329 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003330
3331 VmaPool pool = nullptr;
3332 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003333 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003334
3335 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
3336
3337 VmaAllocationCreateInfo allocCreateInfo = {};
3338 allocCreateInfo.pool = pool;
3339
3340 std::vector<BufferInfo> bufInfo;
3341 VmaAllocationInfo allocInfo;
3342
3343 // Test one-time free.
3344 {
3345 // Allocate buffers until we move to a second block.
3346 VkDeviceMemory lastMem = VK_NULL_HANDLE;
3347 for(uint32_t i = 0; ; ++i)
3348 {
3349 BufferInfo newBufInfo;
3350 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3351 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003352 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003353 bufInfo.push_back(newBufInfo);
3354 if(lastMem && allocInfo.deviceMemory != lastMem)
3355 {
3356 break;
3357 }
3358 lastMem = allocInfo.deviceMemory;
3359 }
3360
Adam Sawickib8d34d52018-10-03 17:41:20 +02003361 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003362
3363 // Make sure that pool has now two blocks.
3364 VmaPoolStats poolStats = {};
3365 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003366 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003367
3368 // Destroy all the buffers in random order.
3369 while(!bufInfo.empty())
3370 {
3371 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3372 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3373 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3374 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3375 }
3376
3377 // Make sure that pool has now at most one block.
3378 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003379 TEST(poolStats.blockCount <= 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003380 }
3381
3382 // Test stack.
3383 {
3384 // Allocate buffers until we move to a second block.
3385 VkDeviceMemory lastMem = VK_NULL_HANDLE;
3386 for(uint32_t i = 0; ; ++i)
3387 {
3388 BufferInfo newBufInfo;
3389 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3390 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003391 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003392 bufInfo.push_back(newBufInfo);
3393 if(lastMem && allocInfo.deviceMemory != lastMem)
3394 {
3395 break;
3396 }
3397 lastMem = allocInfo.deviceMemory;
3398 }
3399
Adam Sawickib8d34d52018-10-03 17:41:20 +02003400 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003401
3402 // Add few more buffers.
3403 for(uint32_t i = 0; i < 5; ++i)
3404 {
3405 BufferInfo newBufInfo;
3406 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3407 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003408 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003409 bufInfo.push_back(newBufInfo);
3410 }
3411
3412 // Make sure that pool has now two blocks.
3413 VmaPoolStats poolStats = {};
3414 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003415 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003416
3417 // Delete half of buffers, LIFO.
3418 for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
3419 {
3420 const BufferInfo& currBufInfo = bufInfo.back();
3421 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3422 bufInfo.pop_back();
3423 }
3424
3425 // Add one more buffer.
3426 BufferInfo newBufInfo;
3427 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3428 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003429 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003430 bufInfo.push_back(newBufInfo);
3431
3432 // Make sure that pool has now one block.
3433 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003434 TEST(poolStats.blockCount == 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003435
3436 // Delete all the remaining buffers, LIFO.
3437 while(!bufInfo.empty())
3438 {
3439 const BufferInfo& currBufInfo = bufInfo.back();
3440 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3441 bufInfo.pop_back();
3442 }
Adam Sawickif799c4f2018-08-23 10:40:30 +02003443 }
3444
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003445 vmaDestroyPool(g_hAllocator, pool);
3446}
3447
Adam Sawickifd11d752018-08-22 15:02:10 +02003448static void ManuallyTestLinearAllocator()
3449{
3450 VmaStats origStats;
3451 vmaCalculateStats(g_hAllocator, &origStats);
3452
3453 wprintf(L"Manually test linear allocator\n");
3454
3455 RandomNumberGenerator rand{645332};
3456
3457 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3458 sampleBufCreateInfo.size = 1024; // Whatever.
3459 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3460
3461 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3462 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3463
3464 VmaPoolCreateInfo poolCreateInfo = {};
3465 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003466 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003467
3468 poolCreateInfo.blockSize = 10 * 1024;
3469 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3470 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
3471
3472 VmaPool pool = nullptr;
3473 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003474 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003475
3476 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
3477
3478 VmaAllocationCreateInfo allocCreateInfo = {};
3479 allocCreateInfo.pool = pool;
3480
3481 std::vector<BufferInfo> bufInfo;
3482 VmaAllocationInfo allocInfo;
3483 BufferInfo newBufInfo;
3484
3485 // Test double stack.
3486 {
3487 /*
3488 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
3489 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
3490
3491 Totally:
3492 1 block allocated
3493 10240 Vulkan bytes
3494 6 new allocations
3495 2256 bytes in allocations
3496 */
3497
3498 bufCreateInfo.size = 32;
3499 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3500 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003501 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003502 bufInfo.push_back(newBufInfo);
3503
3504 bufCreateInfo.size = 1024;
3505 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3506 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003507 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003508 bufInfo.push_back(newBufInfo);
3509
3510 bufCreateInfo.size = 32;
3511 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3512 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003513 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003514 bufInfo.push_back(newBufInfo);
3515
3516 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3517
3518 bufCreateInfo.size = 128;
3519 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3520 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003521 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003522 bufInfo.push_back(newBufInfo);
3523
3524 bufCreateInfo.size = 1024;
3525 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3526 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003527 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003528 bufInfo.push_back(newBufInfo);
3529
3530 bufCreateInfo.size = 16;
3531 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3532 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003533 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003534 bufInfo.push_back(newBufInfo);
3535
3536 VmaStats currStats;
3537 vmaCalculateStats(g_hAllocator, &currStats);
3538 VmaPoolStats poolStats;
3539 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
3540
3541 char* statsStr = nullptr;
3542 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
3543
3544 // PUT BREAKPOINT HERE TO CHECK.
3545 // Inspect: currStats versus origStats, poolStats, statsStr.
3546 int I = 0;
3547
3548 vmaFreeStatsString(g_hAllocator, statsStr);
3549
3550 // Destroy the buffers in reverse order.
3551 while(!bufInfo.empty())
3552 {
3553 const BufferInfo& currBufInfo = bufInfo.back();
3554 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3555 bufInfo.pop_back();
3556 }
3557 }
3558
3559 vmaDestroyPool(g_hAllocator, pool);
3560}
3561
Adam Sawicki80927152018-09-07 17:27:23 +02003562static void BenchmarkAlgorithmsCase(FILE* file,
3563 uint32_t algorithm,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003564 bool empty,
3565 VmaAllocationCreateFlags allocStrategy,
3566 FREE_ORDER freeOrder)
Adam Sawicki0a607132018-08-24 11:18:41 +02003567{
3568 RandomNumberGenerator rand{16223};
3569
3570 const VkDeviceSize bufSizeMin = 32;
3571 const VkDeviceSize bufSizeMax = 1024;
3572 const size_t maxBufCapacity = 10000;
3573 const uint32_t iterationCount = 10;
3574
3575 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3576 sampleBufCreateInfo.size = bufSizeMax;
3577 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3578
3579 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3580 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3581
3582 VmaPoolCreateInfo poolCreateInfo = {};
3583 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003584 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003585
3586 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
Adam Sawicki80927152018-09-07 17:27:23 +02003587 poolCreateInfo.flags |= algorithm;
Adam Sawicki0a607132018-08-24 11:18:41 +02003588 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
3589
3590 VmaPool pool = nullptr;
3591 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003592 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003593
3594 // Buffer created just to get memory requirements. Never bound to any memory.
3595 VkBuffer dummyBuffer = VK_NULL_HANDLE;
Adam Sawicki1f84f622019-07-02 13:40:01 +02003596 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003597 TEST(res == VK_SUCCESS && dummyBuffer);
Adam Sawicki0a607132018-08-24 11:18:41 +02003598
3599 VkMemoryRequirements memReq = {};
3600 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3601
Adam Sawicki1f84f622019-07-02 13:40:01 +02003602 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawicki0a607132018-08-24 11:18:41 +02003603
3604 VmaAllocationCreateInfo allocCreateInfo = {};
3605 allocCreateInfo.pool = pool;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003606 allocCreateInfo.flags = allocStrategy;
Adam Sawicki0a607132018-08-24 11:18:41 +02003607
3608 VmaAllocation alloc;
3609 std::vector<VmaAllocation> baseAllocations;
3610
3611 if(!empty)
3612 {
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003613 // Make allocations up to 1/3 of pool size.
Adam Sawicki0a607132018-08-24 11:18:41 +02003614 VkDeviceSize totalSize = 0;
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003615 while(totalSize < poolCreateInfo.blockSize / 3)
Adam Sawicki0a607132018-08-24 11:18:41 +02003616 {
Adam Sawicki4d844e22019-01-24 16:21:05 +01003617 // This test intentionally allows sizes that are aligned to 4 or 16 bytes.
3618 // This is theoretically allowed and already uncovered one bug.
Adam Sawicki0a607132018-08-24 11:18:41 +02003619 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
3620 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003621 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003622 baseAllocations.push_back(alloc);
3623 totalSize += memReq.size;
3624 }
3625
3626 // Delete half of them, choose randomly.
3627 size_t allocsToDelete = baseAllocations.size() / 2;
3628 for(size_t i = 0; i < allocsToDelete; ++i)
3629 {
3630 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
3631 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
3632 baseAllocations.erase(baseAllocations.begin() + index);
3633 }
3634 }
3635
3636 // BENCHMARK
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003637 const size_t allocCount = maxBufCapacity / 3;
Adam Sawicki0a607132018-08-24 11:18:41 +02003638 std::vector<VmaAllocation> testAllocations;
3639 testAllocations.reserve(allocCount);
3640 duration allocTotalDuration = duration::zero();
3641 duration freeTotalDuration = duration::zero();
3642 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
3643 {
3644 // Allocations
3645 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
3646 for(size_t i = 0; i < allocCount; ++i)
3647 {
3648 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
3649 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003650 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003651 testAllocations.push_back(alloc);
3652 }
3653 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
3654
3655 // Deallocations
3656 switch(freeOrder)
3657 {
3658 case FREE_ORDER::FORWARD:
3659 // Leave testAllocations unchanged.
3660 break;
3661 case FREE_ORDER::BACKWARD:
3662 std::reverse(testAllocations.begin(), testAllocations.end());
3663 break;
3664 case FREE_ORDER::RANDOM:
3665 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
3666 break;
3667 default: assert(0);
3668 }
3669
3670 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
3671 for(size_t i = 0; i < allocCount; ++i)
3672 vmaFreeMemory(g_hAllocator, testAllocations[i]);
3673 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
3674
3675 testAllocations.clear();
3676 }
3677
3678 // Delete baseAllocations
3679 while(!baseAllocations.empty())
3680 {
3681 vmaFreeMemory(g_hAllocator, baseAllocations.back());
3682 baseAllocations.pop_back();
3683 }
3684
3685 vmaDestroyPool(g_hAllocator, pool);
3686
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003687 const float allocTotalSeconds = ToFloatSeconds(allocTotalDuration);
3688 const float freeTotalSeconds = ToFloatSeconds(freeTotalDuration);
3689
Adam Sawicki80927152018-09-07 17:27:23 +02003690 printf(" Algorithm=%s %s Allocation=%s FreeOrder=%s: allocations %g s, free %g s\n",
3691 AlgorithmToStr(algorithm),
Adam Sawicki0667e332018-08-24 17:26:44 +02003692 empty ? "Empty" : "Not empty",
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003693 GetAllocationStrategyName(allocStrategy),
Adam Sawicki0a607132018-08-24 11:18:41 +02003694 FREE_ORDER_NAMES[(size_t)freeOrder],
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003695 allocTotalSeconds,
3696 freeTotalSeconds);
3697
3698 if(file)
3699 {
3700 std::string currTime;
3701 CurrentTimeToStr(currTime);
3702
Adam Sawicki80927152018-09-07 17:27:23 +02003703 fprintf(file, "%s,%s,%s,%u,%s,%s,%g,%g\n",
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003704 CODE_DESCRIPTION, currTime.c_str(),
Adam Sawicki80927152018-09-07 17:27:23 +02003705 AlgorithmToStr(algorithm),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003706 empty ? 1 : 0,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003707 GetAllocationStrategyName(allocStrategy),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003708 FREE_ORDER_NAMES[(uint32_t)freeOrder],
3709 allocTotalSeconds,
3710 freeTotalSeconds);
3711 }
Adam Sawicki0a607132018-08-24 11:18:41 +02003712}
3713
Adam Sawicki80927152018-09-07 17:27:23 +02003714static void BenchmarkAlgorithms(FILE* file)
Adam Sawicki0a607132018-08-24 11:18:41 +02003715{
Adam Sawicki80927152018-09-07 17:27:23 +02003716 wprintf(L"Benchmark algorithms\n");
Adam Sawicki0a607132018-08-24 11:18:41 +02003717
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003718 if(file)
3719 {
3720 fprintf(file,
3721 "Code,Time,"
Adam Sawicki80927152018-09-07 17:27:23 +02003722 "Algorithm,Empty,Allocation strategy,Free order,"
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003723 "Allocation time (s),Deallocation time (s)\n");
3724 }
3725
Adam Sawicki0a607132018-08-24 11:18:41 +02003726 uint32_t freeOrderCount = 1;
3727 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
3728 freeOrderCount = 3;
3729 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
3730 freeOrderCount = 2;
3731
3732 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003733 const uint32_t allocStrategyCount = GetAllocationStrategyCount();
Adam Sawicki0a607132018-08-24 11:18:41 +02003734
3735 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
3736 {
3737 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
3738 switch(freeOrderIndex)
3739 {
3740 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
3741 case 1: freeOrder = FREE_ORDER::FORWARD; break;
3742 case 2: freeOrder = FREE_ORDER::RANDOM; break;
3743 default: assert(0);
3744 }
3745
3746 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
3747 {
Adam Sawicki80927152018-09-07 17:27:23 +02003748 for(uint32_t algorithmIndex = 0; algorithmIndex < 3; ++algorithmIndex)
Adam Sawicki0a607132018-08-24 11:18:41 +02003749 {
Adam Sawicki80927152018-09-07 17:27:23 +02003750 uint32_t algorithm = 0;
3751 switch(algorithmIndex)
3752 {
3753 case 0:
3754 break;
3755 case 1:
3756 algorithm = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
3757 break;
3758 case 2:
3759 algorithm = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3760 break;
3761 default:
3762 assert(0);
3763 }
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003764
Adam Sawicki80927152018-09-07 17:27:23 +02003765 uint32_t currAllocStrategyCount = algorithm != 0 ? 1 : allocStrategyCount;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003766 for(uint32_t allocStrategyIndex = 0; allocStrategyIndex < currAllocStrategyCount; ++allocStrategyIndex)
3767 {
3768 VmaAllocatorCreateFlags strategy = 0;
Adam Sawicki80927152018-09-07 17:27:23 +02003769 if(currAllocStrategyCount > 1)
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003770 {
3771 switch(allocStrategyIndex)
3772 {
3773 case 0: strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; break;
3774 case 1: strategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT; break;
3775 case 2: strategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT; break;
3776 default: assert(0);
3777 }
3778 }
3779
Adam Sawicki80927152018-09-07 17:27:23 +02003780 BenchmarkAlgorithmsCase(
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003781 file,
Adam Sawicki80927152018-09-07 17:27:23 +02003782 algorithm,
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003783 (emptyIndex == 0), // empty
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003784 strategy,
3785 freeOrder); // freeOrder
3786 }
Adam Sawicki0a607132018-08-24 11:18:41 +02003787 }
3788 }
3789 }
3790}
3791
Adam Sawickib8333fb2018-03-13 16:15:53 +01003792static void TestPool_SameSize()
3793{
3794 const VkDeviceSize BUF_SIZE = 1024 * 1024;
3795 const size_t BUF_COUNT = 100;
3796 VkResult res;
3797
3798 RandomNumberGenerator rand{123};
3799
3800 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3801 bufferInfo.size = BUF_SIZE;
3802 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3803
3804 uint32_t memoryTypeBits = UINT32_MAX;
3805 {
3806 VkBuffer dummyBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +02003807 res = vkCreateBuffer(g_hDevice, &bufferInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003808 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003809
3810 VkMemoryRequirements memReq;
3811 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3812 memoryTypeBits = memReq.memoryTypeBits;
3813
Adam Sawicki1f84f622019-07-02 13:40:01 +02003814 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003815 }
3816
3817 VmaAllocationCreateInfo poolAllocInfo = {};
3818 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3819 uint32_t memTypeIndex;
3820 res = vmaFindMemoryTypeIndex(
3821 g_hAllocator,
3822 memoryTypeBits,
3823 &poolAllocInfo,
3824 &memTypeIndex);
3825
3826 VmaPoolCreateInfo poolCreateInfo = {};
3827 poolCreateInfo.memoryTypeIndex = memTypeIndex;
3828 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
3829 poolCreateInfo.minBlockCount = 1;
3830 poolCreateInfo.maxBlockCount = 4;
3831 poolCreateInfo.frameInUseCount = 0;
3832
3833 VmaPool pool;
3834 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003835 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003836
Adam Sawickia020fb82019-11-02 14:43:06 +01003837 // Test pool name
3838 {
3839 static const char* const POOL_NAME = "Pool name";
3840 vmaSetPoolName(g_hAllocator, pool, POOL_NAME);
3841
3842 const char* fetchedPoolName = nullptr;
3843 vmaGetPoolName(g_hAllocator, pool, &fetchedPoolName);
3844 TEST(strcmp(fetchedPoolName, POOL_NAME) == 0);
3845
Adam Sawickia020fb82019-11-02 14:43:06 +01003846 vmaSetPoolName(g_hAllocator, pool, nullptr);
3847 }
3848
Adam Sawickib8333fb2018-03-13 16:15:53 +01003849 vmaSetCurrentFrameIndex(g_hAllocator, 1);
3850
3851 VmaAllocationCreateInfo allocInfo = {};
3852 allocInfo.pool = pool;
3853 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
3854 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
3855
3856 struct BufItem
3857 {
3858 VkBuffer Buf;
3859 VmaAllocation Alloc;
3860 };
3861 std::vector<BufItem> items;
3862
3863 // Fill entire pool.
3864 for(size_t i = 0; i < BUF_COUNT; ++i)
3865 {
3866 BufItem item;
3867 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003868 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003869 items.push_back(item);
3870 }
3871
3872 // Make sure that another allocation would fail.
3873 {
3874 BufItem item;
3875 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003876 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003877 }
3878
3879 // Validate that no buffer is lost. Also check that they are not mapped.
3880 for(size_t i = 0; i < items.size(); ++i)
3881 {
3882 VmaAllocationInfo allocInfo;
3883 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003884 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
3885 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003886 }
3887
3888 // Free some percent of random items.
3889 {
3890 const size_t PERCENT_TO_FREE = 10;
3891 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
3892 for(size_t i = 0; i < itemsToFree; ++i)
3893 {
3894 size_t index = (size_t)rand.Generate() % items.size();
3895 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
3896 items.erase(items.begin() + index);
3897 }
3898 }
3899
3900 // Randomly allocate and free items.
3901 {
3902 const size_t OPERATION_COUNT = BUF_COUNT;
3903 for(size_t i = 0; i < OPERATION_COUNT; ++i)
3904 {
3905 bool allocate = rand.Generate() % 2 != 0;
3906 if(allocate)
3907 {
3908 if(items.size() < BUF_COUNT)
3909 {
3910 BufItem item;
3911 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003912 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003913 items.push_back(item);
3914 }
3915 }
3916 else // Free
3917 {
3918 if(!items.empty())
3919 {
3920 size_t index = (size_t)rand.Generate() % items.size();
3921 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
3922 items.erase(items.begin() + index);
3923 }
3924 }
3925 }
3926 }
3927
3928 // Allocate up to maximum.
3929 while(items.size() < BUF_COUNT)
3930 {
3931 BufItem item;
3932 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003933 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003934 items.push_back(item);
3935 }
3936
3937 // Validate that no buffer is lost.
3938 for(size_t i = 0; i < items.size(); ++i)
3939 {
3940 VmaAllocationInfo allocInfo;
3941 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003942 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003943 }
3944
3945 // Next frame.
3946 vmaSetCurrentFrameIndex(g_hAllocator, 2);
3947
3948 // Allocate another BUF_COUNT buffers.
3949 for(size_t i = 0; i < BUF_COUNT; ++i)
3950 {
3951 BufItem item;
3952 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003953 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003954 items.push_back(item);
3955 }
3956
3957 // Make sure the first BUF_COUNT is lost. Delete them.
3958 for(size_t i = 0; i < BUF_COUNT; ++i)
3959 {
3960 VmaAllocationInfo allocInfo;
3961 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003962 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003963 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
3964 }
3965 items.erase(items.begin(), items.begin() + BUF_COUNT);
3966
3967 // Validate that no buffer is lost.
3968 for(size_t i = 0; i < items.size(); ++i)
3969 {
3970 VmaAllocationInfo allocInfo;
3971 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003972 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003973 }
3974
3975 // Free one item.
3976 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
3977 items.pop_back();
3978
3979 // Validate statistics.
3980 {
3981 VmaPoolStats poolStats = {};
3982 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003983 TEST(poolStats.allocationCount == items.size());
3984 TEST(poolStats.size = BUF_COUNT * BUF_SIZE);
3985 TEST(poolStats.unusedRangeCount == 1);
3986 TEST(poolStats.unusedRangeSizeMax == BUF_SIZE);
3987 TEST(poolStats.unusedSize == BUF_SIZE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003988 }
3989
3990 // Free all remaining items.
3991 for(size_t i = items.size(); i--; )
3992 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
3993 items.clear();
3994
3995 // Allocate maximum items again.
3996 for(size_t i = 0; i < BUF_COUNT; ++i)
3997 {
3998 BufItem item;
3999 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004000 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004001 items.push_back(item);
4002 }
4003
4004 // Delete every other item.
4005 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
4006 {
4007 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4008 items.erase(items.begin() + i);
4009 }
4010
4011 // Defragment!
4012 {
4013 std::vector<VmaAllocation> allocationsToDefragment(items.size());
4014 for(size_t i = 0; i < items.size(); ++i)
4015 allocationsToDefragment[i] = items[i].Alloc;
4016
4017 VmaDefragmentationStats defragmentationStats;
4018 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004019 TEST(res == VK_SUCCESS);
4020 TEST(defragmentationStats.deviceMemoryBlocksFreed == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004021 }
4022
4023 // Free all remaining items.
4024 for(size_t i = items.size(); i--; )
4025 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4026 items.clear();
4027
4028 ////////////////////////////////////////////////////////////////////////////////
4029 // Test for vmaMakePoolAllocationsLost
4030
4031 // Allocate 4 buffers on frame 10.
4032 vmaSetCurrentFrameIndex(g_hAllocator, 10);
4033 for(size_t i = 0; i < 4; ++i)
4034 {
4035 BufItem item;
4036 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004037 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004038 items.push_back(item);
4039 }
4040
4041 // Touch first 2 of them on frame 11.
4042 vmaSetCurrentFrameIndex(g_hAllocator, 11);
4043 for(size_t i = 0; i < 2; ++i)
4044 {
4045 VmaAllocationInfo allocInfo;
4046 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
4047 }
4048
4049 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
4050 size_t lostCount = 0xDEADC0DE;
4051 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004052 TEST(lostCount == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004053
4054 // Make another call. Now 0 should be lost.
4055 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004056 TEST(lostCount == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004057
4058 // Make another call, with null count. Should not crash.
4059 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
4060
4061 // END: Free all remaining items.
4062 for(size_t i = items.size(); i--; )
4063 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4064
4065 items.clear();
4066
Adam Sawickid2924172018-06-11 12:48:46 +02004067 ////////////////////////////////////////////////////////////////////////////////
4068 // Test for allocation too large for pool
4069
4070 {
4071 VmaAllocationCreateInfo allocCreateInfo = {};
4072 allocCreateInfo.pool = pool;
4073
4074 VkMemoryRequirements memReq;
4075 memReq.memoryTypeBits = UINT32_MAX;
4076 memReq.alignment = 1;
4077 memReq.size = poolCreateInfo.blockSize + 4;
4078
4079 VmaAllocation alloc = nullptr;
4080 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004081 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
Adam Sawickid2924172018-06-11 12:48:46 +02004082 }
4083
Adam Sawickib8333fb2018-03-13 16:15:53 +01004084 vmaDestroyPool(g_hAllocator, pool);
4085}
4086
Adam Sawickie44c6262018-06-15 14:30:39 +02004087static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
4088{
4089 const uint8_t* pBytes = (const uint8_t*)pMemory;
4090 for(size_t i = 0; i < size; ++i)
4091 {
4092 if(pBytes[i] != pattern)
4093 {
4094 return false;
4095 }
4096 }
4097 return true;
4098}
4099
4100static void TestAllocationsInitialization()
4101{
4102 VkResult res;
4103
4104 const size_t BUF_SIZE = 1024;
4105
4106 // Create pool.
4107
4108 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4109 bufInfo.size = BUF_SIZE;
4110 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4111
4112 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
4113 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
4114
4115 VmaPoolCreateInfo poolCreateInfo = {};
4116 poolCreateInfo.blockSize = BUF_SIZE * 10;
4117 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
4118 poolCreateInfo.maxBlockCount = 1;
4119 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004120 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004121
4122 VmaAllocationCreateInfo bufAllocCreateInfo = {};
4123 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004124 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004125
4126 // Create one persistently mapped buffer to keep memory of this block mapped,
4127 // so that pointer to mapped data will remain (more or less...) valid even
4128 // after destruction of other allocations.
4129
4130 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
4131 VkBuffer firstBuf;
4132 VmaAllocation firstAlloc;
4133 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004134 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004135
4136 // Test buffers.
4137
4138 for(uint32_t i = 0; i < 2; ++i)
4139 {
4140 const bool persistentlyMapped = i == 0;
4141 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
4142 VkBuffer buf;
4143 VmaAllocation alloc;
4144 VmaAllocationInfo allocInfo;
4145 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004146 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004147
4148 void* pMappedData;
4149 if(!persistentlyMapped)
4150 {
4151 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004152 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004153 }
4154 else
4155 {
4156 pMappedData = allocInfo.pMappedData;
4157 }
4158
4159 // Validate initialized content
4160 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004161 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02004162
4163 if(!persistentlyMapped)
4164 {
4165 vmaUnmapMemory(g_hAllocator, alloc);
4166 }
4167
4168 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4169
4170 // Validate freed content
4171 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004172 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02004173 }
4174
4175 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
4176 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
4177}
4178
Adam Sawickib8333fb2018-03-13 16:15:53 +01004179static void TestPool_Benchmark(
4180 PoolTestResult& outResult,
4181 const PoolTestConfig& config)
4182{
Adam Sawickib8d34d52018-10-03 17:41:20 +02004183 TEST(config.ThreadCount > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004184
4185 RandomNumberGenerator mainRand{config.RandSeed};
4186
4187 uint32_t allocationSizeProbabilitySum = std::accumulate(
4188 config.AllocationSizes.begin(),
4189 config.AllocationSizes.end(),
4190 0u,
4191 [](uint32_t sum, const AllocationSize& allocSize) {
4192 return sum + allocSize.Probability;
4193 });
4194
4195 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4196 bufferInfo.size = 256; // Whatever.
4197 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4198
4199 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4200 imageInfo.imageType = VK_IMAGE_TYPE_2D;
4201 imageInfo.extent.width = 256; // Whatever.
4202 imageInfo.extent.height = 256; // Whatever.
4203 imageInfo.extent.depth = 1;
4204 imageInfo.mipLevels = 1;
4205 imageInfo.arrayLayers = 1;
4206 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4207 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
4208 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
4209 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
4210 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4211
4212 uint32_t bufferMemoryTypeBits = UINT32_MAX;
4213 {
4214 VkBuffer dummyBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +02004215 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004216 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004217
4218 VkMemoryRequirements memReq;
4219 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
4220 bufferMemoryTypeBits = memReq.memoryTypeBits;
4221
Adam Sawicki1f84f622019-07-02 13:40:01 +02004222 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004223 }
4224
4225 uint32_t imageMemoryTypeBits = UINT32_MAX;
4226 {
4227 VkImage dummyImage;
Adam Sawicki1f84f622019-07-02 13:40:01 +02004228 VkResult res = vkCreateImage(g_hDevice, &imageInfo, g_Allocs, &dummyImage);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004229 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004230
4231 VkMemoryRequirements memReq;
4232 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
4233 imageMemoryTypeBits = memReq.memoryTypeBits;
4234
Adam Sawicki1f84f622019-07-02 13:40:01 +02004235 vkDestroyImage(g_hDevice, dummyImage, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004236 }
4237
4238 uint32_t memoryTypeBits = 0;
4239 if(config.UsesBuffers() && config.UsesImages())
4240 {
4241 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
4242 if(memoryTypeBits == 0)
4243 {
4244 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
4245 return;
4246 }
4247 }
4248 else if(config.UsesBuffers())
4249 memoryTypeBits = bufferMemoryTypeBits;
4250 else if(config.UsesImages())
4251 memoryTypeBits = imageMemoryTypeBits;
4252 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02004253 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004254
4255 VmaPoolCreateInfo poolCreateInfo = {};
4256 poolCreateInfo.memoryTypeIndex = 0;
4257 poolCreateInfo.minBlockCount = 1;
4258 poolCreateInfo.maxBlockCount = 1;
4259 poolCreateInfo.blockSize = config.PoolSize;
4260 poolCreateInfo.frameInUseCount = 1;
4261
4262 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
4263 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4264 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
4265
4266 VmaPool pool;
4267 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004268 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004269
4270 // Start time measurement - after creating pool and initializing data structures.
4271 time_point timeBeg = std::chrono::high_resolution_clock::now();
4272
4273 ////////////////////////////////////////////////////////////////////////////////
4274 // ThreadProc
4275 auto ThreadProc = [&](
4276 PoolTestThreadResult* outThreadResult,
4277 uint32_t randSeed,
4278 HANDLE frameStartEvent,
4279 HANDLE frameEndEvent) -> void
4280 {
4281 RandomNumberGenerator threadRand{randSeed};
4282
4283 outThreadResult->AllocationTimeMin = duration::max();
4284 outThreadResult->AllocationTimeSum = duration::zero();
4285 outThreadResult->AllocationTimeMax = duration::min();
4286 outThreadResult->DeallocationTimeMin = duration::max();
4287 outThreadResult->DeallocationTimeSum = duration::zero();
4288 outThreadResult->DeallocationTimeMax = duration::min();
4289 outThreadResult->AllocationCount = 0;
4290 outThreadResult->DeallocationCount = 0;
4291 outThreadResult->LostAllocationCount = 0;
4292 outThreadResult->LostAllocationTotalSize = 0;
4293 outThreadResult->FailedAllocationCount = 0;
4294 outThreadResult->FailedAllocationTotalSize = 0;
4295
4296 struct Item
4297 {
4298 VkDeviceSize BufferSize;
4299 VkExtent2D ImageSize;
4300 VkBuffer Buf;
4301 VkImage Image;
4302 VmaAllocation Alloc;
4303
4304 VkDeviceSize CalcSizeBytes() const
4305 {
4306 return BufferSize +
4307 ImageSize.width * ImageSize.height * 4;
4308 }
4309 };
4310 std::vector<Item> unusedItems, usedItems;
4311
4312 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
4313
4314 // Create all items - all unused, not yet allocated.
4315 for(size_t i = 0; i < threadTotalItemCount; ++i)
4316 {
4317 Item item = {};
4318
4319 uint32_t allocSizeIndex = 0;
4320 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
4321 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
4322 r -= config.AllocationSizes[allocSizeIndex++].Probability;
4323
4324 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
4325 if(allocSize.BufferSizeMax > 0)
4326 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004327 TEST(allocSize.BufferSizeMin > 0);
4328 TEST(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004329 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
4330 item.BufferSize = allocSize.BufferSizeMin;
4331 else
4332 {
4333 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
4334 item.BufferSize = item.BufferSize / 16 * 16;
4335 }
4336 }
4337 else
4338 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004339 TEST(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004340 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
4341 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
4342 else
4343 {
4344 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
4345 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
4346 }
4347 }
4348
4349 unusedItems.push_back(item);
4350 }
4351
4352 auto Allocate = [&](Item& item) -> VkResult
4353 {
4354 VmaAllocationCreateInfo allocCreateInfo = {};
4355 allocCreateInfo.pool = pool;
4356 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
4357 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
4358
4359 if(item.BufferSize)
4360 {
4361 bufferInfo.size = item.BufferSize;
4362 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4363 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
4364 }
4365 else
4366 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004367 TEST(item.ImageSize.width && item.ImageSize.height);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004368
4369 imageInfo.extent.width = item.ImageSize.width;
4370 imageInfo.extent.height = item.ImageSize.height;
4371 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4372 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
4373 }
4374 };
4375
4376 ////////////////////////////////////////////////////////////////////////////////
4377 // Frames
4378 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
4379 {
4380 WaitForSingleObject(frameStartEvent, INFINITE);
4381
4382 // Always make some percent of used bufs unused, to choose different used ones.
4383 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
4384 for(size_t i = 0; i < bufsToMakeUnused; ++i)
4385 {
4386 size_t index = threadRand.Generate() % usedItems.size();
4387 unusedItems.push_back(usedItems[index]);
4388 usedItems.erase(usedItems.begin() + index);
4389 }
4390
4391 // Determine which bufs we want to use in this frame.
4392 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
4393 / config.ThreadCount;
Adam Sawickib8d34d52018-10-03 17:41:20 +02004394 TEST(usedBufCount < usedItems.size() + unusedItems.size());
Adam Sawickib8333fb2018-03-13 16:15:53 +01004395 // Move some used to unused.
4396 while(usedBufCount < usedItems.size())
4397 {
4398 size_t index = threadRand.Generate() % usedItems.size();
4399 unusedItems.push_back(usedItems[index]);
4400 usedItems.erase(usedItems.begin() + index);
4401 }
4402 // Move some unused to used.
4403 while(usedBufCount > usedItems.size())
4404 {
4405 size_t index = threadRand.Generate() % unusedItems.size();
4406 usedItems.push_back(unusedItems[index]);
4407 unusedItems.erase(unusedItems.begin() + index);
4408 }
4409
4410 uint32_t touchExistingCount = 0;
4411 uint32_t touchLostCount = 0;
4412 uint32_t createSucceededCount = 0;
4413 uint32_t createFailedCount = 0;
4414
4415 // Touch all used bufs. If not created or lost, allocate.
4416 for(size_t i = 0; i < usedItems.size(); ++i)
4417 {
4418 Item& item = usedItems[i];
4419 // Not yet created.
4420 if(item.Alloc == VK_NULL_HANDLE)
4421 {
4422 res = Allocate(item);
4423 ++outThreadResult->AllocationCount;
4424 if(res != VK_SUCCESS)
4425 {
4426 item.Alloc = VK_NULL_HANDLE;
4427 item.Buf = VK_NULL_HANDLE;
4428 ++outThreadResult->FailedAllocationCount;
4429 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
4430 ++createFailedCount;
4431 }
4432 else
4433 ++createSucceededCount;
4434 }
4435 else
4436 {
4437 // Touch.
4438 VmaAllocationInfo allocInfo;
4439 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
4440 // Lost.
4441 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
4442 {
4443 ++touchLostCount;
4444
4445 // Destroy.
4446 {
4447 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4448 if(item.Buf)
4449 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
4450 else
4451 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
4452 ++outThreadResult->DeallocationCount;
4453 }
4454 item.Alloc = VK_NULL_HANDLE;
4455 item.Buf = VK_NULL_HANDLE;
4456
4457 ++outThreadResult->LostAllocationCount;
4458 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
4459
4460 // Recreate.
4461 res = Allocate(item);
4462 ++outThreadResult->AllocationCount;
4463 // Creation failed.
4464 if(res != VK_SUCCESS)
4465 {
4466 ++outThreadResult->FailedAllocationCount;
4467 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
4468 ++createFailedCount;
4469 }
4470 else
4471 ++createSucceededCount;
4472 }
4473 else
4474 ++touchExistingCount;
4475 }
4476 }
4477
4478 /*
4479 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
4480 randSeed, frameIndex,
4481 touchExistingCount, touchLostCount,
4482 createSucceededCount, createFailedCount);
4483 */
4484
4485 SetEvent(frameEndEvent);
4486 }
4487
4488 // Free all remaining items.
4489 for(size_t i = usedItems.size(); i--; )
4490 {
4491 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4492 if(usedItems[i].Buf)
4493 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
4494 else
4495 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
4496 ++outThreadResult->DeallocationCount;
4497 }
4498 for(size_t i = unusedItems.size(); i--; )
4499 {
4500 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
4501 if(unusedItems[i].Buf)
4502 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
4503 else
4504 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
4505 ++outThreadResult->DeallocationCount;
4506 }
4507 };
4508
4509 // Launch threads.
4510 uint32_t threadRandSeed = mainRand.Generate();
4511 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
4512 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
4513 std::vector<std::thread> bkgThreads;
4514 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
4515 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4516 {
4517 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
4518 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
4519 bkgThreads.emplace_back(std::bind(
4520 ThreadProc,
4521 &threadResults[threadIndex],
4522 threadRandSeed + threadIndex,
4523 frameStartEvents[threadIndex],
4524 frameEndEvents[threadIndex]));
4525 }
4526
4527 // Execute frames.
Adam Sawickib8d34d52018-10-03 17:41:20 +02004528 TEST(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004529 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
4530 {
4531 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
4532 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4533 SetEvent(frameStartEvents[threadIndex]);
4534 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
4535 }
4536
4537 // Wait for threads finished
4538 for(size_t i = 0; i < bkgThreads.size(); ++i)
4539 {
4540 bkgThreads[i].join();
4541 CloseHandle(frameEndEvents[i]);
4542 CloseHandle(frameStartEvents[i]);
4543 }
4544 bkgThreads.clear();
4545
4546 // Finish time measurement - before destroying pool.
4547 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
4548
4549 vmaDestroyPool(g_hAllocator, pool);
4550
4551 outResult.AllocationTimeMin = duration::max();
4552 outResult.AllocationTimeAvg = duration::zero();
4553 outResult.AllocationTimeMax = duration::min();
4554 outResult.DeallocationTimeMin = duration::max();
4555 outResult.DeallocationTimeAvg = duration::zero();
4556 outResult.DeallocationTimeMax = duration::min();
4557 outResult.LostAllocationCount = 0;
4558 outResult.LostAllocationTotalSize = 0;
4559 outResult.FailedAllocationCount = 0;
4560 outResult.FailedAllocationTotalSize = 0;
4561 size_t allocationCount = 0;
4562 size_t deallocationCount = 0;
4563 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4564 {
4565 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
4566 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
4567 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
4568 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
4569 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
4570 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
4571 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
4572 allocationCount += threadResult.AllocationCount;
4573 deallocationCount += threadResult.DeallocationCount;
4574 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
4575 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
4576 outResult.LostAllocationCount += threadResult.LostAllocationCount;
4577 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
4578 }
4579 if(allocationCount)
4580 outResult.AllocationTimeAvg /= allocationCount;
4581 if(deallocationCount)
4582 outResult.DeallocationTimeAvg /= deallocationCount;
4583}
4584
4585static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
4586{
4587 if(ptr1 < ptr2)
4588 return ptr1 + size1 > ptr2;
4589 else if(ptr2 < ptr1)
4590 return ptr2 + size2 > ptr1;
4591 else
4592 return true;
4593}
4594
Adam Sawickiefa88c42019-11-18 16:33:56 +01004595static void TestMemoryUsage()
4596{
4597 wprintf(L"Testing memory usage:\n");
4598
Adam Sawicki69185552019-11-18 17:03:34 +01004599 static const VmaMemoryUsage lastUsage = VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED;
Adam Sawickiefa88c42019-11-18 16:33:56 +01004600 for(uint32_t usage = 0; usage <= lastUsage; ++usage)
4601 {
4602 switch(usage)
4603 {
4604 case VMA_MEMORY_USAGE_UNKNOWN: printf(" VMA_MEMORY_USAGE_UNKNOWN:\n"); break;
4605 case VMA_MEMORY_USAGE_GPU_ONLY: printf(" VMA_MEMORY_USAGE_GPU_ONLY:\n"); break;
4606 case VMA_MEMORY_USAGE_CPU_ONLY: printf(" VMA_MEMORY_USAGE_CPU_ONLY:\n"); break;
4607 case VMA_MEMORY_USAGE_CPU_TO_GPU: printf(" VMA_MEMORY_USAGE_CPU_TO_GPU:\n"); break;
4608 case VMA_MEMORY_USAGE_GPU_TO_CPU: printf(" VMA_MEMORY_USAGE_GPU_TO_CPU:\n"); break;
4609 case VMA_MEMORY_USAGE_CPU_COPY: printf(" VMA_MEMORY_USAGE_CPU_COPY:\n"); break;
Adam Sawicki69185552019-11-18 17:03:34 +01004610 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED: printf(" VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:\n"); break;
Adam Sawickiefa88c42019-11-18 16:33:56 +01004611 default: assert(0);
4612 }
4613
4614 auto printResult = [](const char* testName, VkResult res, uint32_t memoryTypeBits, uint32_t memoryTypeIndex)
4615 {
4616 if(res == VK_SUCCESS)
4617 printf(" %s: memoryTypeBits=0x%X, memoryTypeIndex=%u\n", testName, memoryTypeBits, memoryTypeIndex);
4618 else
4619 printf(" %s: memoryTypeBits=0x%X, FAILED with res=%d\n", testName, memoryTypeBits, (int32_t)res);
4620 };
4621
4622 // 1: Buffer for copy
4623 {
4624 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4625 bufCreateInfo.size = 65536;
4626 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4627
4628 VkBuffer buf = VK_NULL_HANDLE;
4629 VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
4630 TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
4631
4632 VkMemoryRequirements memReq = {};
4633 vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
4634
4635 VmaAllocationCreateInfo allocCreateInfo = {};
4636 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4637 VmaAllocation alloc = VK_NULL_HANDLE;
4638 VmaAllocationInfo allocInfo = {};
4639 res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
4640 if(res == VK_SUCCESS)
4641 {
4642 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4643 res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
4644 TEST(res == VK_SUCCESS);
4645 }
4646 printResult("Buffer TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
4647 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4648 }
4649
4650 // 2: Vertex buffer
4651 {
4652 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4653 bufCreateInfo.size = 65536;
4654 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4655
4656 VkBuffer buf = VK_NULL_HANDLE;
4657 VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
4658 TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
4659
4660 VkMemoryRequirements memReq = {};
4661 vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
4662
4663 VmaAllocationCreateInfo allocCreateInfo = {};
4664 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4665 VmaAllocation alloc = VK_NULL_HANDLE;
4666 VmaAllocationInfo allocInfo = {};
4667 res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
4668 if(res == VK_SUCCESS)
4669 {
4670 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4671 res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
4672 TEST(res == VK_SUCCESS);
4673 }
4674 printResult("Buffer TRANSFER_DST + VERTEX_BUFFER", res, memReq.memoryTypeBits, allocInfo.memoryType);
4675 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4676 }
4677
4678 // 3: Image for copy, OPTIMAL
4679 {
4680 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4681 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4682 imgCreateInfo.extent.width = 256;
4683 imgCreateInfo.extent.height = 256;
4684 imgCreateInfo.extent.depth = 1;
4685 imgCreateInfo.mipLevels = 1;
4686 imgCreateInfo.arrayLayers = 1;
4687 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4688 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4689 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4690 imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
4691 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4692
4693 VkImage img = VK_NULL_HANDLE;
4694 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4695 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4696
4697 VkMemoryRequirements memReq = {};
4698 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4699
4700 VmaAllocationCreateInfo allocCreateInfo = {};
4701 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4702 VmaAllocation alloc = VK_NULL_HANDLE;
4703 VmaAllocationInfo allocInfo = {};
4704 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4705 if(res == VK_SUCCESS)
4706 {
4707 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4708 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4709 TEST(res == VK_SUCCESS);
4710 }
4711 printResult("Image OPTIMAL TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
4712
4713 vmaDestroyImage(g_hAllocator, img, alloc);
4714 }
4715
4716 // 4: Image SAMPLED, OPTIMAL
4717 {
4718 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4719 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4720 imgCreateInfo.extent.width = 256;
4721 imgCreateInfo.extent.height = 256;
4722 imgCreateInfo.extent.depth = 1;
4723 imgCreateInfo.mipLevels = 1;
4724 imgCreateInfo.arrayLayers = 1;
4725 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4726 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4727 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4728 imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
4729 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4730
4731 VkImage img = VK_NULL_HANDLE;
4732 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4733 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4734
4735 VkMemoryRequirements memReq = {};
4736 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4737
4738 VmaAllocationCreateInfo allocCreateInfo = {};
4739 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4740 VmaAllocation alloc = VK_NULL_HANDLE;
4741 VmaAllocationInfo allocInfo = {};
4742 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4743 if(res == VK_SUCCESS)
4744 {
4745 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4746 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4747 TEST(res == VK_SUCCESS);
4748 }
4749 printResult("Image OPTIMAL TRANSFER_DST + SAMPLED", res, memReq.memoryTypeBits, allocInfo.memoryType);
4750 vmaDestroyImage(g_hAllocator, img, alloc);
4751 }
4752
4753 // 5: Image COLOR_ATTACHMENT, OPTIMAL
4754 {
4755 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4756 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4757 imgCreateInfo.extent.width = 256;
4758 imgCreateInfo.extent.height = 256;
4759 imgCreateInfo.extent.depth = 1;
4760 imgCreateInfo.mipLevels = 1;
4761 imgCreateInfo.arrayLayers = 1;
4762 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4763 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4764 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4765 imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
4766 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4767
4768 VkImage img = VK_NULL_HANDLE;
4769 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4770 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4771
4772 VkMemoryRequirements memReq = {};
4773 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4774
4775 VmaAllocationCreateInfo allocCreateInfo = {};
4776 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4777 VmaAllocation alloc = VK_NULL_HANDLE;
4778 VmaAllocationInfo allocInfo = {};
4779 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4780 if(res == VK_SUCCESS)
4781 {
4782 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4783 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4784 TEST(res == VK_SUCCESS);
4785 }
4786 printResult("Image OPTIMAL SAMPLED + COLOR_ATTACHMENT", res, memReq.memoryTypeBits, allocInfo.memoryType);
4787 vmaDestroyImage(g_hAllocator, img, alloc);
4788 }
4789 }
4790}
4791
Adam Sawicki40ffe982019-10-11 15:56:02 +02004792static void TestBudget()
4793{
4794 wprintf(L"Testing budget...\n");
4795
Adam Sawicki353e3672019-11-02 14:12:05 +01004796 static const VkDeviceSize BUF_SIZE = 100ull * 1024 * 1024;
4797 static const uint32_t BUF_COUNT = 4;
Adam Sawicki40ffe982019-10-11 15:56:02 +02004798
4799 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
4800 {
Adam Sawicki353e3672019-11-02 14:12:05 +01004801 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
4802
4803 VmaBudget budgetBeg[VK_MAX_MEMORY_HEAPS] = {};
4804 vmaGetBudget(g_hAllocator, budgetBeg);
Adam Sawicki40ffe982019-10-11 15:56:02 +02004805
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01004806 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
4807 {
4808 TEST(budgetBeg[i].allocationBytes <= budgetBeg[i].blockBytes);
4809 }
4810
Adam Sawicki40ffe982019-10-11 15:56:02 +02004811 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4812 bufInfo.size = BUF_SIZE;
4813 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4814
4815 VmaAllocationCreateInfo allocCreateInfo = {};
4816 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4817 if(testIndex == 0)
4818 {
4819 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
4820 }
4821
4822 // CREATE BUFFERS
4823 uint32_t heapIndex = 0;
4824 BufferInfo bufInfos[BUF_COUNT] = {};
4825 for(uint32_t bufIndex = 0; bufIndex < BUF_COUNT; ++bufIndex)
4826 {
4827 VmaAllocationInfo allocInfo;
4828 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
4829 &bufInfos[bufIndex].Buffer, &bufInfos[bufIndex].Allocation, &allocInfo);
4830 TEST(res == VK_SUCCESS);
4831 if(bufIndex == 0)
4832 {
4833 heapIndex = MemoryTypeToHeap(allocInfo.memoryType);
4834 }
4835 else
4836 {
4837 // All buffers need to fall into the same heap.
4838 TEST(MemoryTypeToHeap(allocInfo.memoryType) == heapIndex);
4839 }
4840 }
4841
Adam Sawicki353e3672019-11-02 14:12:05 +01004842 VmaBudget budgetWithBufs[VK_MAX_MEMORY_HEAPS] = {};
4843 vmaGetBudget(g_hAllocator, budgetWithBufs);
Adam Sawicki40ffe982019-10-11 15:56:02 +02004844
4845 // DESTROY BUFFERS
4846 for(size_t bufIndex = BUF_COUNT; bufIndex--; )
4847 {
4848 vmaDestroyBuffer(g_hAllocator, bufInfos[bufIndex].Buffer, bufInfos[bufIndex].Allocation);
4849 }
4850
Adam Sawicki353e3672019-11-02 14:12:05 +01004851 VmaBudget budgetEnd[VK_MAX_MEMORY_HEAPS] = {};
4852 vmaGetBudget(g_hAllocator, budgetEnd);
Adam Sawicki40ffe982019-10-11 15:56:02 +02004853
4854 // CHECK
4855 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
4856 {
Adam Sawicki353e3672019-11-02 14:12:05 +01004857 TEST(budgetEnd[i].allocationBytes <= budgetEnd[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02004858 if(i == heapIndex)
4859 {
Adam Sawicki353e3672019-11-02 14:12:05 +01004860 TEST(budgetEnd[i].allocationBytes == budgetBeg[i].allocationBytes);
4861 TEST(budgetWithBufs[i].allocationBytes == budgetBeg[i].allocationBytes + BUF_SIZE * BUF_COUNT);
4862 TEST(budgetWithBufs[i].blockBytes >= budgetEnd[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02004863 }
4864 else
4865 {
Adam Sawicki353e3672019-11-02 14:12:05 +01004866 TEST(budgetEnd[i].allocationBytes == budgetEnd[i].allocationBytes &&
4867 budgetEnd[i].allocationBytes == budgetWithBufs[i].allocationBytes);
4868 TEST(budgetEnd[i].blockBytes == budgetEnd[i].blockBytes &&
4869 budgetEnd[i].blockBytes == budgetWithBufs[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02004870 }
4871 }
4872 }
4873}
4874
Adam Sawickib8333fb2018-03-13 16:15:53 +01004875static void TestMapping()
4876{
4877 wprintf(L"Testing mapping...\n");
4878
4879 VkResult res;
4880 uint32_t memTypeIndex = UINT32_MAX;
4881
4882 enum TEST
4883 {
4884 TEST_NORMAL,
4885 TEST_POOL,
4886 TEST_DEDICATED,
4887 TEST_COUNT
4888 };
4889 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
4890 {
4891 VmaPool pool = nullptr;
4892 if(testIndex == TEST_POOL)
4893 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004894 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004895 VmaPoolCreateInfo poolInfo = {};
4896 poolInfo.memoryTypeIndex = memTypeIndex;
4897 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004898 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004899 }
4900
4901 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4902 bufInfo.size = 0x10000;
4903 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki40ffe982019-10-11 15:56:02 +02004904
Adam Sawickib8333fb2018-03-13 16:15:53 +01004905 VmaAllocationCreateInfo allocCreateInfo = {};
4906 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
4907 allocCreateInfo.pool = pool;
4908 if(testIndex == TEST_DEDICATED)
4909 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
Adam Sawicki40ffe982019-10-11 15:56:02 +02004910
Adam Sawickib8333fb2018-03-13 16:15:53 +01004911 VmaAllocationInfo allocInfo;
Adam Sawicki40ffe982019-10-11 15:56:02 +02004912
Adam Sawickib8333fb2018-03-13 16:15:53 +01004913 // Mapped manually
4914
4915 // Create 2 buffers.
4916 BufferInfo bufferInfos[3];
4917 for(size_t i = 0; i < 2; ++i)
4918 {
4919 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
4920 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004921 TEST(res == VK_SUCCESS);
4922 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004923 memTypeIndex = allocInfo.memoryType;
4924 }
Adam Sawicki40ffe982019-10-11 15:56:02 +02004925
Adam Sawickib8333fb2018-03-13 16:15:53 +01004926 // Map buffer 0.
4927 char* data00 = nullptr;
4928 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004929 TEST(res == VK_SUCCESS && data00 != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004930 data00[0xFFFF] = data00[0];
4931
4932 // Map buffer 0 second time.
4933 char* data01 = nullptr;
4934 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004935 TEST(res == VK_SUCCESS && data01 == data00);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004936
4937 // Map buffer 1.
4938 char* data1 = nullptr;
4939 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004940 TEST(res == VK_SUCCESS && data1 != nullptr);
4941 TEST(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004942 data1[0xFFFF] = data1[0];
4943
4944 // Unmap buffer 0 two times.
4945 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
4946 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
4947 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004948 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004949
4950 // Unmap buffer 1.
4951 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
4952 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004953 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004954
4955 // Create 3rd buffer - persistently mapped.
4956 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
4957 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
4958 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004959 TEST(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004960
4961 // Map buffer 2.
4962 char* data2 = nullptr;
4963 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004964 TEST(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004965 data2[0xFFFF] = data2[0];
4966
4967 // Unmap buffer 2.
4968 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
4969 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004970 TEST(allocInfo.pMappedData == data2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004971
4972 // Destroy all buffers.
4973 for(size_t i = 3; i--; )
4974 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
4975
4976 vmaDestroyPool(g_hAllocator, pool);
4977 }
4978}
4979
Adam Sawickidaa6a552019-06-25 15:26:37 +02004980// Test CREATE_MAPPED with required DEVICE_LOCAL. There was a bug with it.
4981static void TestDeviceLocalMapped()
4982{
4983 VkResult res;
4984
4985 for(uint32_t testIndex = 0; testIndex < 3; ++testIndex)
4986 {
4987 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4988 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4989 bufCreateInfo.size = 4096;
4990
4991 VmaPool pool = VK_NULL_HANDLE;
4992 VmaAllocationCreateInfo allocCreateInfo = {};
4993 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
4994 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
4995 if(testIndex == 2)
4996 {
4997 VmaPoolCreateInfo poolCreateInfo = {};
4998 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
4999 TEST(res == VK_SUCCESS);
5000 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
5001 TEST(res == VK_SUCCESS);
5002 allocCreateInfo.pool = pool;
5003 }
5004 else if(testIndex == 1)
5005 {
5006 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
5007 }
5008
5009 VkBuffer buf = VK_NULL_HANDLE;
5010 VmaAllocation alloc = VK_NULL_HANDLE;
5011 VmaAllocationInfo allocInfo = {};
5012 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
5013 TEST(res == VK_SUCCESS && alloc);
5014
5015 VkMemoryPropertyFlags memTypeFlags = 0;
5016 vmaGetMemoryTypeProperties(g_hAllocator, allocInfo.memoryType, &memTypeFlags);
5017 const bool shouldBeMapped = (memTypeFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
5018 TEST((allocInfo.pMappedData != nullptr) == shouldBeMapped);
5019
5020 vmaDestroyBuffer(g_hAllocator, buf, alloc);
5021 vmaDestroyPool(g_hAllocator, pool);
5022 }
5023}
5024
Adam Sawickib8333fb2018-03-13 16:15:53 +01005025static void TestMappingMultithreaded()
5026{
5027 wprintf(L"Testing mapping multithreaded...\n");
5028
5029 static const uint32_t threadCount = 16;
5030 static const uint32_t bufferCount = 1024;
5031 static const uint32_t threadBufferCount = bufferCount / threadCount;
5032
5033 VkResult res;
5034 volatile uint32_t memTypeIndex = UINT32_MAX;
5035
5036 enum TEST
5037 {
5038 TEST_NORMAL,
5039 TEST_POOL,
5040 TEST_DEDICATED,
5041 TEST_COUNT
5042 };
5043 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
5044 {
5045 VmaPool pool = nullptr;
5046 if(testIndex == TEST_POOL)
5047 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005048 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005049 VmaPoolCreateInfo poolInfo = {};
5050 poolInfo.memoryTypeIndex = memTypeIndex;
5051 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005052 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005053 }
5054
5055 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5056 bufCreateInfo.size = 0x10000;
5057 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
5058
5059 VmaAllocationCreateInfo allocCreateInfo = {};
5060 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
5061 allocCreateInfo.pool = pool;
5062 if(testIndex == TEST_DEDICATED)
5063 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5064
5065 std::thread threads[threadCount];
5066 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
5067 {
5068 threads[threadIndex] = std::thread([=, &memTypeIndex](){
5069 // ======== THREAD FUNCTION ========
5070
5071 RandomNumberGenerator rand{threadIndex};
5072
5073 enum class MODE
5074 {
5075 // Don't map this buffer at all.
5076 DONT_MAP,
5077 // Map and quickly unmap.
5078 MAP_FOR_MOMENT,
5079 // Map and unmap before destruction.
5080 MAP_FOR_LONGER,
5081 // Map two times. Quickly unmap, second unmap before destruction.
5082 MAP_TWO_TIMES,
5083 // Create this buffer as persistently mapped.
5084 PERSISTENTLY_MAPPED,
5085 COUNT
5086 };
5087 std::vector<BufferInfo> bufInfos{threadBufferCount};
5088 std::vector<MODE> bufModes{threadBufferCount};
5089
5090 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
5091 {
5092 BufferInfo& bufInfo = bufInfos[bufferIndex];
5093 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
5094 bufModes[bufferIndex] = mode;
5095
5096 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
5097 if(mode == MODE::PERSISTENTLY_MAPPED)
5098 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
5099
5100 VmaAllocationInfo allocInfo;
5101 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
5102 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005103 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005104
5105 if(memTypeIndex == UINT32_MAX)
5106 memTypeIndex = allocInfo.memoryType;
5107
5108 char* data = nullptr;
5109
5110 if(mode == MODE::PERSISTENTLY_MAPPED)
5111 {
5112 data = (char*)allocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02005113 TEST(data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005114 }
5115 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
5116 mode == MODE::MAP_TWO_TIMES)
5117 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005118 TEST(data == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005119 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005120 TEST(res == VK_SUCCESS && data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005121
5122 if(mode == MODE::MAP_TWO_TIMES)
5123 {
5124 char* data2 = nullptr;
5125 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005126 TEST(res == VK_SUCCESS && data2 == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005127 }
5128 }
5129 else if(mode == MODE::DONT_MAP)
5130 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005131 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005132 }
5133 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02005134 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005135
5136 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
5137 if(data)
5138 data[0xFFFF] = data[0];
5139
5140 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
5141 {
5142 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
5143
5144 VmaAllocationInfo allocInfo;
5145 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
5146 if(mode == MODE::MAP_FOR_MOMENT)
Adam Sawickib8d34d52018-10-03 17:41:20 +02005147 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005148 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02005149 TEST(allocInfo.pMappedData == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005150 }
5151
5152 switch(rand.Generate() % 3)
5153 {
5154 case 0: Sleep(0); break; // Yield.
5155 case 1: Sleep(10); break; // 10 ms
5156 // default: No sleep.
5157 }
5158
5159 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
5160 if(data)
5161 data[0xFFFF] = data[0];
5162 }
5163
5164 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
5165 {
5166 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
5167 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
5168 {
5169 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
5170
5171 VmaAllocationInfo allocInfo;
5172 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005173 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005174 }
5175
5176 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
5177 }
5178 });
5179 }
5180
5181 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
5182 threads[threadIndex].join();
5183
5184 vmaDestroyPool(g_hAllocator, pool);
5185 }
5186}
5187
5188static void WriteMainTestResultHeader(FILE* file)
5189{
5190 fprintf(file,
Adam Sawicki740b08f2018-08-27 13:42:07 +02005191 "Code,Time,"
5192 "Threads,Buffers and images,Sizes,Operations,Allocation strategy,Free order,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01005193 "Total Time (us),"
5194 "Allocation Time Min (us),"
5195 "Allocation Time Avg (us),"
5196 "Allocation Time Max (us),"
5197 "Deallocation Time Min (us),"
5198 "Deallocation Time Avg (us),"
5199 "Deallocation Time Max (us),"
5200 "Total Memory Allocated (B),"
5201 "Free Range Size Avg (B),"
5202 "Free Range Size Max (B)\n");
5203}
5204
5205static void WriteMainTestResult(
5206 FILE* file,
5207 const char* codeDescription,
5208 const char* testDescription,
5209 const Config& config, const Result& result)
5210{
5211 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
5212 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
5213 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
5214 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
5215 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
5216 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
5217 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
5218
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005219 std::string currTime;
5220 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005221
5222 fprintf(file,
5223 "%s,%s,%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01005224 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
5225 codeDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005226 currTime.c_str(),
Adam Sawicki740b08f2018-08-27 13:42:07 +02005227 testDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01005228 totalTimeSeconds * 1e6f,
5229 allocationTimeMinSeconds * 1e6f,
5230 allocationTimeAvgSeconds * 1e6f,
5231 allocationTimeMaxSeconds * 1e6f,
5232 deallocationTimeMinSeconds * 1e6f,
5233 deallocationTimeAvgSeconds * 1e6f,
5234 deallocationTimeMaxSeconds * 1e6f,
5235 result.TotalMemoryAllocated,
5236 result.FreeRangeSizeAvg,
5237 result.FreeRangeSizeMax);
5238}
5239
5240static void WritePoolTestResultHeader(FILE* file)
5241{
5242 fprintf(file,
5243 "Code,Test,Time,"
5244 "Config,"
5245 "Total Time (us),"
5246 "Allocation Time Min (us),"
5247 "Allocation Time Avg (us),"
5248 "Allocation Time Max (us),"
5249 "Deallocation Time Min (us),"
5250 "Deallocation Time Avg (us),"
5251 "Deallocation Time Max (us),"
5252 "Lost Allocation Count,"
5253 "Lost Allocation Total Size (B),"
5254 "Failed Allocation Count,"
5255 "Failed Allocation Total Size (B)\n");
5256}
5257
5258static void WritePoolTestResult(
5259 FILE* file,
5260 const char* codeDescription,
5261 const char* testDescription,
5262 const PoolTestConfig& config,
5263 const PoolTestResult& result)
5264{
5265 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
5266 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
5267 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
5268 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
5269 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
5270 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
5271 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
5272
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005273 std::string currTime;
5274 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005275
5276 fprintf(file,
5277 "%s,%s,%s,"
5278 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
5279 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
5280 // General
5281 codeDescription,
5282 testDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005283 currTime.c_str(),
Adam Sawickib8333fb2018-03-13 16:15:53 +01005284 // Config
5285 config.ThreadCount,
5286 (unsigned long long)config.PoolSize,
5287 config.FrameCount,
5288 config.TotalItemCount,
5289 config.UsedItemCountMin,
5290 config.UsedItemCountMax,
5291 config.ItemsToMakeUnusedPercent,
5292 // Results
5293 totalTimeSeconds * 1e6f,
5294 allocationTimeMinSeconds * 1e6f,
5295 allocationTimeAvgSeconds * 1e6f,
5296 allocationTimeMaxSeconds * 1e6f,
5297 deallocationTimeMinSeconds * 1e6f,
5298 deallocationTimeAvgSeconds * 1e6f,
5299 deallocationTimeMaxSeconds * 1e6f,
5300 result.LostAllocationCount,
5301 result.LostAllocationTotalSize,
5302 result.FailedAllocationCount,
5303 result.FailedAllocationTotalSize);
5304}
5305
5306static void PerformCustomMainTest(FILE* file)
5307{
5308 Config config{};
5309 config.RandSeed = 65735476;
5310 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
5311 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
5312 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
5313 config.FreeOrder = FREE_ORDER::FORWARD;
5314 config.ThreadCount = 16;
5315 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02005316 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005317
5318 // Buffers
5319 //config.AllocationSizes.push_back({4, 16, 1024});
5320 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5321
5322 // Images
5323 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5324 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5325
5326 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
5327 config.AdditionalOperationCount = 1024;
5328
5329 Result result{};
5330 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005331 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005332 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
5333}
5334
5335static void PerformCustomPoolTest(FILE* file)
5336{
5337 PoolTestConfig config;
5338 config.PoolSize = 100 * 1024 * 1024;
5339 config.RandSeed = 2345764;
5340 config.ThreadCount = 1;
5341 config.FrameCount = 200;
5342 config.ItemsToMakeUnusedPercent = 2;
5343
5344 AllocationSize allocSize = {};
5345 allocSize.BufferSizeMin = 1024;
5346 allocSize.BufferSizeMax = 1024 * 1024;
5347 allocSize.Probability = 1;
5348 config.AllocationSizes.push_back(allocSize);
5349
5350 allocSize.BufferSizeMin = 0;
5351 allocSize.BufferSizeMax = 0;
5352 allocSize.ImageSizeMin = 128;
5353 allocSize.ImageSizeMax = 1024;
5354 allocSize.Probability = 1;
5355 config.AllocationSizes.push_back(allocSize);
5356
5357 config.PoolSize = config.CalcAvgResourceSize() * 200;
5358 config.UsedItemCountMax = 160;
5359 config.TotalItemCount = config.UsedItemCountMax * 10;
5360 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
5361
5362 g_MemoryAliasingWarningEnabled = false;
5363 PoolTestResult result = {};
5364 TestPool_Benchmark(result, config);
5365 g_MemoryAliasingWarningEnabled = true;
5366
5367 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
5368}
5369
Adam Sawickib8333fb2018-03-13 16:15:53 +01005370static void PerformMainTests(FILE* file)
5371{
5372 uint32_t repeatCount = 1;
5373 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
5374
5375 Config config{};
5376 config.RandSeed = 65735476;
5377 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
5378 config.FreeOrder = FREE_ORDER::FORWARD;
5379
5380 size_t threadCountCount = 1;
5381 switch(ConfigType)
5382 {
5383 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
5384 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
5385 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
5386 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
5387 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
5388 default: assert(0);
5389 }
Adam Sawicki0667e332018-08-24 17:26:44 +02005390
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02005391 const size_t strategyCount = GetAllocationStrategyCount();
Adam Sawicki0667e332018-08-24 17:26:44 +02005392
Adam Sawickib8333fb2018-03-13 16:15:53 +01005393 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
5394 {
5395 std::string desc1;
5396
5397 switch(threadCountIndex)
5398 {
5399 case 0:
5400 desc1 += "1_thread";
5401 config.ThreadCount = 1;
5402 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5403 break;
5404 case 1:
5405 desc1 += "16_threads+0%_common";
5406 config.ThreadCount = 16;
5407 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5408 break;
5409 case 2:
5410 desc1 += "16_threads+50%_common";
5411 config.ThreadCount = 16;
5412 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
5413 break;
5414 case 3:
5415 desc1 += "16_threads+100%_common";
5416 config.ThreadCount = 16;
5417 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
5418 break;
5419 case 4:
5420 desc1 += "2_threads+0%_common";
5421 config.ThreadCount = 2;
5422 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5423 break;
5424 case 5:
5425 desc1 += "2_threads+50%_common";
5426 config.ThreadCount = 2;
5427 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
5428 break;
5429 case 6:
5430 desc1 += "2_threads+100%_common";
5431 config.ThreadCount = 2;
5432 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
5433 break;
5434 default:
5435 assert(0);
5436 }
5437
5438 // 0 = buffers, 1 = images, 2 = buffers and images
5439 size_t buffersVsImagesCount = 2;
5440 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
5441 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
5442 {
5443 std::string desc2 = desc1;
5444 switch(buffersVsImagesIndex)
5445 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005446 case 0: desc2 += ",Buffers"; break;
5447 case 1: desc2 += ",Images"; break;
5448 case 2: desc2 += ",Buffers+Images"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005449 default: assert(0);
5450 }
5451
5452 // 0 = small, 1 = large, 2 = small and large
5453 size_t smallVsLargeCount = 2;
5454 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
5455 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
5456 {
5457 std::string desc3 = desc2;
5458 switch(smallVsLargeIndex)
5459 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005460 case 0: desc3 += ",Small"; break;
5461 case 1: desc3 += ",Large"; break;
5462 case 2: desc3 += ",Small+Large"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005463 default: assert(0);
5464 }
5465
5466 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5467 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
5468 else
5469 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
5470
5471 // 0 = varying sizes min...max, 1 = set of constant sizes
5472 size_t constantSizesCount = 1;
5473 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
5474 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
5475 {
5476 std::string desc4 = desc3;
5477 switch(constantSizesIndex)
5478 {
5479 case 0: desc4 += " Varying_sizes"; break;
5480 case 1: desc4 += " Constant_sizes"; break;
5481 default: assert(0);
5482 }
5483
5484 config.AllocationSizes.clear();
5485 // Buffers present
5486 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
5487 {
5488 // Small
5489 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5490 {
5491 // Varying size
5492 if(constantSizesIndex == 0)
5493 config.AllocationSizes.push_back({4, 16, 1024});
5494 // Constant sizes
5495 else
5496 {
5497 config.AllocationSizes.push_back({1, 16, 16});
5498 config.AllocationSizes.push_back({1, 64, 64});
5499 config.AllocationSizes.push_back({1, 256, 256});
5500 config.AllocationSizes.push_back({1, 1024, 1024});
5501 }
5502 }
5503 // Large
5504 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5505 {
5506 // Varying size
5507 if(constantSizesIndex == 0)
5508 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5509 // Constant sizes
5510 else
5511 {
5512 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
5513 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
5514 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
5515 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
5516 }
5517 }
5518 }
5519 // Images present
5520 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
5521 {
5522 // Small
5523 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5524 {
5525 // Varying size
5526 if(constantSizesIndex == 0)
5527 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5528 // Constant sizes
5529 else
5530 {
5531 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
5532 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
5533 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
5534 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
5535 }
5536 }
5537 // Large
5538 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5539 {
5540 // Varying size
5541 if(constantSizesIndex == 0)
5542 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5543 // Constant sizes
5544 else
5545 {
5546 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
5547 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
5548 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
5549 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
5550 }
5551 }
5552 }
5553
5554 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
5555 size_t beginBytesToAllocateCount = 1;
5556 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
5557 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
5558 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
5559 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
5560 {
5561 std::string desc5 = desc4;
5562
5563 switch(beginBytesToAllocateIndex)
5564 {
5565 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005566 desc5 += ",Allocate_100%";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005567 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
5568 config.AdditionalOperationCount = 0;
5569 break;
5570 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005571 desc5 += ",Allocate_50%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005572 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
5573 config.AdditionalOperationCount = 1024;
5574 break;
5575 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005576 desc5 += ",Allocate_5%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005577 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
5578 config.AdditionalOperationCount = 1024;
5579 break;
5580 case 3:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005581 desc5 += ",Allocate_95%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005582 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
5583 config.AdditionalOperationCount = 1024;
5584 break;
5585 default:
5586 assert(0);
5587 }
5588
Adam Sawicki0667e332018-08-24 17:26:44 +02005589 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01005590 {
Adam Sawicki0667e332018-08-24 17:26:44 +02005591 std::string desc6 = desc5;
5592 switch(strategyIndex)
5593 {
5594 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005595 desc6 += ",BestFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02005596 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
5597 break;
5598 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005599 desc6 += ",WorstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02005600 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
5601 break;
5602 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005603 desc6 += ",FirstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02005604 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
5605 break;
5606 default:
5607 assert(0);
5608 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01005609
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005610 desc6 += ',';
5611 desc6 += FREE_ORDER_NAMES[(uint32_t)config.FreeOrder];
Adam Sawicki740b08f2018-08-27 13:42:07 +02005612
5613 const char* testDescription = desc6.c_str();
Adam Sawicki0667e332018-08-24 17:26:44 +02005614
5615 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
5616 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005617 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawicki0667e332018-08-24 17:26:44 +02005618
5619 Result result{};
5620 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005621 TEST(res == VK_SUCCESS);
Adam Sawicki740b08f2018-08-27 13:42:07 +02005622 if(file)
5623 {
5624 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
5625 }
Adam Sawicki0667e332018-08-24 17:26:44 +02005626 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01005627 }
5628 }
5629 }
5630 }
5631 }
5632 }
5633}
5634
5635static void PerformPoolTests(FILE* file)
5636{
5637 const size_t AVG_RESOURCES_PER_POOL = 300;
5638
5639 uint32_t repeatCount = 1;
5640 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
5641
5642 PoolTestConfig config{};
5643 config.RandSeed = 2346343;
5644 config.FrameCount = 200;
5645 config.ItemsToMakeUnusedPercent = 2;
5646
5647 size_t threadCountCount = 1;
5648 switch(ConfigType)
5649 {
5650 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
5651 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
5652 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
5653 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
5654 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
5655 default: assert(0);
5656 }
5657 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
5658 {
5659 std::string desc1;
5660
5661 switch(threadCountIndex)
5662 {
5663 case 0:
5664 desc1 += "1_thread";
5665 config.ThreadCount = 1;
5666 break;
5667 case 1:
5668 desc1 += "16_threads";
5669 config.ThreadCount = 16;
5670 break;
5671 case 2:
5672 desc1 += "2_threads";
5673 config.ThreadCount = 2;
5674 break;
5675 default:
5676 assert(0);
5677 }
5678
5679 // 0 = buffers, 1 = images, 2 = buffers and images
5680 size_t buffersVsImagesCount = 2;
5681 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
5682 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
5683 {
5684 std::string desc2 = desc1;
5685 switch(buffersVsImagesIndex)
5686 {
5687 case 0: desc2 += " Buffers"; break;
5688 case 1: desc2 += " Images"; break;
5689 case 2: desc2 += " Buffers+Images"; break;
5690 default: assert(0);
5691 }
5692
5693 // 0 = small, 1 = large, 2 = small and large
5694 size_t smallVsLargeCount = 2;
5695 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
5696 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
5697 {
5698 std::string desc3 = desc2;
5699 switch(smallVsLargeIndex)
5700 {
5701 case 0: desc3 += " Small"; break;
5702 case 1: desc3 += " Large"; break;
5703 case 2: desc3 += " Small+Large"; break;
5704 default: assert(0);
5705 }
5706
5707 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5708 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
5709 else
5710 config.PoolSize = 4ull * 1024 * 1024;
5711
5712 // 0 = varying sizes min...max, 1 = set of constant sizes
5713 size_t constantSizesCount = 1;
5714 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
5715 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
5716 {
5717 std::string desc4 = desc3;
5718 switch(constantSizesIndex)
5719 {
5720 case 0: desc4 += " Varying_sizes"; break;
5721 case 1: desc4 += " Constant_sizes"; break;
5722 default: assert(0);
5723 }
5724
5725 config.AllocationSizes.clear();
5726 // Buffers present
5727 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
5728 {
5729 // Small
5730 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5731 {
5732 // Varying size
5733 if(constantSizesIndex == 0)
5734 config.AllocationSizes.push_back({4, 16, 1024});
5735 // Constant sizes
5736 else
5737 {
5738 config.AllocationSizes.push_back({1, 16, 16});
5739 config.AllocationSizes.push_back({1, 64, 64});
5740 config.AllocationSizes.push_back({1, 256, 256});
5741 config.AllocationSizes.push_back({1, 1024, 1024});
5742 }
5743 }
5744 // Large
5745 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5746 {
5747 // Varying size
5748 if(constantSizesIndex == 0)
5749 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5750 // Constant sizes
5751 else
5752 {
5753 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
5754 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
5755 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
5756 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
5757 }
5758 }
5759 }
5760 // Images present
5761 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
5762 {
5763 // Small
5764 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5765 {
5766 // Varying size
5767 if(constantSizesIndex == 0)
5768 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5769 // Constant sizes
5770 else
5771 {
5772 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
5773 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
5774 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
5775 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
5776 }
5777 }
5778 // Large
5779 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5780 {
5781 // Varying size
5782 if(constantSizesIndex == 0)
5783 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5784 // Constant sizes
5785 else
5786 {
5787 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
5788 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
5789 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
5790 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
5791 }
5792 }
5793 }
5794
5795 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
5796 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
5797
5798 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
5799 size_t subscriptionModeCount;
5800 switch(ConfigType)
5801 {
5802 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
5803 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
5804 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
5805 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
5806 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
5807 default: assert(0);
5808 }
5809 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
5810 {
5811 std::string desc5 = desc4;
5812
5813 switch(subscriptionModeIndex)
5814 {
5815 case 0:
5816 desc5 += " Subscription_66%";
5817 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
5818 break;
5819 case 1:
5820 desc5 += " Subscription_133%";
5821 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
5822 break;
5823 case 2:
5824 desc5 += " Subscription_100%";
5825 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
5826 break;
5827 case 3:
5828 desc5 += " Subscription_33%";
5829 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
5830 break;
5831 case 4:
5832 desc5 += " Subscription_166%";
5833 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
5834 break;
5835 default:
5836 assert(0);
5837 }
5838
5839 config.TotalItemCount = config.UsedItemCountMax * 5;
5840 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
5841
5842 const char* testDescription = desc5.c_str();
5843
5844 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
5845 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005846 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005847
5848 PoolTestResult result{};
5849 g_MemoryAliasingWarningEnabled = false;
5850 TestPool_Benchmark(result, config);
5851 g_MemoryAliasingWarningEnabled = true;
5852 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
5853 }
5854 }
5855 }
5856 }
5857 }
5858 }
5859}
5860
Adam Sawickia83793a2018-09-03 13:40:42 +02005861static void BasicTestBuddyAllocator()
5862{
5863 wprintf(L"Basic test buddy allocator\n");
5864
5865 RandomNumberGenerator rand{76543};
5866
5867 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5868 sampleBufCreateInfo.size = 1024; // Whatever.
5869 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
5870
5871 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
5872 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
5873
5874 VmaPoolCreateInfo poolCreateInfo = {};
5875 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005876 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02005877
Adam Sawickid6e6d6b2018-09-21 14:07:02 +02005878 // Deliberately adding 1023 to test usable size smaller than memory block size.
5879 poolCreateInfo.blockSize = 1024 * 1024 + 1023;
Adam Sawickia83793a2018-09-03 13:40:42 +02005880 poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
Adam Sawicki80927152018-09-07 17:27:23 +02005881 //poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
Adam Sawickia83793a2018-09-03 13:40:42 +02005882
5883 VmaPool pool = nullptr;
5884 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005885 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02005886
5887 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
5888
5889 VmaAllocationCreateInfo allocCreateInfo = {};
5890 allocCreateInfo.pool = pool;
5891
5892 std::vector<BufferInfo> bufInfo;
5893 BufferInfo newBufInfo;
5894 VmaAllocationInfo allocInfo;
5895
5896 bufCreateInfo.size = 1024 * 256;
5897 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
5898 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005899 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02005900 bufInfo.push_back(newBufInfo);
5901
5902 bufCreateInfo.size = 1024 * 512;
5903 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
5904 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005905 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02005906 bufInfo.push_back(newBufInfo);
5907
5908 bufCreateInfo.size = 1024 * 128;
5909 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
5910 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005911 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02005912 bufInfo.push_back(newBufInfo);
Adam Sawickia01d4582018-09-21 14:22:35 +02005913
5914 // Test very small allocation, smaller than minimum node size.
5915 bufCreateInfo.size = 1;
5916 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
5917 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005918 TEST(res == VK_SUCCESS);
Adam Sawickia01d4582018-09-21 14:22:35 +02005919 bufInfo.push_back(newBufInfo);
Adam Sawickia83793a2018-09-03 13:40:42 +02005920
Adam Sawicki9933c5c2018-09-21 14:57:24 +02005921 // Test some small allocation with alignment requirement.
5922 {
5923 VkMemoryRequirements memReq;
5924 memReq.alignment = 256;
5925 memReq.memoryTypeBits = UINT32_MAX;
5926 memReq.size = 32;
5927
5928 newBufInfo.Buffer = VK_NULL_HANDLE;
5929 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo,
5930 &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005931 TEST(res == VK_SUCCESS);
5932 TEST(allocInfo.offset % memReq.alignment == 0);
Adam Sawicki9933c5c2018-09-21 14:57:24 +02005933 bufInfo.push_back(newBufInfo);
5934 }
5935
5936 //SaveAllocatorStatsToFile(L"TEST.json");
5937
Adam Sawicki21017c62018-09-07 15:26:59 +02005938 VmaPoolStats stats = {};
5939 vmaGetPoolStats(g_hAllocator, pool, &stats);
5940 int DBG = 0; // Set breakpoint here to inspect `stats`.
5941
Adam Sawicki80927152018-09-07 17:27:23 +02005942 // Allocate enough new buffers to surely fall into second block.
5943 for(uint32_t i = 0; i < 32; ++i)
5944 {
5945 bufCreateInfo.size = 1024 * (rand.Generate() % 32 + 1);
5946 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
5947 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005948 TEST(res == VK_SUCCESS);
Adam Sawicki80927152018-09-07 17:27:23 +02005949 bufInfo.push_back(newBufInfo);
5950 }
5951
5952 SaveAllocatorStatsToFile(L"BuddyTest01.json");
5953
Adam Sawickia83793a2018-09-03 13:40:42 +02005954 // Destroy the buffers in random order.
5955 while(!bufInfo.empty())
5956 {
5957 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
5958 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
5959 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
5960 bufInfo.erase(bufInfo.begin() + indexToDestroy);
5961 }
5962
5963 vmaDestroyPool(g_hAllocator, pool);
5964}
5965
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02005966static void BasicTestAllocatePages()
5967{
5968 wprintf(L"Basic test allocate pages\n");
5969
5970 RandomNumberGenerator rand{765461};
5971
5972 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5973 sampleBufCreateInfo.size = 1024; // Whatever.
5974 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
5975
5976 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
5977 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
5978
5979 VmaPoolCreateInfo poolCreateInfo = {};
5980 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickia7d77692018-10-03 16:15:27 +02005981 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02005982
5983 // 1 block of 1 MB.
5984 poolCreateInfo.blockSize = 1024 * 1024;
5985 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
5986
5987 // Create pool.
5988 VmaPool pool = nullptr;
5989 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickia7d77692018-10-03 16:15:27 +02005990 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02005991
5992 // Make 100 allocations of 4 KB - they should fit into the pool.
5993 VkMemoryRequirements memReq;
5994 memReq.memoryTypeBits = UINT32_MAX;
5995 memReq.alignment = 4 * 1024;
5996 memReq.size = 4 * 1024;
5997
5998 VmaAllocationCreateInfo allocCreateInfo = {};
5999 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
6000 allocCreateInfo.pool = pool;
6001
6002 constexpr uint32_t allocCount = 100;
6003
6004 std::vector<VmaAllocation> alloc{allocCount};
6005 std::vector<VmaAllocationInfo> allocInfo{allocCount};
6006 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006007 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006008 for(uint32_t i = 0; i < allocCount; ++i)
6009 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006010 TEST(alloc[i] != VK_NULL_HANDLE &&
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006011 allocInfo[i].pMappedData != nullptr &&
6012 allocInfo[i].deviceMemory == allocInfo[0].deviceMemory &&
6013 allocInfo[i].memoryType == allocInfo[0].memoryType);
6014 }
6015
6016 // Free the allocations.
6017 vmaFreeMemoryPages(g_hAllocator, allocCount, alloc.data());
6018 std::fill(alloc.begin(), alloc.end(), nullptr);
6019 std::fill(allocInfo.begin(), allocInfo.end(), VmaAllocationInfo{});
6020
6021 // Try to make 100 allocations of 100 KB. This call should fail due to not enough memory.
6022 // Also test optional allocationInfo = null.
6023 memReq.size = 100 * 1024;
6024 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), nullptr);
Adam Sawickia7d77692018-10-03 16:15:27 +02006025 TEST(res != VK_SUCCESS);
6026 TEST(std::find_if(alloc.begin(), alloc.end(), [](VmaAllocation alloc){ return alloc != VK_NULL_HANDLE; }) == alloc.end());
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006027
6028 // Make 100 allocations of 4 KB, but with required alignment of 128 KB. This should also fail.
6029 memReq.size = 4 * 1024;
6030 memReq.alignment = 128 * 1024;
6031 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006032 TEST(res != VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006033
6034 // Make 100 dedicated allocations of 4 KB.
6035 memReq.alignment = 4 * 1024;
6036 memReq.size = 4 * 1024;
6037
6038 VmaAllocationCreateInfo dedicatedAllocCreateInfo = {};
6039 dedicatedAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
6040 dedicatedAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
6041 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &dedicatedAllocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006042 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006043 for(uint32_t i = 0; i < allocCount; ++i)
6044 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006045 TEST(alloc[i] != VK_NULL_HANDLE &&
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006046 allocInfo[i].pMappedData != nullptr &&
6047 allocInfo[i].memoryType == allocInfo[0].memoryType &&
6048 allocInfo[i].offset == 0);
6049 if(i > 0)
6050 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006051 TEST(allocInfo[i].deviceMemory != allocInfo[0].deviceMemory);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006052 }
6053 }
6054
6055 // Free the allocations.
6056 vmaFreeMemoryPages(g_hAllocator, allocCount, alloc.data());
6057 std::fill(alloc.begin(), alloc.end(), nullptr);
6058 std::fill(allocInfo.begin(), allocInfo.end(), VmaAllocationInfo{});
6059
6060 vmaDestroyPool(g_hAllocator, pool);
6061}
6062
Adam Sawickif2975342018-10-16 13:49:02 +02006063// Test the testing environment.
6064static void TestGpuData()
6065{
6066 RandomNumberGenerator rand = { 53434 };
6067
6068 std::vector<AllocInfo> allocInfo;
6069
6070 for(size_t i = 0; i < 100; ++i)
6071 {
6072 AllocInfo info = {};
6073
6074 info.m_BufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
6075 info.m_BufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT |
6076 VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
6077 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6078 info.m_BufferInfo.size = 1024 * 1024 * (rand.Generate() % 9 + 1);
6079
6080 VmaAllocationCreateInfo allocCreateInfo = {};
6081 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6082
6083 VkResult res = vmaCreateBuffer(g_hAllocator, &info.m_BufferInfo, &allocCreateInfo, &info.m_Buffer, &info.m_Allocation, nullptr);
6084 TEST(res == VK_SUCCESS);
6085
6086 info.m_StartValue = rand.Generate();
6087
6088 allocInfo.push_back(std::move(info));
6089 }
6090
6091 UploadGpuData(allocInfo.data(), allocInfo.size());
6092
6093 ValidateGpuData(allocInfo.data(), allocInfo.size());
6094
6095 DestroyAllAllocations(allocInfo);
6096}
6097
Adam Sawickib8333fb2018-03-13 16:15:53 +01006098void Test()
6099{
6100 wprintf(L"TESTING:\n");
6101
Adam Sawicki48b8a332019-11-02 15:24:33 +01006102 if(false)
Adam Sawicki70a683e2018-08-24 15:36:32 +02006103 {
Adam Sawicki1a8424f2018-12-13 11:01:16 +01006104 ////////////////////////////////////////////////////////////////////////////////
6105 // Temporarily insert custom tests here:
Adam Sawicki70a683e2018-08-24 15:36:32 +02006106 return;
6107 }
6108
Adam Sawickib8333fb2018-03-13 16:15:53 +01006109 // # Simple tests
6110
6111 TestBasics();
Adam Sawickif2975342018-10-16 13:49:02 +02006112 //TestGpuData(); // Not calling this because it's just testing the testing environment.
Adam Sawicki212a4a62018-06-14 15:44:45 +02006113#if VMA_DEBUG_MARGIN
6114 TestDebugMargin();
6115#else
6116 TestPool_SameSize();
Adam Sawickiddcbf8c2019-11-22 15:22:42 +01006117 TestPool_MinBlockCount();
Adam Sawicki212a4a62018-06-14 15:44:45 +02006118 TestHeapSizeLimit();
6119#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02006120#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
6121 TestAllocationsInitialization();
6122#endif
Adam Sawickiefa88c42019-11-18 16:33:56 +01006123 TestMemoryUsage();
Adam Sawicki40ffe982019-10-11 15:56:02 +02006124 TestBudget();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006125 TestMapping();
Adam Sawickidaa6a552019-06-25 15:26:37 +02006126 TestDeviceLocalMapped();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006127 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02006128 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02006129 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02006130 TestLinearAllocatorMultiBlock();
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006131
Adam Sawicki4338f662018-09-07 14:12:37 +02006132 BasicTestBuddyAllocator();
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006133 BasicTestAllocatePages();
Adam Sawicki4338f662018-09-07 14:12:37 +02006134
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006135 {
6136 FILE* file;
Adam Sawickic6432d12018-09-21 16:44:16 +02006137 fopen_s(&file, "Algorithms.csv", "w");
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006138 assert(file != NULL);
Adam Sawicki80927152018-09-07 17:27:23 +02006139 BenchmarkAlgorithms(file);
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006140 fclose(file);
6141 }
6142
Adam Sawickib8333fb2018-03-13 16:15:53 +01006143 TestDefragmentationSimple();
6144 TestDefragmentationFull();
Adam Sawicki52076eb2018-11-22 16:14:50 +01006145 TestDefragmentationWholePool();
Adam Sawicki9a4f5082018-11-23 17:26:05 +01006146 TestDefragmentationGpu();
Adam Sawickia52012d2019-12-23 15:28:51 +01006147 TestDefragmentationIncrementalBasic();
6148 TestDefragmentationIncrementalComplex();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006149
6150 // # Detailed tests
6151 FILE* file;
6152 fopen_s(&file, "Results.csv", "w");
6153 assert(file != NULL);
6154
6155 WriteMainTestResultHeader(file);
6156 PerformMainTests(file);
6157 //PerformCustomMainTest(file);
6158
6159 WritePoolTestResultHeader(file);
6160 PerformPoolTests(file);
6161 //PerformCustomPoolTest(file);
6162
6163 fclose(file);
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01006164
Adam Sawickib8333fb2018-03-13 16:15:53 +01006165 wprintf(L"Done.\n");
6166}
6167
Adam Sawickif1a793c2018-03-13 15:42:22 +01006168#endif // #ifdef _WIN32