blob: f7536c401f08390ca2c1a6dbf35cdc526940ef6e [file] [log] [blame]
Adam Sawickif1a793c2018-03-13 15:42:22 +01001#include "Tests.h"
2#include "VmaUsage.h"
3#include "Common.h"
Adam Sawickib8333fb2018-03-13 16:15:53 +01004#include <atomic>
5#include <thread>
6#include <mutex>
Adam Sawickif1a793c2018-03-13 15:42:22 +01007
8#ifdef _WIN32
9
Adam Sawicki33d2ce72018-08-27 13:59:13 +020010static const char* CODE_DESCRIPTION = "Foo";
11
Adam Sawicki0a607132018-08-24 11:18:41 +020012enum CONFIG_TYPE {
13 CONFIG_TYPE_MINIMUM,
14 CONFIG_TYPE_SMALL,
15 CONFIG_TYPE_AVERAGE,
16 CONFIG_TYPE_LARGE,
17 CONFIG_TYPE_MAXIMUM,
18 CONFIG_TYPE_COUNT
19};
20
Adam Sawicki80927152018-09-07 17:27:23 +020021//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
22static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
Adam Sawicki0a607132018-08-24 11:18:41 +020023
Adam Sawickib8333fb2018-03-13 16:15:53 +010024enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
25
Adam Sawicki0667e332018-08-24 17:26:44 +020026static const char* FREE_ORDER_NAMES[] = {
27 "FORWARD",
28 "BACKWARD",
29 "RANDOM",
Adam Sawicki0a607132018-08-24 11:18:41 +020030};
31
Adam Sawicki80927152018-09-07 17:27:23 +020032// Copy of internal VmaAlgorithmToStr.
33static const char* AlgorithmToStr(uint32_t algorithm)
34{
35 switch(algorithm)
36 {
37 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
38 return "Linear";
39 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
40 return "Buddy";
41 case 0:
42 return "Default";
43 default:
44 assert(0);
45 return "";
46 }
47}
48
Adam Sawickib8333fb2018-03-13 16:15:53 +010049struct AllocationSize
50{
51 uint32_t Probability;
52 VkDeviceSize BufferSizeMin, BufferSizeMax;
53 uint32_t ImageSizeMin, ImageSizeMax;
54};
55
56struct Config
57{
58 uint32_t RandSeed;
59 VkDeviceSize BeginBytesToAllocate;
60 uint32_t AdditionalOperationCount;
61 VkDeviceSize MaxBytesToAllocate;
62 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
63 std::vector<AllocationSize> AllocationSizes;
64 uint32_t ThreadCount;
65 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
66 FREE_ORDER FreeOrder;
Adam Sawicki0667e332018-08-24 17:26:44 +020067 VmaAllocationCreateFlags AllocationStrategy; // For VMA_ALLOCATION_CREATE_STRATEGY_*
Adam Sawickib8333fb2018-03-13 16:15:53 +010068};
69
70struct Result
71{
72 duration TotalTime;
73 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
74 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
75 VkDeviceSize TotalMemoryAllocated;
76 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
77};
78
79void TestDefragmentationSimple();
80void TestDefragmentationFull();
81
82struct PoolTestConfig
83{
84 uint32_t RandSeed;
85 uint32_t ThreadCount;
86 VkDeviceSize PoolSize;
87 uint32_t FrameCount;
88 uint32_t TotalItemCount;
89 // Range for number of items used in each frame.
90 uint32_t UsedItemCountMin, UsedItemCountMax;
91 // Percent of items to make unused, and possibly make some others used in each frame.
92 uint32_t ItemsToMakeUnusedPercent;
93 std::vector<AllocationSize> AllocationSizes;
94
95 VkDeviceSize CalcAvgResourceSize() const
96 {
97 uint32_t probabilitySum = 0;
98 VkDeviceSize sizeSum = 0;
99 for(size_t i = 0; i < AllocationSizes.size(); ++i)
100 {
101 const AllocationSize& allocSize = AllocationSizes[i];
102 if(allocSize.BufferSizeMax > 0)
103 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
104 else
105 {
106 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
107 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
108 }
109 probabilitySum += allocSize.Probability;
110 }
111 return sizeSum / probabilitySum;
112 }
113
114 bool UsesBuffers() const
115 {
116 for(size_t i = 0; i < AllocationSizes.size(); ++i)
117 if(AllocationSizes[i].BufferSizeMax > 0)
118 return true;
119 return false;
120 }
121
122 bool UsesImages() const
123 {
124 for(size_t i = 0; i < AllocationSizes.size(); ++i)
125 if(AllocationSizes[i].ImageSizeMax > 0)
126 return true;
127 return false;
128 }
129};
130
131struct PoolTestResult
132{
133 duration TotalTime;
134 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
135 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
136 size_t LostAllocationCount, LostAllocationTotalSize;
137 size_t FailedAllocationCount, FailedAllocationTotalSize;
138};
139
140static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
141
Adam Sawicki8cfe05f2018-08-22 16:48:17 +0200142static uint32_t g_FrameIndex = 0;
143
Adam Sawickib8333fb2018-03-13 16:15:53 +0100144struct BufferInfo
145{
146 VkBuffer Buffer = VK_NULL_HANDLE;
147 VmaAllocation Allocation = VK_NULL_HANDLE;
148};
149
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +0200150static uint32_t GetAllocationStrategyCount()
151{
152 uint32_t strategyCount = 0;
153 switch(ConfigType)
154 {
155 case CONFIG_TYPE_MINIMUM: strategyCount = 1; break;
156 case CONFIG_TYPE_SMALL: strategyCount = 1; break;
157 case CONFIG_TYPE_AVERAGE: strategyCount = 2; break;
158 case CONFIG_TYPE_LARGE: strategyCount = 2; break;
159 case CONFIG_TYPE_MAXIMUM: strategyCount = 3; break;
160 default: assert(0);
161 }
162 return strategyCount;
163}
164
165static const char* GetAllocationStrategyName(VmaAllocationCreateFlags allocStrategy)
166{
167 switch(allocStrategy)
168 {
169 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT: return "BEST_FIT"; break;
170 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT: return "WORST_FIT"; break;
171 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT: return "FIRST_FIT"; break;
172 case 0: return "Default"; break;
173 default: assert(0); return "";
174 }
175}
176
Adam Sawickib8333fb2018-03-13 16:15:53 +0100177static void InitResult(Result& outResult)
178{
179 outResult.TotalTime = duration::zero();
180 outResult.AllocationTimeMin = duration::max();
181 outResult.AllocationTimeAvg = duration::zero();
182 outResult.AllocationTimeMax = duration::min();
183 outResult.DeallocationTimeMin = duration::max();
184 outResult.DeallocationTimeAvg = duration::zero();
185 outResult.DeallocationTimeMax = duration::min();
186 outResult.TotalMemoryAllocated = 0;
187 outResult.FreeRangeSizeAvg = 0;
188 outResult.FreeRangeSizeMax = 0;
189}
190
191class TimeRegisterObj
192{
193public:
194 TimeRegisterObj(duration& min, duration& sum, duration& max) :
195 m_Min(min),
196 m_Sum(sum),
197 m_Max(max),
198 m_TimeBeg(std::chrono::high_resolution_clock::now())
199 {
200 }
201
202 ~TimeRegisterObj()
203 {
204 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
205 m_Sum += d;
206 if(d < m_Min) m_Min = d;
207 if(d > m_Max) m_Max = d;
208 }
209
210private:
211 duration& m_Min;
212 duration& m_Sum;
213 duration& m_Max;
214 time_point m_TimeBeg;
215};
216
217struct PoolTestThreadResult
218{
219 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
220 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
221 size_t AllocationCount, DeallocationCount;
222 size_t LostAllocationCount, LostAllocationTotalSize;
223 size_t FailedAllocationCount, FailedAllocationTotalSize;
224};
225
226class AllocationTimeRegisterObj : public TimeRegisterObj
227{
228public:
229 AllocationTimeRegisterObj(Result& result) :
230 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
231 {
232 }
233};
234
235class DeallocationTimeRegisterObj : public TimeRegisterObj
236{
237public:
238 DeallocationTimeRegisterObj(Result& result) :
239 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
240 {
241 }
242};
243
244class PoolAllocationTimeRegisterObj : public TimeRegisterObj
245{
246public:
247 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
248 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
249 {
250 }
251};
252
253class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
254{
255public:
256 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
257 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
258 {
259 }
260};
261
Adam Sawicki33d2ce72018-08-27 13:59:13 +0200262static void CurrentTimeToStr(std::string& out)
263{
264 time_t rawTime; time(&rawTime);
265 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
266 char timeStr[128];
267 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
268 out = timeStr;
269}
270
Adam Sawickib8333fb2018-03-13 16:15:53 +0100271VkResult MainTest(Result& outResult, const Config& config)
272{
273 assert(config.ThreadCount > 0);
274
275 InitResult(outResult);
276
277 RandomNumberGenerator mainRand{config.RandSeed};
278
279 time_point timeBeg = std::chrono::high_resolution_clock::now();
280
281 std::atomic<size_t> allocationCount = 0;
282 VkResult res = VK_SUCCESS;
283
284 uint32_t memUsageProbabilitySum =
285 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
286 config.MemUsageProbability[2] + config.MemUsageProbability[3];
287 assert(memUsageProbabilitySum > 0);
288
289 uint32_t allocationSizeProbabilitySum = std::accumulate(
290 config.AllocationSizes.begin(),
291 config.AllocationSizes.end(),
292 0u,
293 [](uint32_t sum, const AllocationSize& allocSize) {
294 return sum + allocSize.Probability;
295 });
296
297 struct Allocation
298 {
299 VkBuffer Buffer;
300 VkImage Image;
301 VmaAllocation Alloc;
302 };
303
304 std::vector<Allocation> commonAllocations;
305 std::mutex commonAllocationsMutex;
306
307 auto Allocate = [&](
308 VkDeviceSize bufferSize,
309 const VkExtent2D imageExtent,
310 RandomNumberGenerator& localRand,
311 VkDeviceSize& totalAllocatedBytes,
312 std::vector<Allocation>& allocations) -> VkResult
313 {
314 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
315
316 uint32_t memUsageIndex = 0;
317 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
318 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
319 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
320
321 VmaAllocationCreateInfo memReq = {};
322 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
Adam Sawicki0667e332018-08-24 17:26:44 +0200323 memReq.flags |= config.AllocationStrategy;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100324
325 Allocation allocation = {};
326 VmaAllocationInfo allocationInfo;
327
328 // Buffer
329 if(bufferSize > 0)
330 {
331 assert(imageExtent.width == 0);
332 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
333 bufferInfo.size = bufferSize;
334 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
335
336 {
337 AllocationTimeRegisterObj timeRegisterObj{outResult};
338 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
339 }
340 }
341 // Image
342 else
343 {
344 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
345 imageInfo.imageType = VK_IMAGE_TYPE_2D;
346 imageInfo.extent.width = imageExtent.width;
347 imageInfo.extent.height = imageExtent.height;
348 imageInfo.extent.depth = 1;
349 imageInfo.mipLevels = 1;
350 imageInfo.arrayLayers = 1;
351 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
352 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
353 VK_IMAGE_TILING_OPTIMAL :
354 VK_IMAGE_TILING_LINEAR;
355 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
356 switch(memReq.usage)
357 {
358 case VMA_MEMORY_USAGE_GPU_ONLY:
359 switch(localRand.Generate() % 3)
360 {
361 case 0:
362 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
363 break;
364 case 1:
365 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
366 break;
367 case 2:
368 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
369 break;
370 }
371 break;
372 case VMA_MEMORY_USAGE_CPU_ONLY:
373 case VMA_MEMORY_USAGE_CPU_TO_GPU:
374 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
375 break;
376 case VMA_MEMORY_USAGE_GPU_TO_CPU:
377 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
378 break;
379 }
380 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
381 imageInfo.flags = 0;
382
383 {
384 AllocationTimeRegisterObj timeRegisterObj{outResult};
385 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
386 }
387 }
388
389 if(res == VK_SUCCESS)
390 {
391 ++allocationCount;
392 totalAllocatedBytes += allocationInfo.size;
393 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
394 if(useCommonAllocations)
395 {
396 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
397 commonAllocations.push_back(allocation);
398 }
399 else
400 allocations.push_back(allocation);
401 }
402 else
403 {
Adam Sawickib8d34d52018-10-03 17:41:20 +0200404 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100405 }
406 return res;
407 };
408
409 auto GetNextAllocationSize = [&](
410 VkDeviceSize& outBufSize,
411 VkExtent2D& outImageSize,
412 RandomNumberGenerator& localRand)
413 {
414 outBufSize = 0;
415 outImageSize = {0, 0};
416
417 uint32_t allocSizeIndex = 0;
418 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
419 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
420 r -= config.AllocationSizes[allocSizeIndex++].Probability;
421
422 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
423 if(allocSize.BufferSizeMax > 0)
424 {
425 assert(allocSize.ImageSizeMax == 0);
426 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
427 outBufSize = allocSize.BufferSizeMin;
428 else
429 {
430 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
431 outBufSize = outBufSize / 16 * 16;
432 }
433 }
434 else
435 {
436 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
437 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
438 else
439 {
440 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
441 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
442 }
443 }
444 };
445
446 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
447 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
448
449 auto ThreadProc = [&](uint32_t randSeed) -> void
450 {
451 RandomNumberGenerator threadRand(randSeed);
452 VkDeviceSize threadTotalAllocatedBytes = 0;
453 std::vector<Allocation> threadAllocations;
454 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
455 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
456 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
457
458 // BEGIN ALLOCATIONS
459 for(;;)
460 {
461 VkDeviceSize bufferSize = 0;
462 VkExtent2D imageExtent = {};
463 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
464 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
465 threadBeginBytesToAllocate)
466 {
467 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
468 break;
469 }
470 else
471 break;
472 }
473
474 // ADDITIONAL ALLOCATIONS AND FREES
475 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
476 {
477 VkDeviceSize bufferSize = 0;
478 VkExtent2D imageExtent = {};
479 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
480
481 // true = allocate, false = free
482 bool allocate = threadRand.Generate() % 2 != 0;
483
484 if(allocate)
485 {
486 if(threadTotalAllocatedBytes +
487 bufferSize +
488 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
489 threadMaxBytesToAllocate)
490 {
491 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
492 break;
493 }
494 }
495 else
496 {
497 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
498 if(useCommonAllocations)
499 {
500 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
501 if(!commonAllocations.empty())
502 {
503 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
504 VmaAllocationInfo allocationInfo;
505 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
506 if(threadTotalAllocatedBytes >= allocationInfo.size)
507 {
508 DeallocationTimeRegisterObj timeRegisterObj{outResult};
509 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
510 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
511 else
512 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
513 threadTotalAllocatedBytes -= allocationInfo.size;
514 commonAllocations.erase(commonAllocations.begin() + indexToFree);
515 }
516 }
517 }
518 else
519 {
520 if(!threadAllocations.empty())
521 {
522 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
523 VmaAllocationInfo allocationInfo;
524 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
525 if(threadTotalAllocatedBytes >= allocationInfo.size)
526 {
527 DeallocationTimeRegisterObj timeRegisterObj{outResult};
528 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
529 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
530 else
531 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
532 threadTotalAllocatedBytes -= allocationInfo.size;
533 threadAllocations.erase(threadAllocations.begin() + indexToFree);
534 }
535 }
536 }
537 }
538 }
539
540 ++numThreadsReachedMaxAllocations;
541
542 WaitForSingleObject(threadsFinishEvent, INFINITE);
543
544 // DEALLOCATION
545 while(!threadAllocations.empty())
546 {
547 size_t indexToFree = 0;
548 switch(config.FreeOrder)
549 {
550 case FREE_ORDER::FORWARD:
551 indexToFree = 0;
552 break;
553 case FREE_ORDER::BACKWARD:
554 indexToFree = threadAllocations.size() - 1;
555 break;
556 case FREE_ORDER::RANDOM:
557 indexToFree = mainRand.Generate() % threadAllocations.size();
558 break;
559 }
560
561 {
562 DeallocationTimeRegisterObj timeRegisterObj{outResult};
563 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
564 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
565 else
566 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
567 }
568 threadAllocations.erase(threadAllocations.begin() + indexToFree);
569 }
570 };
571
572 uint32_t threadRandSeed = mainRand.Generate();
573 std::vector<std::thread> bkgThreads;
574 for(size_t i = 0; i < config.ThreadCount; ++i)
575 {
576 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
577 }
578
579 // Wait for threads reached max allocations
580 while(numThreadsReachedMaxAllocations < config.ThreadCount)
581 Sleep(0);
582
583 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
584 VmaStats vmaStats = {};
585 vmaCalculateStats(g_hAllocator, &vmaStats);
586 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
587 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
588 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
589
590 // Signal threads to deallocate
591 SetEvent(threadsFinishEvent);
592
593 // Wait for threads finished
594 for(size_t i = 0; i < bkgThreads.size(); ++i)
595 bkgThreads[i].join();
596 bkgThreads.clear();
597
598 CloseHandle(threadsFinishEvent);
599
600 // Deallocate remaining common resources
601 while(!commonAllocations.empty())
602 {
603 size_t indexToFree = 0;
604 switch(config.FreeOrder)
605 {
606 case FREE_ORDER::FORWARD:
607 indexToFree = 0;
608 break;
609 case FREE_ORDER::BACKWARD:
610 indexToFree = commonAllocations.size() - 1;
611 break;
612 case FREE_ORDER::RANDOM:
613 indexToFree = mainRand.Generate() % commonAllocations.size();
614 break;
615 }
616
617 {
618 DeallocationTimeRegisterObj timeRegisterObj{outResult};
619 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
620 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
621 else
622 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
623 }
624 commonAllocations.erase(commonAllocations.begin() + indexToFree);
625 }
626
627 if(allocationCount)
628 {
629 outResult.AllocationTimeAvg /= allocationCount;
630 outResult.DeallocationTimeAvg /= allocationCount;
631 }
632
633 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
634
635 return res;
636}
637
Adam Sawickie44c6262018-06-15 14:30:39 +0200638static void SaveAllocatorStatsToFile(const wchar_t* filePath)
Adam Sawickib8333fb2018-03-13 16:15:53 +0100639{
640 char* stats;
Adam Sawickie44c6262018-06-15 14:30:39 +0200641 vmaBuildStatsString(g_hAllocator, &stats, VK_TRUE);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100642 SaveFile(filePath, stats, strlen(stats));
Adam Sawickie44c6262018-06-15 14:30:39 +0200643 vmaFreeStatsString(g_hAllocator, stats);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100644}
645
646struct AllocInfo
647{
648 VmaAllocation m_Allocation;
649 VkBuffer m_Buffer;
650 VkImage m_Image;
651 uint32_t m_StartValue;
652 union
653 {
654 VkBufferCreateInfo m_BufferInfo;
655 VkImageCreateInfo m_ImageInfo;
656 };
657};
658
659static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
660{
661 outMemReq = {};
662 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
663 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
664}
665
666static void CreateBuffer(
667 VmaPool pool,
668 const VkBufferCreateInfo& bufCreateInfo,
669 bool persistentlyMapped,
670 AllocInfo& outAllocInfo)
671{
672 outAllocInfo = {};
673 outAllocInfo.m_BufferInfo = bufCreateInfo;
674
675 VmaAllocationCreateInfo allocCreateInfo = {};
676 allocCreateInfo.pool = pool;
677 if(persistentlyMapped)
678 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
679
680 VmaAllocationInfo vmaAllocInfo = {};
681 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
682
683 // Setup StartValue and fill.
684 {
685 outAllocInfo.m_StartValue = (uint32_t)rand();
686 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +0200687 TEST((data != nullptr) == persistentlyMapped);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100688 if(!persistentlyMapped)
689 {
690 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
691 }
692
693 uint32_t value = outAllocInfo.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +0200694 TEST(bufCreateInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100695 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
696 data[i] = value++;
697
698 if(!persistentlyMapped)
699 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
700 }
701}
702
703static void CreateAllocation(AllocInfo& outAllocation, VmaAllocator allocator)
704{
705 outAllocation.m_Allocation = nullptr;
706 outAllocation.m_Buffer = nullptr;
707 outAllocation.m_Image = nullptr;
708 outAllocation.m_StartValue = (uint32_t)rand();
709
710 VmaAllocationCreateInfo vmaMemReq;
711 GetMemReq(vmaMemReq);
712
713 VmaAllocationInfo allocInfo;
714
715 const bool isBuffer = true;//(rand() & 0x1) != 0;
716 const bool isLarge = (rand() % 16) == 0;
717 if(isBuffer)
718 {
719 const uint32_t bufferSize = isLarge ?
720 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
721 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
722
723 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
724 bufferInfo.size = bufferSize;
725 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
726
727 VkResult res = vmaCreateBuffer(allocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
728 outAllocation.m_BufferInfo = bufferInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +0200729 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100730 }
731 else
732 {
733 const uint32_t imageSizeX = isLarge ?
734 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
735 rand() % 1024 + 1; // 1 ... 1024
736 const uint32_t imageSizeY = isLarge ?
737 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
738 rand() % 1024 + 1; // 1 ... 1024
739
740 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
741 imageInfo.imageType = VK_IMAGE_TYPE_2D;
742 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
743 imageInfo.extent.width = imageSizeX;
744 imageInfo.extent.height = imageSizeY;
745 imageInfo.extent.depth = 1;
746 imageInfo.mipLevels = 1;
747 imageInfo.arrayLayers = 1;
748 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
749 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
750 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
751 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
752
753 VkResult res = vmaCreateImage(allocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
754 outAllocation.m_ImageInfo = imageInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +0200755 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100756 }
757
758 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
759 if(allocInfo.pMappedData == nullptr)
760 {
761 VkResult res = vmaMapMemory(allocator, outAllocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200762 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100763 }
764
765 uint32_t value = outAllocation.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +0200766 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100767 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
768 data[i] = value++;
769
770 if(allocInfo.pMappedData == nullptr)
771 vmaUnmapMemory(allocator, outAllocation.m_Allocation);
772}
773
774static void DestroyAllocation(const AllocInfo& allocation)
775{
776 if(allocation.m_Buffer)
777 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
778 else
779 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
780}
781
782static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
783{
784 for(size_t i = allocations.size(); i--; )
785 DestroyAllocation(allocations[i]);
786 allocations.clear();
787}
788
789static void ValidateAllocationData(const AllocInfo& allocation)
790{
791 VmaAllocationInfo allocInfo;
792 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
793
794 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
795 if(allocInfo.pMappedData == nullptr)
796 {
797 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200798 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100799 }
800
801 uint32_t value = allocation.m_StartValue;
802 bool ok = true;
803 size_t i;
Adam Sawickib8d34d52018-10-03 17:41:20 +0200804 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100805 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
806 {
807 if(data[i] != value++)
808 {
809 ok = false;
810 break;
811 }
812 }
Adam Sawickib8d34d52018-10-03 17:41:20 +0200813 TEST(ok);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100814
815 if(allocInfo.pMappedData == nullptr)
816 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
817}
818
819static void RecreateAllocationResource(AllocInfo& allocation)
820{
821 VmaAllocationInfo allocInfo;
822 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
823
824 if(allocation.m_Buffer)
825 {
826 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, nullptr);
827
828 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, nullptr, &allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200829 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100830
831 // Just to silence validation layer warnings.
832 VkMemoryRequirements vkMemReq;
833 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200834 TEST(vkMemReq.size == allocation.m_BufferInfo.size);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100835
836 res = vkBindBufferMemory(g_hDevice, allocation.m_Buffer, allocInfo.deviceMemory, allocInfo.offset);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200837 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100838 }
839 else
840 {
841 vkDestroyImage(g_hDevice, allocation.m_Image, nullptr);
842
843 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, nullptr, &allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200844 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100845
846 // Just to silence validation layer warnings.
847 VkMemoryRequirements vkMemReq;
848 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
849
850 res = vkBindImageMemory(g_hDevice, allocation.m_Image, allocInfo.deviceMemory, allocInfo.offset);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200851 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100852 }
853}
854
855static void Defragment(AllocInfo* allocs, size_t allocCount,
856 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
857 VmaDefragmentationStats* defragmentationStats = nullptr)
858{
859 std::vector<VmaAllocation> vmaAllocs(allocCount);
860 for(size_t i = 0; i < allocCount; ++i)
861 vmaAllocs[i] = allocs[i].m_Allocation;
862
863 std::vector<VkBool32> allocChanged(allocCount);
864
865 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
866 defragmentationInfo, defragmentationStats) );
867
868 for(size_t i = 0; i < allocCount; ++i)
869 {
870 if(allocChanged[i])
871 {
872 RecreateAllocationResource(allocs[i]);
873 }
874 }
875}
876
877static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
878{
879 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
880 ValidateAllocationData(allocInfo);
881 });
882}
883
884void TestDefragmentationSimple()
885{
886 wprintf(L"Test defragmentation simple\n");
887
888 RandomNumberGenerator rand(667);
889
890 const VkDeviceSize BUF_SIZE = 0x10000;
891 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
892
893 const VkDeviceSize MIN_BUF_SIZE = 32;
894 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
895 auto RandomBufSize = [&]() -> VkDeviceSize {
896 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
897 };
898
899 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
900 bufCreateInfo.size = BUF_SIZE;
901 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
902
903 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
904 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
905
906 uint32_t memTypeIndex = UINT32_MAX;
907 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
908
909 VmaPoolCreateInfo poolCreateInfo = {};
910 poolCreateInfo.blockSize = BLOCK_SIZE;
911 poolCreateInfo.memoryTypeIndex = memTypeIndex;
912
913 VmaPool pool;
914 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
915
916 std::vector<AllocInfo> allocations;
917
918 // persistentlyMappedOption = 0 - not persistently mapped.
919 // persistentlyMappedOption = 1 - persistently mapped.
920 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
921 {
922 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
923 const bool persistentlyMapped = persistentlyMappedOption != 0;
924
925 // # Test 1
926 // Buffers of fixed size.
927 // Fill 2 blocks. Remove odd buffers. Defragment everything.
928 // Expected result: at least 1 block freed.
929 {
930 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
931 {
932 AllocInfo allocInfo;
933 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
934 allocations.push_back(allocInfo);
935 }
936
937 for(size_t i = 1; i < allocations.size(); ++i)
938 {
939 DestroyAllocation(allocations[i]);
940 allocations.erase(allocations.begin() + i);
941 }
942
943 VmaDefragmentationStats defragStats;
944 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200945 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
946 TEST(defragStats.deviceMemoryBlocksFreed >= 1);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100947
948 ValidateAllocationsData(allocations.data(), allocations.size());
949
950 DestroyAllAllocations(allocations);
951 }
952
953 // # Test 2
954 // Buffers of fixed size.
955 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
956 // Expected result: Each of 4 interations makes some progress.
957 {
958 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
959 {
960 AllocInfo allocInfo;
961 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
962 allocations.push_back(allocInfo);
963 }
964
965 for(size_t i = 1; i < allocations.size(); ++i)
966 {
967 DestroyAllocation(allocations[i]);
968 allocations.erase(allocations.begin() + i);
969 }
970
971 VmaDefragmentationInfo defragInfo = {};
972 defragInfo.maxAllocationsToMove = 1;
973 defragInfo.maxBytesToMove = BUF_SIZE;
974
975 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
976 {
977 VmaDefragmentationStats defragStats;
978 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +0200979 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100980 }
981
982 ValidateAllocationsData(allocations.data(), allocations.size());
983
984 DestroyAllAllocations(allocations);
985 }
986
987 // # Test 3
988 // Buffers of variable size.
989 // Create a number of buffers. Remove some percent of them.
990 // Defragment while having some percent of them unmovable.
991 // Expected result: Just simple validation.
992 {
993 for(size_t i = 0; i < 100; ++i)
994 {
995 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
996 localBufCreateInfo.size = RandomBufSize();
997
998 AllocInfo allocInfo;
999 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1000 allocations.push_back(allocInfo);
1001 }
1002
1003 const uint32_t percentToDelete = 60;
1004 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
1005 for(size_t i = 0; i < numberToDelete; ++i)
1006 {
1007 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
1008 DestroyAllocation(allocations[indexToDelete]);
1009 allocations.erase(allocations.begin() + indexToDelete);
1010 }
1011
1012 // Non-movable allocations will be at the beginning of allocations array.
1013 const uint32_t percentNonMovable = 20;
1014 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
1015 for(size_t i = 0; i < numberNonMovable; ++i)
1016 {
1017 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
1018 if(indexNonMovable != i)
1019 std::swap(allocations[i], allocations[indexNonMovable]);
1020 }
1021
1022 VmaDefragmentationStats defragStats;
1023 Defragment(
1024 allocations.data() + numberNonMovable,
1025 allocations.size() - numberNonMovable,
1026 nullptr, &defragStats);
1027
1028 ValidateAllocationsData(allocations.data(), allocations.size());
1029
1030 DestroyAllAllocations(allocations);
1031 }
1032 }
1033
1034 vmaDestroyPool(g_hAllocator, pool);
1035}
1036
1037void TestDefragmentationFull()
1038{
1039 std::vector<AllocInfo> allocations;
1040
1041 // Create initial allocations.
1042 for(size_t i = 0; i < 400; ++i)
1043 {
1044 AllocInfo allocation;
1045 CreateAllocation(allocation, g_hAllocator);
1046 allocations.push_back(allocation);
1047 }
1048
1049 // Delete random allocations
1050 const size_t allocationsToDeletePercent = 80;
1051 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
1052 for(size_t i = 0; i < allocationsToDelete; ++i)
1053 {
1054 size_t index = (size_t)rand() % allocations.size();
1055 DestroyAllocation(allocations[index]);
1056 allocations.erase(allocations.begin() + index);
1057 }
1058
1059 for(size_t i = 0; i < allocations.size(); ++i)
1060 ValidateAllocationData(allocations[i]);
1061
Adam Sawicki0667e332018-08-24 17:26:44 +02001062 //SaveAllocatorStatsToFile(L"Before.csv");
Adam Sawickib8333fb2018-03-13 16:15:53 +01001063
1064 {
1065 std::vector<VmaAllocation> vmaAllocations(allocations.size());
1066 for(size_t i = 0; i < allocations.size(); ++i)
1067 vmaAllocations[i] = allocations[i].m_Allocation;
1068
1069 const size_t nonMovablePercent = 0;
1070 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
1071 for(size_t i = 0; i < nonMovableCount; ++i)
1072 {
1073 size_t index = (size_t)rand() % vmaAllocations.size();
1074 vmaAllocations.erase(vmaAllocations.begin() + index);
1075 }
1076
1077 const uint32_t defragCount = 1;
1078 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1079 {
1080 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1081
1082 VmaDefragmentationInfo defragmentationInfo;
1083 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1084 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1085
1086 wprintf(L"Defragmentation #%u\n", defragIndex);
1087
1088 time_point begTime = std::chrono::high_resolution_clock::now();
1089
1090 VmaDefragmentationStats stats;
1091 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001092 TEST(res >= 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001093
1094 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1095
1096 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1097 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1098 wprintf(L"Time: %.2f s\n", defragmentDuration);
1099
1100 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1101 {
1102 if(allocationsChanged[i])
1103 {
1104 RecreateAllocationResource(allocations[i]);
1105 }
1106 }
1107
1108 for(size_t i = 0; i < allocations.size(); ++i)
1109 ValidateAllocationData(allocations[i]);
1110
Adam Sawicki0667e332018-08-24 17:26:44 +02001111 //wchar_t fileName[MAX_PATH];
1112 //swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
1113 //SaveAllocatorStatsToFile(fileName);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001114 }
1115 }
1116
1117 // Destroy all remaining allocations.
1118 DestroyAllAllocations(allocations);
1119}
1120
1121static void TestUserData()
1122{
1123 VkResult res;
1124
1125 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1126 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1127 bufCreateInfo.size = 0x10000;
1128
1129 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
1130 {
1131 // Opaque pointer
1132 {
1133
1134 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
1135 void* pointerToSomething = &res;
1136
1137 VmaAllocationCreateInfo allocCreateInfo = {};
1138 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1139 allocCreateInfo.pUserData = numberAsPointer;
1140 if(testIndex == 1)
1141 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1142
1143 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1144 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001145 TEST(res == VK_SUCCESS);
1146 TEST(allocInfo.pUserData = numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001147
1148 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001149 TEST(allocInfo.pUserData == numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001150
1151 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
1152 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001153 TEST(allocInfo.pUserData == pointerToSomething);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001154
1155 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1156 }
1157
1158 // String
1159 {
1160 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
1161 const char* name2 = "2";
1162 const size_t name1Len = strlen(name1);
1163
1164 char* name1Buf = new char[name1Len + 1];
1165 strcpy_s(name1Buf, name1Len + 1, name1);
1166
1167 VmaAllocationCreateInfo allocCreateInfo = {};
1168 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1169 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1170 allocCreateInfo.pUserData = name1Buf;
1171 if(testIndex == 1)
1172 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1173
1174 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1175 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001176 TEST(res == VK_SUCCESS);
1177 TEST(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
1178 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001179
1180 delete[] name1Buf;
1181
1182 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001183 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001184
1185 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
1186 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001187 TEST(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001188
1189 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
1190 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001191 TEST(allocInfo.pUserData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001192
1193 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1194 }
1195 }
1196}
1197
Adam Sawicki370ab182018-11-08 16:31:00 +01001198static void TestInvalidAllocations()
1199{
1200 VkResult res;
1201
1202 VmaAllocationCreateInfo allocCreateInfo = {};
1203 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1204
1205 // Try to allocate 0 bytes.
1206 {
1207 VkMemoryRequirements memReq = {};
1208 memReq.size = 0; // !!!
1209 memReq.alignment = 4;
1210 memReq.memoryTypeBits = UINT32_MAX;
1211 VmaAllocation alloc = VK_NULL_HANDLE;
1212 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
1213 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && alloc == VK_NULL_HANDLE);
1214 }
1215
1216 // Try to create buffer with size = 0.
1217 {
1218 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1219 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1220 bufCreateInfo.size = 0; // !!!
1221 VkBuffer buf = VK_NULL_HANDLE;
1222 VmaAllocation alloc = VK_NULL_HANDLE;
1223 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
1224 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && buf == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
1225 }
1226
1227 // Try to create image with one dimension = 0.
1228 {
1229 VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1230 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
1231 imageCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
1232 imageCreateInfo.extent.width = 128;
1233 imageCreateInfo.extent.height = 0; // !!!
1234 imageCreateInfo.extent.depth = 1;
1235 imageCreateInfo.mipLevels = 1;
1236 imageCreateInfo.arrayLayers = 1;
1237 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1238 imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
1239 imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1240 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
1241 VkImage image = VK_NULL_HANDLE;
1242 VmaAllocation alloc = VK_NULL_HANDLE;
1243 res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &image, &alloc, nullptr);
1244 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && image == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
1245 }
1246}
1247
Adam Sawickib8333fb2018-03-13 16:15:53 +01001248static void TestMemoryRequirements()
1249{
1250 VkResult res;
1251 VkBuffer buf;
1252 VmaAllocation alloc;
1253 VmaAllocationInfo allocInfo;
1254
1255 const VkPhysicalDeviceMemoryProperties* memProps;
1256 vmaGetMemoryProperties(g_hAllocator, &memProps);
1257
1258 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1259 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1260 bufInfo.size = 128;
1261
1262 VmaAllocationCreateInfo allocCreateInfo = {};
1263
1264 // No requirements.
1265 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001266 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001267 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1268
1269 // Usage.
1270 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1271 allocCreateInfo.requiredFlags = 0;
1272 allocCreateInfo.preferredFlags = 0;
1273 allocCreateInfo.memoryTypeBits = UINT32_MAX;
1274
1275 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001276 TEST(res == VK_SUCCESS);
1277 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001278 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1279
1280 // Required flags, preferred flags.
1281 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
1282 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1283 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1284 allocCreateInfo.memoryTypeBits = 0;
1285
1286 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001287 TEST(res == VK_SUCCESS);
1288 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1289 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001290 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1291
1292 // memoryTypeBits.
1293 const uint32_t memType = allocInfo.memoryType;
1294 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1295 allocCreateInfo.requiredFlags = 0;
1296 allocCreateInfo.preferredFlags = 0;
1297 allocCreateInfo.memoryTypeBits = 1u << memType;
1298
1299 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001300 TEST(res == VK_SUCCESS);
1301 TEST(allocInfo.memoryType == memType);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001302 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1303
1304}
1305
1306static void TestBasics()
1307{
1308 VkResult res;
1309
1310 TestMemoryRequirements();
1311
1312 // Lost allocation
1313 {
1314 VmaAllocation alloc = VK_NULL_HANDLE;
1315 vmaCreateLostAllocation(g_hAllocator, &alloc);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001316 TEST(alloc != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001317
1318 VmaAllocationInfo allocInfo;
1319 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001320 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
1321 TEST(allocInfo.size == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001322
1323 vmaFreeMemory(g_hAllocator, alloc);
1324 }
1325
1326 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
1327 {
1328 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1329 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1330 bufCreateInfo.size = 128;
1331
1332 VmaAllocationCreateInfo allocCreateInfo = {};
1333 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1334 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1335
1336 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1337 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001338 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001339
1340 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1341
1342 // Same with OWN_MEMORY.
1343 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1344
1345 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001346 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001347
1348 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1349 }
1350
1351 TestUserData();
Adam Sawicki370ab182018-11-08 16:31:00 +01001352
1353 TestInvalidAllocations();
Adam Sawickib8333fb2018-03-13 16:15:53 +01001354}
1355
1356void TestHeapSizeLimit()
1357{
1358 const VkDeviceSize HEAP_SIZE_LIMIT = 1ull * 1024 * 1024 * 1024; // 1 GB
1359 const VkDeviceSize BLOCK_SIZE = 128ull * 1024 * 1024; // 128 MB
1360
1361 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
1362 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
1363 {
1364 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
1365 }
1366
1367 VmaAllocatorCreateInfo allocatorCreateInfo = {};
1368 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
1369 allocatorCreateInfo.device = g_hDevice;
1370 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
1371
1372 VmaAllocator hAllocator;
1373 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001374 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001375
1376 struct Item
1377 {
1378 VkBuffer hBuf;
1379 VmaAllocation hAlloc;
1380 };
1381 std::vector<Item> items;
1382
1383 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1384 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1385
1386 // 1. Allocate two blocks of Own Memory, half the size of BLOCK_SIZE.
1387 VmaAllocationInfo ownAllocInfo;
1388 {
1389 VmaAllocationCreateInfo allocCreateInfo = {};
1390 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1391 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1392
1393 bufCreateInfo.size = BLOCK_SIZE / 2;
1394
1395 for(size_t i = 0; i < 2; ++i)
1396 {
1397 Item item;
1398 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &ownAllocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001399 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001400 items.push_back(item);
1401 }
1402 }
1403
1404 // Create pool to make sure allocations must be out of this memory type.
1405 VmaPoolCreateInfo poolCreateInfo = {};
1406 poolCreateInfo.memoryTypeIndex = ownAllocInfo.memoryType;
1407 poolCreateInfo.blockSize = BLOCK_SIZE;
1408
1409 VmaPool hPool;
1410 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001411 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001412
1413 // 2. Allocate normal buffers from all the remaining memory.
1414 {
1415 VmaAllocationCreateInfo allocCreateInfo = {};
1416 allocCreateInfo.pool = hPool;
1417
1418 bufCreateInfo.size = BLOCK_SIZE / 2;
1419
1420 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
1421 for(size_t i = 0; i < bufCount; ++i)
1422 {
1423 Item item;
1424 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001425 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001426 items.push_back(item);
1427 }
1428 }
1429
1430 // 3. Allocation of one more (even small) buffer should fail.
1431 {
1432 VmaAllocationCreateInfo allocCreateInfo = {};
1433 allocCreateInfo.pool = hPool;
1434
1435 bufCreateInfo.size = 128;
1436
1437 VkBuffer hBuf;
1438 VmaAllocation hAlloc;
1439 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001440 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001441 }
1442
1443 // Destroy everything.
1444 for(size_t i = items.size(); i--; )
1445 {
1446 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
1447 }
1448
1449 vmaDestroyPool(hAllocator, hPool);
1450
1451 vmaDestroyAllocator(hAllocator);
1452}
1453
Adam Sawicki212a4a62018-06-14 15:44:45 +02001454#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02001455static void TestDebugMargin()
1456{
1457 if(VMA_DEBUG_MARGIN == 0)
1458 {
1459 return;
1460 }
1461
1462 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02001463 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02001464
1465 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02001466 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02001467
1468 // Create few buffers of different size.
1469 const size_t BUF_COUNT = 10;
1470 BufferInfo buffers[BUF_COUNT];
1471 VmaAllocationInfo allocInfo[BUF_COUNT];
1472 for(size_t i = 0; i < 10; ++i)
1473 {
1474 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02001475 // Last one will be mapped.
1476 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02001477
1478 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001479 TEST(res == VK_SUCCESS);
Adam Sawicki73b16652018-06-11 16:39:25 +02001480 // Margin is preserved also at the beginning of a block.
Adam Sawickib8d34d52018-10-03 17:41:20 +02001481 TEST(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02001482
1483 if(i == BUF_COUNT - 1)
1484 {
1485 // Fill with data.
Adam Sawickib8d34d52018-10-03 17:41:20 +02001486 TEST(allocInfo[i].pMappedData != nullptr);
Adam Sawicki212a4a62018-06-14 15:44:45 +02001487 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
1488 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
1489 }
Adam Sawicki73b16652018-06-11 16:39:25 +02001490 }
1491
1492 // Check if their offsets preserve margin between them.
1493 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
1494 {
1495 if(lhs.deviceMemory != rhs.deviceMemory)
1496 {
1497 return lhs.deviceMemory < rhs.deviceMemory;
1498 }
1499 return lhs.offset < rhs.offset;
1500 });
1501 for(size_t i = 1; i < BUF_COUNT; ++i)
1502 {
1503 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
1504 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02001505 TEST(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
Adam Sawicki73b16652018-06-11 16:39:25 +02001506 }
1507 }
1508
Adam Sawicki212a4a62018-06-14 15:44:45 +02001509 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001510 TEST(res == VK_SUCCESS);
Adam Sawicki212a4a62018-06-14 15:44:45 +02001511
Adam Sawicki73b16652018-06-11 16:39:25 +02001512 // Destroy all buffers.
1513 for(size_t i = BUF_COUNT; i--; )
1514 {
1515 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
1516 }
1517}
Adam Sawicki212a4a62018-06-14 15:44:45 +02001518#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02001519
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001520static void TestLinearAllocator()
1521{
1522 wprintf(L"Test linear allocator\n");
1523
1524 RandomNumberGenerator rand{645332};
1525
1526 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1527 sampleBufCreateInfo.size = 1024; // Whatever.
1528 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1529
1530 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1531 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1532
1533 VmaPoolCreateInfo poolCreateInfo = {};
1534 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001535 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001536
Adam Sawickiee082772018-06-20 17:45:49 +02001537 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001538 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1539 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
1540
1541 VmaPool pool = nullptr;
1542 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001543 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001544
1545 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1546
1547 VmaAllocationCreateInfo allocCreateInfo = {};
1548 allocCreateInfo.pool = pool;
1549
1550 constexpr size_t maxBufCount = 100;
1551 std::vector<BufferInfo> bufInfo;
1552
1553 constexpr VkDeviceSize bufSizeMin = 16;
1554 constexpr VkDeviceSize bufSizeMax = 1024;
1555 VmaAllocationInfo allocInfo;
1556 VkDeviceSize prevOffset = 0;
1557
1558 // Test one-time free.
1559 for(size_t i = 0; i < 2; ++i)
1560 {
1561 // Allocate number of buffers of varying size that surely fit into this block.
1562 VkDeviceSize bufSumSize = 0;
1563 for(size_t i = 0; i < maxBufCount; ++i)
1564 {
1565 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1566 BufferInfo newBufInfo;
1567 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1568 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001569 TEST(res == VK_SUCCESS);
1570 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001571 bufInfo.push_back(newBufInfo);
1572 prevOffset = allocInfo.offset;
1573 bufSumSize += bufCreateInfo.size;
1574 }
1575
1576 // Validate pool stats.
1577 VmaPoolStats stats;
1578 vmaGetPoolStats(g_hAllocator, pool, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001579 TEST(stats.size == poolCreateInfo.blockSize);
1580 TEST(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
1581 TEST(stats.allocationCount == bufInfo.size());
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001582
1583 // Destroy the buffers in random order.
1584 while(!bufInfo.empty())
1585 {
1586 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1587 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1588 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1589 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1590 }
1591 }
1592
1593 // Test stack.
1594 {
1595 // Allocate number of buffers of varying size that surely fit into this block.
1596 for(size_t i = 0; i < maxBufCount; ++i)
1597 {
1598 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1599 BufferInfo newBufInfo;
1600 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1601 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001602 TEST(res == VK_SUCCESS);
1603 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001604 bufInfo.push_back(newBufInfo);
1605 prevOffset = allocInfo.offset;
1606 }
1607
1608 // Destroy few buffers from top of the stack.
1609 for(size_t i = 0; i < maxBufCount / 5; ++i)
1610 {
1611 const BufferInfo& currBufInfo = bufInfo.back();
1612 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1613 bufInfo.pop_back();
1614 }
1615
1616 // Create some more
1617 for(size_t i = 0; i < maxBufCount / 5; ++i)
1618 {
1619 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1620 BufferInfo newBufInfo;
1621 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1622 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001623 TEST(res == VK_SUCCESS);
1624 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001625 bufInfo.push_back(newBufInfo);
1626 prevOffset = allocInfo.offset;
1627 }
1628
1629 // Destroy the buffers in reverse order.
1630 while(!bufInfo.empty())
1631 {
1632 const BufferInfo& currBufInfo = bufInfo.back();
1633 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1634 bufInfo.pop_back();
1635 }
1636 }
1637
Adam Sawickiee082772018-06-20 17:45:49 +02001638 // Test ring buffer.
1639 {
1640 // Allocate number of buffers that surely fit into this block.
1641 bufCreateInfo.size = bufSizeMax;
1642 for(size_t i = 0; i < maxBufCount; ++i)
1643 {
1644 BufferInfo newBufInfo;
1645 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1646 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001647 TEST(res == VK_SUCCESS);
1648 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawickiee082772018-06-20 17:45:49 +02001649 bufInfo.push_back(newBufInfo);
1650 prevOffset = allocInfo.offset;
1651 }
1652
1653 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
1654 const size_t buffersPerIter = maxBufCount / 10 - 1;
1655 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
1656 for(size_t iter = 0; iter < iterCount; ++iter)
1657 {
1658 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1659 {
1660 const BufferInfo& currBufInfo = bufInfo.front();
1661 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1662 bufInfo.erase(bufInfo.begin());
1663 }
1664 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1665 {
1666 BufferInfo newBufInfo;
1667 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1668 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001669 TEST(res == VK_SUCCESS);
Adam Sawickiee082772018-06-20 17:45:49 +02001670 bufInfo.push_back(newBufInfo);
1671 }
1672 }
1673
1674 // Allocate buffers until we reach out-of-memory.
1675 uint32_t debugIndex = 0;
1676 while(res == VK_SUCCESS)
1677 {
1678 BufferInfo newBufInfo;
1679 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1680 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1681 if(res == VK_SUCCESS)
1682 {
1683 bufInfo.push_back(newBufInfo);
1684 }
1685 else
1686 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02001687 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickiee082772018-06-20 17:45:49 +02001688 }
1689 ++debugIndex;
1690 }
1691
1692 // Destroy the buffers in random order.
1693 while(!bufInfo.empty())
1694 {
1695 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1696 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1697 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1698 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1699 }
1700 }
1701
Adam Sawicki680b2252018-08-22 14:47:32 +02001702 // Test double stack.
1703 {
1704 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
1705 VkDeviceSize prevOffsetLower = 0;
1706 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
1707 for(size_t i = 0; i < maxBufCount; ++i)
1708 {
1709 const bool upperAddress = (i % 2) != 0;
1710 if(upperAddress)
1711 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1712 else
1713 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1714 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1715 BufferInfo newBufInfo;
1716 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1717 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001718 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02001719 if(upperAddress)
1720 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02001721 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02001722 prevOffsetUpper = allocInfo.offset;
1723 }
1724 else
1725 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02001726 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02001727 prevOffsetLower = allocInfo.offset;
1728 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02001729 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02001730 bufInfo.push_back(newBufInfo);
1731 }
1732
1733 // Destroy few buffers from top of the stack.
1734 for(size_t i = 0; i < maxBufCount / 5; ++i)
1735 {
1736 const BufferInfo& currBufInfo = bufInfo.back();
1737 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1738 bufInfo.pop_back();
1739 }
1740
1741 // Create some more
1742 for(size_t i = 0; i < maxBufCount / 5; ++i)
1743 {
1744 const bool upperAddress = (i % 2) != 0;
1745 if(upperAddress)
1746 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1747 else
1748 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1749 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1750 BufferInfo newBufInfo;
1751 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1752 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001753 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02001754 bufInfo.push_back(newBufInfo);
1755 }
1756
1757 // Destroy the buffers in reverse order.
1758 while(!bufInfo.empty())
1759 {
1760 const BufferInfo& currBufInfo = bufInfo.back();
1761 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1762 bufInfo.pop_back();
1763 }
1764
1765 // Create buffers on both sides until we reach out of memory.
1766 prevOffsetLower = 0;
1767 prevOffsetUpper = poolCreateInfo.blockSize;
1768 res = VK_SUCCESS;
1769 for(size_t i = 0; res == VK_SUCCESS; ++i)
1770 {
1771 const bool upperAddress = (i % 2) != 0;
1772 if(upperAddress)
1773 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1774 else
1775 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1776 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1777 BufferInfo newBufInfo;
1778 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1779 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1780 if(res == VK_SUCCESS)
1781 {
1782 if(upperAddress)
1783 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02001784 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02001785 prevOffsetUpper = allocInfo.offset;
1786 }
1787 else
1788 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02001789 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02001790 prevOffsetLower = allocInfo.offset;
1791 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02001792 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02001793 bufInfo.push_back(newBufInfo);
1794 }
1795 }
1796
1797 // Destroy the buffers in random order.
1798 while(!bufInfo.empty())
1799 {
1800 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1801 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1802 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1803 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1804 }
1805
1806 // Create buffers on upper side only, constant size, until we reach out of memory.
1807 prevOffsetUpper = poolCreateInfo.blockSize;
1808 res = VK_SUCCESS;
1809 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1810 bufCreateInfo.size = bufSizeMax;
1811 for(size_t i = 0; res == VK_SUCCESS; ++i)
1812 {
1813 BufferInfo newBufInfo;
1814 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1815 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1816 if(res == VK_SUCCESS)
1817 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02001818 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02001819 prevOffsetUpper = allocInfo.offset;
1820 bufInfo.push_back(newBufInfo);
1821 }
1822 }
1823
1824 // Destroy the buffers in reverse order.
1825 while(!bufInfo.empty())
1826 {
1827 const BufferInfo& currBufInfo = bufInfo.back();
1828 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1829 bufInfo.pop_back();
1830 }
1831 }
1832
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001833 // Test ring buffer with lost allocations.
1834 {
1835 // Allocate number of buffers until pool is full.
1836 // Notice CAN_BECOME_LOST flag and call to vmaSetCurrentFrameIndex.
1837 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT;
1838 res = VK_SUCCESS;
1839 for(size_t i = 0; res == VK_SUCCESS; ++i)
1840 {
1841 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1842
1843 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1844
1845 BufferInfo newBufInfo;
1846 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1847 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1848 if(res == VK_SUCCESS)
1849 bufInfo.push_back(newBufInfo);
1850 }
1851
1852 // Free first half of it.
1853 {
1854 const size_t buffersToDelete = bufInfo.size() / 2;
1855 for(size_t i = 0; i < buffersToDelete; ++i)
1856 {
1857 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1858 }
1859 bufInfo.erase(bufInfo.begin(), bufInfo.begin() + buffersToDelete);
1860 }
1861
1862 // Allocate number of buffers until pool is full again.
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001863 // This way we make sure ring buffers wraps around, front in in the middle.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001864 res = VK_SUCCESS;
1865 for(size_t i = 0; res == VK_SUCCESS; ++i)
1866 {
1867 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1868
1869 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1870
1871 BufferInfo newBufInfo;
1872 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1873 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1874 if(res == VK_SUCCESS)
1875 bufInfo.push_back(newBufInfo);
1876 }
1877
1878 VkDeviceSize firstNewOffset;
1879 {
1880 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1881
1882 // Allocate a large buffer with CAN_MAKE_OTHER_LOST.
1883 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1884 bufCreateInfo.size = bufSizeMax;
1885
1886 BufferInfo newBufInfo;
1887 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1888 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001889 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001890 bufInfo.push_back(newBufInfo);
1891 firstNewOffset = allocInfo.offset;
1892
1893 // Make sure at least one buffer from the beginning became lost.
1894 vmaGetAllocationInfo(g_hAllocator, bufInfo[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001895 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001896 }
1897
1898 // Allocate more buffers that CAN_MAKE_OTHER_LOST until we wrap-around with this.
1899 size_t newCount = 1;
1900 for(;;)
1901 {
1902 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1903
1904 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1905
1906 BufferInfo newBufInfo;
1907 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1908 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001909 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001910 bufInfo.push_back(newBufInfo);
1911 ++newCount;
1912 if(allocInfo.offset < firstNewOffset)
1913 break;
1914 }
1915
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001916 // Delete buffers that are lost.
1917 for(size_t i = bufInfo.size(); i--; )
1918 {
1919 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
1920 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1921 {
1922 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1923 bufInfo.erase(bufInfo.begin() + i);
1924 }
1925 }
1926
1927 // Test vmaMakePoolAllocationsLost
1928 {
1929 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1930
1931 size_t lostAllocCount = SIZE_MAX;
1932 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostAllocCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001933 TEST(lostAllocCount > 0);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001934
1935 size_t realLostAllocCount = 0;
1936 for(size_t i = 0; i < bufInfo.size(); ++i)
1937 {
1938 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
1939 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1940 ++realLostAllocCount;
1941 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02001942 TEST(realLostAllocCount == lostAllocCount);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001943 }
1944
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001945 // Destroy all the buffers in forward order.
1946 for(size_t i = 0; i < bufInfo.size(); ++i)
1947 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1948 bufInfo.clear();
1949 }
1950
Adam Sawicki70a683e2018-08-24 15:36:32 +02001951 vmaDestroyPool(g_hAllocator, pool);
1952}
Adam Sawickif799c4f2018-08-23 10:40:30 +02001953
Adam Sawicki70a683e2018-08-24 15:36:32 +02001954static void TestLinearAllocatorMultiBlock()
1955{
1956 wprintf(L"Test linear allocator multi block\n");
1957
1958 RandomNumberGenerator rand{345673};
1959
1960 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1961 sampleBufCreateInfo.size = 1024 * 1024;
1962 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1963
1964 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1965 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1966
1967 VmaPoolCreateInfo poolCreateInfo = {};
1968 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1969 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001970 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02001971
1972 VmaPool pool = nullptr;
1973 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001974 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02001975
1976 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1977
1978 VmaAllocationCreateInfo allocCreateInfo = {};
1979 allocCreateInfo.pool = pool;
1980
1981 std::vector<BufferInfo> bufInfo;
1982 VmaAllocationInfo allocInfo;
1983
1984 // Test one-time free.
1985 {
1986 // Allocate buffers until we move to a second block.
1987 VkDeviceMemory lastMem = VK_NULL_HANDLE;
1988 for(uint32_t i = 0; ; ++i)
1989 {
1990 BufferInfo newBufInfo;
1991 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1992 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001993 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02001994 bufInfo.push_back(newBufInfo);
1995 if(lastMem && allocInfo.deviceMemory != lastMem)
1996 {
1997 break;
1998 }
1999 lastMem = allocInfo.deviceMemory;
2000 }
2001
Adam Sawickib8d34d52018-10-03 17:41:20 +02002002 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002003
2004 // Make sure that pool has now two blocks.
2005 VmaPoolStats poolStats = {};
2006 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002007 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002008
2009 // Destroy all the buffers in random order.
2010 while(!bufInfo.empty())
2011 {
2012 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
2013 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
2014 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2015 bufInfo.erase(bufInfo.begin() + indexToDestroy);
2016 }
2017
2018 // Make sure that pool has now at most one block.
2019 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002020 TEST(poolStats.blockCount <= 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002021 }
2022
2023 // Test stack.
2024 {
2025 // Allocate buffers until we move to a second block.
2026 VkDeviceMemory lastMem = VK_NULL_HANDLE;
2027 for(uint32_t i = 0; ; ++i)
2028 {
2029 BufferInfo newBufInfo;
2030 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2031 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002032 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002033 bufInfo.push_back(newBufInfo);
2034 if(lastMem && allocInfo.deviceMemory != lastMem)
2035 {
2036 break;
2037 }
2038 lastMem = allocInfo.deviceMemory;
2039 }
2040
Adam Sawickib8d34d52018-10-03 17:41:20 +02002041 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002042
2043 // Add few more buffers.
2044 for(uint32_t i = 0; i < 5; ++i)
2045 {
2046 BufferInfo newBufInfo;
2047 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2048 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002049 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002050 bufInfo.push_back(newBufInfo);
2051 }
2052
2053 // Make sure that pool has now two blocks.
2054 VmaPoolStats poolStats = {};
2055 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002056 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002057
2058 // Delete half of buffers, LIFO.
2059 for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
2060 {
2061 const BufferInfo& currBufInfo = bufInfo.back();
2062 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2063 bufInfo.pop_back();
2064 }
2065
2066 // Add one more buffer.
2067 BufferInfo newBufInfo;
2068 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2069 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002070 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002071 bufInfo.push_back(newBufInfo);
2072
2073 // Make sure that pool has now one block.
2074 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002075 TEST(poolStats.blockCount == 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02002076
2077 // Delete all the remaining buffers, LIFO.
2078 while(!bufInfo.empty())
2079 {
2080 const BufferInfo& currBufInfo = bufInfo.back();
2081 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2082 bufInfo.pop_back();
2083 }
Adam Sawickif799c4f2018-08-23 10:40:30 +02002084 }
2085
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002086 vmaDestroyPool(g_hAllocator, pool);
2087}
2088
Adam Sawickifd11d752018-08-22 15:02:10 +02002089static void ManuallyTestLinearAllocator()
2090{
2091 VmaStats origStats;
2092 vmaCalculateStats(g_hAllocator, &origStats);
2093
2094 wprintf(L"Manually test linear allocator\n");
2095
2096 RandomNumberGenerator rand{645332};
2097
2098 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2099 sampleBufCreateInfo.size = 1024; // Whatever.
2100 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2101
2102 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2103 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2104
2105 VmaPoolCreateInfo poolCreateInfo = {};
2106 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002107 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02002108
2109 poolCreateInfo.blockSize = 10 * 1024;
2110 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2111 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2112
2113 VmaPool pool = nullptr;
2114 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002115 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02002116
2117 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
2118
2119 VmaAllocationCreateInfo allocCreateInfo = {};
2120 allocCreateInfo.pool = pool;
2121
2122 std::vector<BufferInfo> bufInfo;
2123 VmaAllocationInfo allocInfo;
2124 BufferInfo newBufInfo;
2125
2126 // Test double stack.
2127 {
2128 /*
2129 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
2130 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
2131
2132 Totally:
2133 1 block allocated
2134 10240 Vulkan bytes
2135 6 new allocations
2136 2256 bytes in allocations
2137 */
2138
2139 bufCreateInfo.size = 32;
2140 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2141 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002142 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02002143 bufInfo.push_back(newBufInfo);
2144
2145 bufCreateInfo.size = 1024;
2146 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2147 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002148 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02002149 bufInfo.push_back(newBufInfo);
2150
2151 bufCreateInfo.size = 32;
2152 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2153 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002154 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02002155 bufInfo.push_back(newBufInfo);
2156
2157 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
2158
2159 bufCreateInfo.size = 128;
2160 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2161 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002162 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02002163 bufInfo.push_back(newBufInfo);
2164
2165 bufCreateInfo.size = 1024;
2166 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2167 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002168 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02002169 bufInfo.push_back(newBufInfo);
2170
2171 bufCreateInfo.size = 16;
2172 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2173 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002174 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02002175 bufInfo.push_back(newBufInfo);
2176
2177 VmaStats currStats;
2178 vmaCalculateStats(g_hAllocator, &currStats);
2179 VmaPoolStats poolStats;
2180 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2181
2182 char* statsStr = nullptr;
2183 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
2184
2185 // PUT BREAKPOINT HERE TO CHECK.
2186 // Inspect: currStats versus origStats, poolStats, statsStr.
2187 int I = 0;
2188
2189 vmaFreeStatsString(g_hAllocator, statsStr);
2190
2191 // Destroy the buffers in reverse order.
2192 while(!bufInfo.empty())
2193 {
2194 const BufferInfo& currBufInfo = bufInfo.back();
2195 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2196 bufInfo.pop_back();
2197 }
2198 }
2199
2200 vmaDestroyPool(g_hAllocator, pool);
2201}
2202
Adam Sawicki80927152018-09-07 17:27:23 +02002203static void BenchmarkAlgorithmsCase(FILE* file,
2204 uint32_t algorithm,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002205 bool empty,
2206 VmaAllocationCreateFlags allocStrategy,
2207 FREE_ORDER freeOrder)
Adam Sawicki0a607132018-08-24 11:18:41 +02002208{
2209 RandomNumberGenerator rand{16223};
2210
2211 const VkDeviceSize bufSizeMin = 32;
2212 const VkDeviceSize bufSizeMax = 1024;
2213 const size_t maxBufCapacity = 10000;
2214 const uint32_t iterationCount = 10;
2215
2216 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2217 sampleBufCreateInfo.size = bufSizeMax;
2218 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2219
2220 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2221 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2222
2223 VmaPoolCreateInfo poolCreateInfo = {};
2224 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002225 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02002226
2227 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
Adam Sawicki80927152018-09-07 17:27:23 +02002228 poolCreateInfo.flags |= algorithm;
Adam Sawicki0a607132018-08-24 11:18:41 +02002229 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2230
2231 VmaPool pool = nullptr;
2232 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002233 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02002234
2235 // Buffer created just to get memory requirements. Never bound to any memory.
2236 VkBuffer dummyBuffer = VK_NULL_HANDLE;
2237 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, nullptr, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002238 TEST(res == VK_SUCCESS && dummyBuffer);
Adam Sawicki0a607132018-08-24 11:18:41 +02002239
2240 VkMemoryRequirements memReq = {};
2241 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2242
2243 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2244
2245 VmaAllocationCreateInfo allocCreateInfo = {};
2246 allocCreateInfo.pool = pool;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002247 allocCreateInfo.flags = allocStrategy;
Adam Sawicki0a607132018-08-24 11:18:41 +02002248
2249 VmaAllocation alloc;
2250 std::vector<VmaAllocation> baseAllocations;
2251
2252 if(!empty)
2253 {
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02002254 // Make allocations up to 1/3 of pool size.
Adam Sawicki0a607132018-08-24 11:18:41 +02002255 VkDeviceSize totalSize = 0;
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02002256 while(totalSize < poolCreateInfo.blockSize / 3)
Adam Sawicki0a607132018-08-24 11:18:41 +02002257 {
2258 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2259 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002260 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02002261 baseAllocations.push_back(alloc);
2262 totalSize += memReq.size;
2263 }
2264
2265 // Delete half of them, choose randomly.
2266 size_t allocsToDelete = baseAllocations.size() / 2;
2267 for(size_t i = 0; i < allocsToDelete; ++i)
2268 {
2269 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
2270 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
2271 baseAllocations.erase(baseAllocations.begin() + index);
2272 }
2273 }
2274
2275 // BENCHMARK
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02002276 const size_t allocCount = maxBufCapacity / 3;
Adam Sawicki0a607132018-08-24 11:18:41 +02002277 std::vector<VmaAllocation> testAllocations;
2278 testAllocations.reserve(allocCount);
2279 duration allocTotalDuration = duration::zero();
2280 duration freeTotalDuration = duration::zero();
2281 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
2282 {
2283 // Allocations
2284 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
2285 for(size_t i = 0; i < allocCount; ++i)
2286 {
2287 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2288 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002289 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02002290 testAllocations.push_back(alloc);
2291 }
2292 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
2293
2294 // Deallocations
2295 switch(freeOrder)
2296 {
2297 case FREE_ORDER::FORWARD:
2298 // Leave testAllocations unchanged.
2299 break;
2300 case FREE_ORDER::BACKWARD:
2301 std::reverse(testAllocations.begin(), testAllocations.end());
2302 break;
2303 case FREE_ORDER::RANDOM:
2304 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
2305 break;
2306 default: assert(0);
2307 }
2308
2309 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
2310 for(size_t i = 0; i < allocCount; ++i)
2311 vmaFreeMemory(g_hAllocator, testAllocations[i]);
2312 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
2313
2314 testAllocations.clear();
2315 }
2316
2317 // Delete baseAllocations
2318 while(!baseAllocations.empty())
2319 {
2320 vmaFreeMemory(g_hAllocator, baseAllocations.back());
2321 baseAllocations.pop_back();
2322 }
2323
2324 vmaDestroyPool(g_hAllocator, pool);
2325
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002326 const float allocTotalSeconds = ToFloatSeconds(allocTotalDuration);
2327 const float freeTotalSeconds = ToFloatSeconds(freeTotalDuration);
2328
Adam Sawicki80927152018-09-07 17:27:23 +02002329 printf(" Algorithm=%s %s Allocation=%s FreeOrder=%s: allocations %g s, free %g s\n",
2330 AlgorithmToStr(algorithm),
Adam Sawicki0667e332018-08-24 17:26:44 +02002331 empty ? "Empty" : "Not empty",
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002332 GetAllocationStrategyName(allocStrategy),
Adam Sawicki0a607132018-08-24 11:18:41 +02002333 FREE_ORDER_NAMES[(size_t)freeOrder],
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002334 allocTotalSeconds,
2335 freeTotalSeconds);
2336
2337 if(file)
2338 {
2339 std::string currTime;
2340 CurrentTimeToStr(currTime);
2341
Adam Sawicki80927152018-09-07 17:27:23 +02002342 fprintf(file, "%s,%s,%s,%u,%s,%s,%g,%g\n",
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002343 CODE_DESCRIPTION, currTime.c_str(),
Adam Sawicki80927152018-09-07 17:27:23 +02002344 AlgorithmToStr(algorithm),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002345 empty ? 1 : 0,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002346 GetAllocationStrategyName(allocStrategy),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002347 FREE_ORDER_NAMES[(uint32_t)freeOrder],
2348 allocTotalSeconds,
2349 freeTotalSeconds);
2350 }
Adam Sawicki0a607132018-08-24 11:18:41 +02002351}
2352
Adam Sawicki80927152018-09-07 17:27:23 +02002353static void BenchmarkAlgorithms(FILE* file)
Adam Sawicki0a607132018-08-24 11:18:41 +02002354{
Adam Sawicki80927152018-09-07 17:27:23 +02002355 wprintf(L"Benchmark algorithms\n");
Adam Sawicki0a607132018-08-24 11:18:41 +02002356
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002357 if(file)
2358 {
2359 fprintf(file,
2360 "Code,Time,"
Adam Sawicki80927152018-09-07 17:27:23 +02002361 "Algorithm,Empty,Allocation strategy,Free order,"
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002362 "Allocation time (s),Deallocation time (s)\n");
2363 }
2364
Adam Sawicki0a607132018-08-24 11:18:41 +02002365 uint32_t freeOrderCount = 1;
2366 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
2367 freeOrderCount = 3;
2368 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
2369 freeOrderCount = 2;
2370
2371 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002372 const uint32_t allocStrategyCount = GetAllocationStrategyCount();
Adam Sawicki0a607132018-08-24 11:18:41 +02002373
2374 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
2375 {
2376 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
2377 switch(freeOrderIndex)
2378 {
2379 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
2380 case 1: freeOrder = FREE_ORDER::FORWARD; break;
2381 case 2: freeOrder = FREE_ORDER::RANDOM; break;
2382 default: assert(0);
2383 }
2384
2385 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
2386 {
Adam Sawicki80927152018-09-07 17:27:23 +02002387 for(uint32_t algorithmIndex = 0; algorithmIndex < 3; ++algorithmIndex)
Adam Sawicki0a607132018-08-24 11:18:41 +02002388 {
Adam Sawicki80927152018-09-07 17:27:23 +02002389 uint32_t algorithm = 0;
2390 switch(algorithmIndex)
2391 {
2392 case 0:
2393 break;
2394 case 1:
2395 algorithm = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
2396 break;
2397 case 2:
2398 algorithm = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2399 break;
2400 default:
2401 assert(0);
2402 }
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002403
Adam Sawicki80927152018-09-07 17:27:23 +02002404 uint32_t currAllocStrategyCount = algorithm != 0 ? 1 : allocStrategyCount;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002405 for(uint32_t allocStrategyIndex = 0; allocStrategyIndex < currAllocStrategyCount; ++allocStrategyIndex)
2406 {
2407 VmaAllocatorCreateFlags strategy = 0;
Adam Sawicki80927152018-09-07 17:27:23 +02002408 if(currAllocStrategyCount > 1)
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002409 {
2410 switch(allocStrategyIndex)
2411 {
2412 case 0: strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; break;
2413 case 1: strategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT; break;
2414 case 2: strategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT; break;
2415 default: assert(0);
2416 }
2417 }
2418
Adam Sawicki80927152018-09-07 17:27:23 +02002419 BenchmarkAlgorithmsCase(
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002420 file,
Adam Sawicki80927152018-09-07 17:27:23 +02002421 algorithm,
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02002422 (emptyIndex == 0), // empty
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002423 strategy,
2424 freeOrder); // freeOrder
2425 }
Adam Sawicki0a607132018-08-24 11:18:41 +02002426 }
2427 }
2428 }
2429}
2430
Adam Sawickib8333fb2018-03-13 16:15:53 +01002431static void TestPool_SameSize()
2432{
2433 const VkDeviceSize BUF_SIZE = 1024 * 1024;
2434 const size_t BUF_COUNT = 100;
2435 VkResult res;
2436
2437 RandomNumberGenerator rand{123};
2438
2439 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2440 bufferInfo.size = BUF_SIZE;
2441 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2442
2443 uint32_t memoryTypeBits = UINT32_MAX;
2444 {
2445 VkBuffer dummyBuffer;
2446 res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002447 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002448
2449 VkMemoryRequirements memReq;
2450 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2451 memoryTypeBits = memReq.memoryTypeBits;
2452
2453 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2454 }
2455
2456 VmaAllocationCreateInfo poolAllocInfo = {};
2457 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2458 uint32_t memTypeIndex;
2459 res = vmaFindMemoryTypeIndex(
2460 g_hAllocator,
2461 memoryTypeBits,
2462 &poolAllocInfo,
2463 &memTypeIndex);
2464
2465 VmaPoolCreateInfo poolCreateInfo = {};
2466 poolCreateInfo.memoryTypeIndex = memTypeIndex;
2467 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
2468 poolCreateInfo.minBlockCount = 1;
2469 poolCreateInfo.maxBlockCount = 4;
2470 poolCreateInfo.frameInUseCount = 0;
2471
2472 VmaPool pool;
2473 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002474 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002475
2476 vmaSetCurrentFrameIndex(g_hAllocator, 1);
2477
2478 VmaAllocationCreateInfo allocInfo = {};
2479 allocInfo.pool = pool;
2480 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2481 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2482
2483 struct BufItem
2484 {
2485 VkBuffer Buf;
2486 VmaAllocation Alloc;
2487 };
2488 std::vector<BufItem> items;
2489
2490 // Fill entire pool.
2491 for(size_t i = 0; i < BUF_COUNT; ++i)
2492 {
2493 BufItem item;
2494 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002495 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002496 items.push_back(item);
2497 }
2498
2499 // Make sure that another allocation would fail.
2500 {
2501 BufItem item;
2502 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002503 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002504 }
2505
2506 // Validate that no buffer is lost. Also check that they are not mapped.
2507 for(size_t i = 0; i < items.size(); ++i)
2508 {
2509 VmaAllocationInfo allocInfo;
2510 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002511 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
2512 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002513 }
2514
2515 // Free some percent of random items.
2516 {
2517 const size_t PERCENT_TO_FREE = 10;
2518 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
2519 for(size_t i = 0; i < itemsToFree; ++i)
2520 {
2521 size_t index = (size_t)rand.Generate() % items.size();
2522 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2523 items.erase(items.begin() + index);
2524 }
2525 }
2526
2527 // Randomly allocate and free items.
2528 {
2529 const size_t OPERATION_COUNT = BUF_COUNT;
2530 for(size_t i = 0; i < OPERATION_COUNT; ++i)
2531 {
2532 bool allocate = rand.Generate() % 2 != 0;
2533 if(allocate)
2534 {
2535 if(items.size() < BUF_COUNT)
2536 {
2537 BufItem item;
2538 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002539 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002540 items.push_back(item);
2541 }
2542 }
2543 else // Free
2544 {
2545 if(!items.empty())
2546 {
2547 size_t index = (size_t)rand.Generate() % items.size();
2548 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2549 items.erase(items.begin() + index);
2550 }
2551 }
2552 }
2553 }
2554
2555 // Allocate up to maximum.
2556 while(items.size() < BUF_COUNT)
2557 {
2558 BufItem item;
2559 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002560 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002561 items.push_back(item);
2562 }
2563
2564 // Validate that no buffer is lost.
2565 for(size_t i = 0; i < items.size(); ++i)
2566 {
2567 VmaAllocationInfo allocInfo;
2568 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002569 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002570 }
2571
2572 // Next frame.
2573 vmaSetCurrentFrameIndex(g_hAllocator, 2);
2574
2575 // Allocate another BUF_COUNT buffers.
2576 for(size_t i = 0; i < BUF_COUNT; ++i)
2577 {
2578 BufItem item;
2579 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002580 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002581 items.push_back(item);
2582 }
2583
2584 // Make sure the first BUF_COUNT is lost. Delete them.
2585 for(size_t i = 0; i < BUF_COUNT; ++i)
2586 {
2587 VmaAllocationInfo allocInfo;
2588 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002589 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002590 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2591 }
2592 items.erase(items.begin(), items.begin() + BUF_COUNT);
2593
2594 // Validate that no buffer is lost.
2595 for(size_t i = 0; i < items.size(); ++i)
2596 {
2597 VmaAllocationInfo allocInfo;
2598 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002599 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002600 }
2601
2602 // Free one item.
2603 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
2604 items.pop_back();
2605
2606 // Validate statistics.
2607 {
2608 VmaPoolStats poolStats = {};
2609 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002610 TEST(poolStats.allocationCount == items.size());
2611 TEST(poolStats.size = BUF_COUNT * BUF_SIZE);
2612 TEST(poolStats.unusedRangeCount == 1);
2613 TEST(poolStats.unusedRangeSizeMax == BUF_SIZE);
2614 TEST(poolStats.unusedSize == BUF_SIZE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002615 }
2616
2617 // Free all remaining items.
2618 for(size_t i = items.size(); i--; )
2619 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2620 items.clear();
2621
2622 // Allocate maximum items again.
2623 for(size_t i = 0; i < BUF_COUNT; ++i)
2624 {
2625 BufItem item;
2626 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002627 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002628 items.push_back(item);
2629 }
2630
2631 // Delete every other item.
2632 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
2633 {
2634 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2635 items.erase(items.begin() + i);
2636 }
2637
2638 // Defragment!
2639 {
2640 std::vector<VmaAllocation> allocationsToDefragment(items.size());
2641 for(size_t i = 0; i < items.size(); ++i)
2642 allocationsToDefragment[i] = items[i].Alloc;
2643
2644 VmaDefragmentationStats defragmentationStats;
2645 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002646 TEST(res == VK_SUCCESS);
2647 TEST(defragmentationStats.deviceMemoryBlocksFreed == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002648 }
2649
2650 // Free all remaining items.
2651 for(size_t i = items.size(); i--; )
2652 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2653 items.clear();
2654
2655 ////////////////////////////////////////////////////////////////////////////////
2656 // Test for vmaMakePoolAllocationsLost
2657
2658 // Allocate 4 buffers on frame 10.
2659 vmaSetCurrentFrameIndex(g_hAllocator, 10);
2660 for(size_t i = 0; i < 4; ++i)
2661 {
2662 BufItem item;
2663 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002664 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002665 items.push_back(item);
2666 }
2667
2668 // Touch first 2 of them on frame 11.
2669 vmaSetCurrentFrameIndex(g_hAllocator, 11);
2670 for(size_t i = 0; i < 2; ++i)
2671 {
2672 VmaAllocationInfo allocInfo;
2673 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2674 }
2675
2676 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
2677 size_t lostCount = 0xDEADC0DE;
2678 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002679 TEST(lostCount == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002680
2681 // Make another call. Now 0 should be lost.
2682 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002683 TEST(lostCount == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002684
2685 // Make another call, with null count. Should not crash.
2686 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
2687
2688 // END: Free all remaining items.
2689 for(size_t i = items.size(); i--; )
2690 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2691
2692 items.clear();
2693
Adam Sawickid2924172018-06-11 12:48:46 +02002694 ////////////////////////////////////////////////////////////////////////////////
2695 // Test for allocation too large for pool
2696
2697 {
2698 VmaAllocationCreateInfo allocCreateInfo = {};
2699 allocCreateInfo.pool = pool;
2700
2701 VkMemoryRequirements memReq;
2702 memReq.memoryTypeBits = UINT32_MAX;
2703 memReq.alignment = 1;
2704 memReq.size = poolCreateInfo.blockSize + 4;
2705
2706 VmaAllocation alloc = nullptr;
2707 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002708 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
Adam Sawickid2924172018-06-11 12:48:46 +02002709 }
2710
Adam Sawickib8333fb2018-03-13 16:15:53 +01002711 vmaDestroyPool(g_hAllocator, pool);
2712}
2713
Adam Sawickib0c36362018-11-13 16:17:38 +01002714static void TestResize()
2715{
2716 wprintf(L"Testing vmaResizeAllocation...\n");
2717
2718 const VkDeviceSize KILOBYTE = 1024ull;
2719 const VkDeviceSize MEGABYTE = KILOBYTE * 1024;
2720
2721 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2722 bufCreateInfo.size = 2 * MEGABYTE;
2723 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2724
2725 VmaAllocationCreateInfo allocCreateInfo = {};
2726 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2727
2728 uint32_t memTypeIndex = UINT32_MAX;
2729 TEST( vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &memTypeIndex) == VK_SUCCESS );
2730
2731 VmaPoolCreateInfo poolCreateInfo = {};
2732 poolCreateInfo.flags = VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT;
2733 poolCreateInfo.blockSize = 8 * MEGABYTE;
2734 poolCreateInfo.minBlockCount = 1;
2735 poolCreateInfo.maxBlockCount = 1;
2736 poolCreateInfo.memoryTypeIndex = memTypeIndex;
2737
2738 VmaPool pool;
2739 TEST( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) == VK_SUCCESS );
2740
2741 allocCreateInfo.pool = pool;
2742
2743 // Fill 8 MB pool with 4 * 2 MB allocations.
2744 VmaAllocation allocs[4] = {};
2745
2746 VkMemoryRequirements memReq = {};
2747 memReq.memoryTypeBits = UINT32_MAX;
2748 memReq.alignment = 4;
2749 memReq.size = bufCreateInfo.size;
2750
2751 VmaAllocationInfo allocInfo = {};
2752
2753 for(uint32_t i = 0; i < 4; ++i)
2754 {
2755 TEST( vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &allocs[i], nullptr) == VK_SUCCESS );
2756 }
2757
2758 // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 2MB
2759
2760 // Case: Resize to the same size always succeeds.
2761 {
2762 TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 2 * MEGABYTE) == VK_SUCCESS);
2763 vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);
2764 TEST(allocInfo.size == 2ull * 1024 * 1024);
2765 }
2766
2767 // Case: Shrink allocation at the end.
2768 {
2769 TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 1 * MEGABYTE) == VK_SUCCESS );
2770 vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);
2771 TEST(allocInfo.size == 1ull * 1024 * 1024);
2772 }
2773
2774 // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 1MB, free 1MB
2775
2776 // Case: Shrink allocation before free space.
2777 {
2778 TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 512 * KILOBYTE) == VK_SUCCESS );
2779 vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);
2780 TEST(allocInfo.size == 512 * KILOBYTE);
2781 }
2782
2783 // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 0.5MB, free 1.5MB
2784
2785 // Case: Shrink allocation before next allocation.
2786 {
2787 TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 1 * MEGABYTE) == VK_SUCCESS );
2788 vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo);
2789 TEST(allocInfo.size == 1 * MEGABYTE);
2790 }
2791
2792 // Now it's: a0 1MB, free 1 MB, a1 2MB, a2 2MB, a3 0.5MB, free 1.5MB
2793
2794 // Case: Grow allocation while there is even more space available.
2795 {
2796 TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 1 * MEGABYTE) == VK_SUCCESS );
2797 vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);
2798 TEST(allocInfo.size == 1 * MEGABYTE);
2799 }
2800
2801 // Now it's: a0 1MB, free 1 MB, a1 2MB, a2 2MB, a3 1MB, free 1MB
2802
2803 // Case: Grow allocation while there is exact amount of free space available.
2804 {
2805 TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 2 * MEGABYTE) == VK_SUCCESS );
2806 vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo);
2807 TEST(allocInfo.size == 2 * MEGABYTE);
2808 }
2809
2810 // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 1MB, free 1MB
2811
2812 // Case: Fail to grow when there is not enough free space due to next allocation.
2813 {
2814 TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 3 * MEGABYTE) == VK_ERROR_OUT_OF_POOL_MEMORY );
2815 vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo);
2816 TEST(allocInfo.size == 2 * MEGABYTE);
2817 }
2818
2819 // Case: Fail to grow when there is not enough free space due to end of memory block.
2820 {
2821 TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 3 * MEGABYTE) == VK_ERROR_OUT_OF_POOL_MEMORY );
2822 vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);
2823 TEST(allocInfo.size == 1 * MEGABYTE);
2824 }
2825
2826 for(uint32_t i = 4; i--; )
2827 {
2828 vmaFreeMemory(g_hAllocator, allocs[i]);
2829 }
2830
2831 vmaDestroyPool(g_hAllocator, pool);
2832
2833 // Test dedicated allocation
2834 {
2835 VmaAllocationCreateInfo dedicatedAllocCreateInfo = {};
2836 dedicatedAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2837 dedicatedAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2838
2839 VmaAllocation dedicatedAlloc = VK_NULL_HANDLE;
2840 TEST( vmaAllocateMemory(g_hAllocator, &memReq, &dedicatedAllocCreateInfo, &dedicatedAlloc, nullptr) == VK_SUCCESS );
2841
2842 // Case: Resize to the same size always succeeds.
2843 {
2844 TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 2 * MEGABYTE) == VK_SUCCESS);
2845 vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo);
2846 TEST(allocInfo.size == 2ull * 1024 * 1024);
2847 }
2848
2849 // Case: Shrinking fails.
2850 {
2851 TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 1 * MEGABYTE) < VK_SUCCESS);
2852 vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo);
2853 TEST(allocInfo.size == 2ull * 1024 * 1024);
2854 }
2855
2856 // Case: Growing fails.
2857 {
2858 TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 3 * MEGABYTE) < VK_SUCCESS);
2859 vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo);
2860 TEST(allocInfo.size == 2ull * 1024 * 1024);
2861 }
2862
2863 vmaFreeMemory(g_hAllocator, dedicatedAlloc);
2864 }
2865}
2866
Adam Sawickie44c6262018-06-15 14:30:39 +02002867static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
2868{
2869 const uint8_t* pBytes = (const uint8_t*)pMemory;
2870 for(size_t i = 0; i < size; ++i)
2871 {
2872 if(pBytes[i] != pattern)
2873 {
2874 return false;
2875 }
2876 }
2877 return true;
2878}
2879
2880static void TestAllocationsInitialization()
2881{
2882 VkResult res;
2883
2884 const size_t BUF_SIZE = 1024;
2885
2886 // Create pool.
2887
2888 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2889 bufInfo.size = BUF_SIZE;
2890 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2891
2892 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
2893 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2894
2895 VmaPoolCreateInfo poolCreateInfo = {};
2896 poolCreateInfo.blockSize = BUF_SIZE * 10;
2897 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
2898 poolCreateInfo.maxBlockCount = 1;
2899 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002900 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002901
2902 VmaAllocationCreateInfo bufAllocCreateInfo = {};
2903 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002904 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002905
2906 // Create one persistently mapped buffer to keep memory of this block mapped,
2907 // so that pointer to mapped data will remain (more or less...) valid even
2908 // after destruction of other allocations.
2909
2910 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2911 VkBuffer firstBuf;
2912 VmaAllocation firstAlloc;
2913 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002914 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002915
2916 // Test buffers.
2917
2918 for(uint32_t i = 0; i < 2; ++i)
2919 {
2920 const bool persistentlyMapped = i == 0;
2921 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
2922 VkBuffer buf;
2923 VmaAllocation alloc;
2924 VmaAllocationInfo allocInfo;
2925 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002926 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002927
2928 void* pMappedData;
2929 if(!persistentlyMapped)
2930 {
2931 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002932 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002933 }
2934 else
2935 {
2936 pMappedData = allocInfo.pMappedData;
2937 }
2938
2939 // Validate initialized content
2940 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002941 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02002942
2943 if(!persistentlyMapped)
2944 {
2945 vmaUnmapMemory(g_hAllocator, alloc);
2946 }
2947
2948 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2949
2950 // Validate freed content
2951 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002952 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02002953 }
2954
2955 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
2956 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
2957}
2958
Adam Sawickib8333fb2018-03-13 16:15:53 +01002959static void TestPool_Benchmark(
2960 PoolTestResult& outResult,
2961 const PoolTestConfig& config)
2962{
Adam Sawickib8d34d52018-10-03 17:41:20 +02002963 TEST(config.ThreadCount > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002964
2965 RandomNumberGenerator mainRand{config.RandSeed};
2966
2967 uint32_t allocationSizeProbabilitySum = std::accumulate(
2968 config.AllocationSizes.begin(),
2969 config.AllocationSizes.end(),
2970 0u,
2971 [](uint32_t sum, const AllocationSize& allocSize) {
2972 return sum + allocSize.Probability;
2973 });
2974
2975 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2976 bufferInfo.size = 256; // Whatever.
2977 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2978
2979 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2980 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2981 imageInfo.extent.width = 256; // Whatever.
2982 imageInfo.extent.height = 256; // Whatever.
2983 imageInfo.extent.depth = 1;
2984 imageInfo.mipLevels = 1;
2985 imageInfo.arrayLayers = 1;
2986 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2987 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
2988 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2989 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
2990 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2991
2992 uint32_t bufferMemoryTypeBits = UINT32_MAX;
2993 {
2994 VkBuffer dummyBuffer;
2995 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002996 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002997
2998 VkMemoryRequirements memReq;
2999 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3000 bufferMemoryTypeBits = memReq.memoryTypeBits;
3001
3002 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
3003 }
3004
3005 uint32_t imageMemoryTypeBits = UINT32_MAX;
3006 {
3007 VkImage dummyImage;
3008 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003009 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003010
3011 VkMemoryRequirements memReq;
3012 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
3013 imageMemoryTypeBits = memReq.memoryTypeBits;
3014
3015 vkDestroyImage(g_hDevice, dummyImage, nullptr);
3016 }
3017
3018 uint32_t memoryTypeBits = 0;
3019 if(config.UsesBuffers() && config.UsesImages())
3020 {
3021 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
3022 if(memoryTypeBits == 0)
3023 {
3024 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
3025 return;
3026 }
3027 }
3028 else if(config.UsesBuffers())
3029 memoryTypeBits = bufferMemoryTypeBits;
3030 else if(config.UsesImages())
3031 memoryTypeBits = imageMemoryTypeBits;
3032 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02003033 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003034
3035 VmaPoolCreateInfo poolCreateInfo = {};
3036 poolCreateInfo.memoryTypeIndex = 0;
3037 poolCreateInfo.minBlockCount = 1;
3038 poolCreateInfo.maxBlockCount = 1;
3039 poolCreateInfo.blockSize = config.PoolSize;
3040 poolCreateInfo.frameInUseCount = 1;
3041
3042 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
3043 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3044 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
3045
3046 VmaPool pool;
3047 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003048 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003049
3050 // Start time measurement - after creating pool and initializing data structures.
3051 time_point timeBeg = std::chrono::high_resolution_clock::now();
3052
3053 ////////////////////////////////////////////////////////////////////////////////
3054 // ThreadProc
3055 auto ThreadProc = [&](
3056 PoolTestThreadResult* outThreadResult,
3057 uint32_t randSeed,
3058 HANDLE frameStartEvent,
3059 HANDLE frameEndEvent) -> void
3060 {
3061 RandomNumberGenerator threadRand{randSeed};
3062
3063 outThreadResult->AllocationTimeMin = duration::max();
3064 outThreadResult->AllocationTimeSum = duration::zero();
3065 outThreadResult->AllocationTimeMax = duration::min();
3066 outThreadResult->DeallocationTimeMin = duration::max();
3067 outThreadResult->DeallocationTimeSum = duration::zero();
3068 outThreadResult->DeallocationTimeMax = duration::min();
3069 outThreadResult->AllocationCount = 0;
3070 outThreadResult->DeallocationCount = 0;
3071 outThreadResult->LostAllocationCount = 0;
3072 outThreadResult->LostAllocationTotalSize = 0;
3073 outThreadResult->FailedAllocationCount = 0;
3074 outThreadResult->FailedAllocationTotalSize = 0;
3075
3076 struct Item
3077 {
3078 VkDeviceSize BufferSize;
3079 VkExtent2D ImageSize;
3080 VkBuffer Buf;
3081 VkImage Image;
3082 VmaAllocation Alloc;
3083
3084 VkDeviceSize CalcSizeBytes() const
3085 {
3086 return BufferSize +
3087 ImageSize.width * ImageSize.height * 4;
3088 }
3089 };
3090 std::vector<Item> unusedItems, usedItems;
3091
3092 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
3093
3094 // Create all items - all unused, not yet allocated.
3095 for(size_t i = 0; i < threadTotalItemCount; ++i)
3096 {
3097 Item item = {};
3098
3099 uint32_t allocSizeIndex = 0;
3100 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
3101 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
3102 r -= config.AllocationSizes[allocSizeIndex++].Probability;
3103
3104 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
3105 if(allocSize.BufferSizeMax > 0)
3106 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003107 TEST(allocSize.BufferSizeMin > 0);
3108 TEST(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003109 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
3110 item.BufferSize = allocSize.BufferSizeMin;
3111 else
3112 {
3113 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
3114 item.BufferSize = item.BufferSize / 16 * 16;
3115 }
3116 }
3117 else
3118 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003119 TEST(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003120 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
3121 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
3122 else
3123 {
3124 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
3125 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
3126 }
3127 }
3128
3129 unusedItems.push_back(item);
3130 }
3131
3132 auto Allocate = [&](Item& item) -> VkResult
3133 {
3134 VmaAllocationCreateInfo allocCreateInfo = {};
3135 allocCreateInfo.pool = pool;
3136 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
3137 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
3138
3139 if(item.BufferSize)
3140 {
3141 bufferInfo.size = item.BufferSize;
3142 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
3143 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
3144 }
3145 else
3146 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003147 TEST(item.ImageSize.width && item.ImageSize.height);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003148
3149 imageInfo.extent.width = item.ImageSize.width;
3150 imageInfo.extent.height = item.ImageSize.height;
3151 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
3152 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
3153 }
3154 };
3155
3156 ////////////////////////////////////////////////////////////////////////////////
3157 // Frames
3158 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
3159 {
3160 WaitForSingleObject(frameStartEvent, INFINITE);
3161
3162 // Always make some percent of used bufs unused, to choose different used ones.
3163 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
3164 for(size_t i = 0; i < bufsToMakeUnused; ++i)
3165 {
3166 size_t index = threadRand.Generate() % usedItems.size();
3167 unusedItems.push_back(usedItems[index]);
3168 usedItems.erase(usedItems.begin() + index);
3169 }
3170
3171 // Determine which bufs we want to use in this frame.
3172 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
3173 / config.ThreadCount;
Adam Sawickib8d34d52018-10-03 17:41:20 +02003174 TEST(usedBufCount < usedItems.size() + unusedItems.size());
Adam Sawickib8333fb2018-03-13 16:15:53 +01003175 // Move some used to unused.
3176 while(usedBufCount < usedItems.size())
3177 {
3178 size_t index = threadRand.Generate() % usedItems.size();
3179 unusedItems.push_back(usedItems[index]);
3180 usedItems.erase(usedItems.begin() + index);
3181 }
3182 // Move some unused to used.
3183 while(usedBufCount > usedItems.size())
3184 {
3185 size_t index = threadRand.Generate() % unusedItems.size();
3186 usedItems.push_back(unusedItems[index]);
3187 unusedItems.erase(unusedItems.begin() + index);
3188 }
3189
3190 uint32_t touchExistingCount = 0;
3191 uint32_t touchLostCount = 0;
3192 uint32_t createSucceededCount = 0;
3193 uint32_t createFailedCount = 0;
3194
3195 // Touch all used bufs. If not created or lost, allocate.
3196 for(size_t i = 0; i < usedItems.size(); ++i)
3197 {
3198 Item& item = usedItems[i];
3199 // Not yet created.
3200 if(item.Alloc == VK_NULL_HANDLE)
3201 {
3202 res = Allocate(item);
3203 ++outThreadResult->AllocationCount;
3204 if(res != VK_SUCCESS)
3205 {
3206 item.Alloc = VK_NULL_HANDLE;
3207 item.Buf = VK_NULL_HANDLE;
3208 ++outThreadResult->FailedAllocationCount;
3209 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
3210 ++createFailedCount;
3211 }
3212 else
3213 ++createSucceededCount;
3214 }
3215 else
3216 {
3217 // Touch.
3218 VmaAllocationInfo allocInfo;
3219 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
3220 // Lost.
3221 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3222 {
3223 ++touchLostCount;
3224
3225 // Destroy.
3226 {
3227 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
3228 if(item.Buf)
3229 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
3230 else
3231 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
3232 ++outThreadResult->DeallocationCount;
3233 }
3234 item.Alloc = VK_NULL_HANDLE;
3235 item.Buf = VK_NULL_HANDLE;
3236
3237 ++outThreadResult->LostAllocationCount;
3238 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
3239
3240 // Recreate.
3241 res = Allocate(item);
3242 ++outThreadResult->AllocationCount;
3243 // Creation failed.
3244 if(res != VK_SUCCESS)
3245 {
3246 ++outThreadResult->FailedAllocationCount;
3247 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
3248 ++createFailedCount;
3249 }
3250 else
3251 ++createSucceededCount;
3252 }
3253 else
3254 ++touchExistingCount;
3255 }
3256 }
3257
3258 /*
3259 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
3260 randSeed, frameIndex,
3261 touchExistingCount, touchLostCount,
3262 createSucceededCount, createFailedCount);
3263 */
3264
3265 SetEvent(frameEndEvent);
3266 }
3267
3268 // Free all remaining items.
3269 for(size_t i = usedItems.size(); i--; )
3270 {
3271 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
3272 if(usedItems[i].Buf)
3273 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
3274 else
3275 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
3276 ++outThreadResult->DeallocationCount;
3277 }
3278 for(size_t i = unusedItems.size(); i--; )
3279 {
3280 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
3281 if(unusedItems[i].Buf)
3282 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
3283 else
3284 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
3285 ++outThreadResult->DeallocationCount;
3286 }
3287 };
3288
3289 // Launch threads.
3290 uint32_t threadRandSeed = mainRand.Generate();
3291 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
3292 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
3293 std::vector<std::thread> bkgThreads;
3294 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
3295 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3296 {
3297 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
3298 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
3299 bkgThreads.emplace_back(std::bind(
3300 ThreadProc,
3301 &threadResults[threadIndex],
3302 threadRandSeed + threadIndex,
3303 frameStartEvents[threadIndex],
3304 frameEndEvents[threadIndex]));
3305 }
3306
3307 // Execute frames.
Adam Sawickib8d34d52018-10-03 17:41:20 +02003308 TEST(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003309 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
3310 {
3311 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
3312 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3313 SetEvent(frameStartEvents[threadIndex]);
3314 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
3315 }
3316
3317 // Wait for threads finished
3318 for(size_t i = 0; i < bkgThreads.size(); ++i)
3319 {
3320 bkgThreads[i].join();
3321 CloseHandle(frameEndEvents[i]);
3322 CloseHandle(frameStartEvents[i]);
3323 }
3324 bkgThreads.clear();
3325
3326 // Finish time measurement - before destroying pool.
3327 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
3328
3329 vmaDestroyPool(g_hAllocator, pool);
3330
3331 outResult.AllocationTimeMin = duration::max();
3332 outResult.AllocationTimeAvg = duration::zero();
3333 outResult.AllocationTimeMax = duration::min();
3334 outResult.DeallocationTimeMin = duration::max();
3335 outResult.DeallocationTimeAvg = duration::zero();
3336 outResult.DeallocationTimeMax = duration::min();
3337 outResult.LostAllocationCount = 0;
3338 outResult.LostAllocationTotalSize = 0;
3339 outResult.FailedAllocationCount = 0;
3340 outResult.FailedAllocationTotalSize = 0;
3341 size_t allocationCount = 0;
3342 size_t deallocationCount = 0;
3343 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3344 {
3345 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
3346 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
3347 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
3348 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
3349 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
3350 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
3351 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
3352 allocationCount += threadResult.AllocationCount;
3353 deallocationCount += threadResult.DeallocationCount;
3354 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
3355 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
3356 outResult.LostAllocationCount += threadResult.LostAllocationCount;
3357 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
3358 }
3359 if(allocationCount)
3360 outResult.AllocationTimeAvg /= allocationCount;
3361 if(deallocationCount)
3362 outResult.DeallocationTimeAvg /= deallocationCount;
3363}
3364
3365static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
3366{
3367 if(ptr1 < ptr2)
3368 return ptr1 + size1 > ptr2;
3369 else if(ptr2 < ptr1)
3370 return ptr2 + size2 > ptr1;
3371 else
3372 return true;
3373}
3374
3375static void TestMapping()
3376{
3377 wprintf(L"Testing mapping...\n");
3378
3379 VkResult res;
3380 uint32_t memTypeIndex = UINT32_MAX;
3381
3382 enum TEST
3383 {
3384 TEST_NORMAL,
3385 TEST_POOL,
3386 TEST_DEDICATED,
3387 TEST_COUNT
3388 };
3389 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3390 {
3391 VmaPool pool = nullptr;
3392 if(testIndex == TEST_POOL)
3393 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003394 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003395 VmaPoolCreateInfo poolInfo = {};
3396 poolInfo.memoryTypeIndex = memTypeIndex;
3397 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003398 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003399 }
3400
3401 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3402 bufInfo.size = 0x10000;
3403 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3404
3405 VmaAllocationCreateInfo allocCreateInfo = {};
3406 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3407 allocCreateInfo.pool = pool;
3408 if(testIndex == TEST_DEDICATED)
3409 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3410
3411 VmaAllocationInfo allocInfo;
3412
3413 // Mapped manually
3414
3415 // Create 2 buffers.
3416 BufferInfo bufferInfos[3];
3417 for(size_t i = 0; i < 2; ++i)
3418 {
3419 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3420 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003421 TEST(res == VK_SUCCESS);
3422 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003423 memTypeIndex = allocInfo.memoryType;
3424 }
3425
3426 // Map buffer 0.
3427 char* data00 = nullptr;
3428 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003429 TEST(res == VK_SUCCESS && data00 != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003430 data00[0xFFFF] = data00[0];
3431
3432 // Map buffer 0 second time.
3433 char* data01 = nullptr;
3434 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003435 TEST(res == VK_SUCCESS && data01 == data00);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003436
3437 // Map buffer 1.
3438 char* data1 = nullptr;
3439 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003440 TEST(res == VK_SUCCESS && data1 != nullptr);
3441 TEST(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
Adam Sawickib8333fb2018-03-13 16:15:53 +01003442 data1[0xFFFF] = data1[0];
3443
3444 // Unmap buffer 0 two times.
3445 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3446 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3447 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003448 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003449
3450 // Unmap buffer 1.
3451 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
3452 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003453 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003454
3455 // Create 3rd buffer - persistently mapped.
3456 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3457 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3458 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003459 TEST(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003460
3461 // Map buffer 2.
3462 char* data2 = nullptr;
3463 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003464 TEST(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003465 data2[0xFFFF] = data2[0];
3466
3467 // Unmap buffer 2.
3468 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
3469 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003470 TEST(allocInfo.pMappedData == data2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003471
3472 // Destroy all buffers.
3473 for(size_t i = 3; i--; )
3474 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
3475
3476 vmaDestroyPool(g_hAllocator, pool);
3477 }
3478}
3479
3480static void TestMappingMultithreaded()
3481{
3482 wprintf(L"Testing mapping multithreaded...\n");
3483
3484 static const uint32_t threadCount = 16;
3485 static const uint32_t bufferCount = 1024;
3486 static const uint32_t threadBufferCount = bufferCount / threadCount;
3487
3488 VkResult res;
3489 volatile uint32_t memTypeIndex = UINT32_MAX;
3490
3491 enum TEST
3492 {
3493 TEST_NORMAL,
3494 TEST_POOL,
3495 TEST_DEDICATED,
3496 TEST_COUNT
3497 };
3498 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3499 {
3500 VmaPool pool = nullptr;
3501 if(testIndex == TEST_POOL)
3502 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003503 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003504 VmaPoolCreateInfo poolInfo = {};
3505 poolInfo.memoryTypeIndex = memTypeIndex;
3506 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003507 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003508 }
3509
3510 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3511 bufCreateInfo.size = 0x10000;
3512 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3513
3514 VmaAllocationCreateInfo allocCreateInfo = {};
3515 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3516 allocCreateInfo.pool = pool;
3517 if(testIndex == TEST_DEDICATED)
3518 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3519
3520 std::thread threads[threadCount];
3521 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3522 {
3523 threads[threadIndex] = std::thread([=, &memTypeIndex](){
3524 // ======== THREAD FUNCTION ========
3525
3526 RandomNumberGenerator rand{threadIndex};
3527
3528 enum class MODE
3529 {
3530 // Don't map this buffer at all.
3531 DONT_MAP,
3532 // Map and quickly unmap.
3533 MAP_FOR_MOMENT,
3534 // Map and unmap before destruction.
3535 MAP_FOR_LONGER,
3536 // Map two times. Quickly unmap, second unmap before destruction.
3537 MAP_TWO_TIMES,
3538 // Create this buffer as persistently mapped.
3539 PERSISTENTLY_MAPPED,
3540 COUNT
3541 };
3542 std::vector<BufferInfo> bufInfos{threadBufferCount};
3543 std::vector<MODE> bufModes{threadBufferCount};
3544
3545 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
3546 {
3547 BufferInfo& bufInfo = bufInfos[bufferIndex];
3548 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
3549 bufModes[bufferIndex] = mode;
3550
3551 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
3552 if(mode == MODE::PERSISTENTLY_MAPPED)
3553 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3554
3555 VmaAllocationInfo allocInfo;
3556 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
3557 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003558 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003559
3560 if(memTypeIndex == UINT32_MAX)
3561 memTypeIndex = allocInfo.memoryType;
3562
3563 char* data = nullptr;
3564
3565 if(mode == MODE::PERSISTENTLY_MAPPED)
3566 {
3567 data = (char*)allocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02003568 TEST(data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003569 }
3570 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
3571 mode == MODE::MAP_TWO_TIMES)
3572 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003573 TEST(data == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003574 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003575 TEST(res == VK_SUCCESS && data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003576
3577 if(mode == MODE::MAP_TWO_TIMES)
3578 {
3579 char* data2 = nullptr;
3580 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003581 TEST(res == VK_SUCCESS && data2 == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003582 }
3583 }
3584 else if(mode == MODE::DONT_MAP)
3585 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003586 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003587 }
3588 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02003589 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003590
3591 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3592 if(data)
3593 data[0xFFFF] = data[0];
3594
3595 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
3596 {
3597 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
3598
3599 VmaAllocationInfo allocInfo;
3600 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
3601 if(mode == MODE::MAP_FOR_MOMENT)
Adam Sawickib8d34d52018-10-03 17:41:20 +02003602 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003603 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02003604 TEST(allocInfo.pMappedData == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003605 }
3606
3607 switch(rand.Generate() % 3)
3608 {
3609 case 0: Sleep(0); break; // Yield.
3610 case 1: Sleep(10); break; // 10 ms
3611 // default: No sleep.
3612 }
3613
3614 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3615 if(data)
3616 data[0xFFFF] = data[0];
3617 }
3618
3619 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
3620 {
3621 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
3622 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
3623 {
3624 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
3625
3626 VmaAllocationInfo allocInfo;
3627 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003628 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003629 }
3630
3631 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
3632 }
3633 });
3634 }
3635
3636 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3637 threads[threadIndex].join();
3638
3639 vmaDestroyPool(g_hAllocator, pool);
3640 }
3641}
3642
3643static void WriteMainTestResultHeader(FILE* file)
3644{
3645 fprintf(file,
Adam Sawicki740b08f2018-08-27 13:42:07 +02003646 "Code,Time,"
3647 "Threads,Buffers and images,Sizes,Operations,Allocation strategy,Free order,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003648 "Total Time (us),"
3649 "Allocation Time Min (us),"
3650 "Allocation Time Avg (us),"
3651 "Allocation Time Max (us),"
3652 "Deallocation Time Min (us),"
3653 "Deallocation Time Avg (us),"
3654 "Deallocation Time Max (us),"
3655 "Total Memory Allocated (B),"
3656 "Free Range Size Avg (B),"
3657 "Free Range Size Max (B)\n");
3658}
3659
3660static void WriteMainTestResult(
3661 FILE* file,
3662 const char* codeDescription,
3663 const char* testDescription,
3664 const Config& config, const Result& result)
3665{
3666 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3667 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3668 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3669 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3670 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3671 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3672 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3673
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003674 std::string currTime;
3675 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003676
3677 fprintf(file,
3678 "%s,%s,%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003679 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
3680 codeDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003681 currTime.c_str(),
Adam Sawicki740b08f2018-08-27 13:42:07 +02003682 testDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01003683 totalTimeSeconds * 1e6f,
3684 allocationTimeMinSeconds * 1e6f,
3685 allocationTimeAvgSeconds * 1e6f,
3686 allocationTimeMaxSeconds * 1e6f,
3687 deallocationTimeMinSeconds * 1e6f,
3688 deallocationTimeAvgSeconds * 1e6f,
3689 deallocationTimeMaxSeconds * 1e6f,
3690 result.TotalMemoryAllocated,
3691 result.FreeRangeSizeAvg,
3692 result.FreeRangeSizeMax);
3693}
3694
3695static void WritePoolTestResultHeader(FILE* file)
3696{
3697 fprintf(file,
3698 "Code,Test,Time,"
3699 "Config,"
3700 "Total Time (us),"
3701 "Allocation Time Min (us),"
3702 "Allocation Time Avg (us),"
3703 "Allocation Time Max (us),"
3704 "Deallocation Time Min (us),"
3705 "Deallocation Time Avg (us),"
3706 "Deallocation Time Max (us),"
3707 "Lost Allocation Count,"
3708 "Lost Allocation Total Size (B),"
3709 "Failed Allocation Count,"
3710 "Failed Allocation Total Size (B)\n");
3711}
3712
3713static void WritePoolTestResult(
3714 FILE* file,
3715 const char* codeDescription,
3716 const char* testDescription,
3717 const PoolTestConfig& config,
3718 const PoolTestResult& result)
3719{
3720 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3721 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3722 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3723 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3724 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3725 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3726 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3727
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003728 std::string currTime;
3729 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003730
3731 fprintf(file,
3732 "%s,%s,%s,"
3733 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
3734 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
3735 // General
3736 codeDescription,
3737 testDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003738 currTime.c_str(),
Adam Sawickib8333fb2018-03-13 16:15:53 +01003739 // Config
3740 config.ThreadCount,
3741 (unsigned long long)config.PoolSize,
3742 config.FrameCount,
3743 config.TotalItemCount,
3744 config.UsedItemCountMin,
3745 config.UsedItemCountMax,
3746 config.ItemsToMakeUnusedPercent,
3747 // Results
3748 totalTimeSeconds * 1e6f,
3749 allocationTimeMinSeconds * 1e6f,
3750 allocationTimeAvgSeconds * 1e6f,
3751 allocationTimeMaxSeconds * 1e6f,
3752 deallocationTimeMinSeconds * 1e6f,
3753 deallocationTimeAvgSeconds * 1e6f,
3754 deallocationTimeMaxSeconds * 1e6f,
3755 result.LostAllocationCount,
3756 result.LostAllocationTotalSize,
3757 result.FailedAllocationCount,
3758 result.FailedAllocationTotalSize);
3759}
3760
3761static void PerformCustomMainTest(FILE* file)
3762{
3763 Config config{};
3764 config.RandSeed = 65735476;
3765 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
3766 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3767 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3768 config.FreeOrder = FREE_ORDER::FORWARD;
3769 config.ThreadCount = 16;
3770 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02003771 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003772
3773 // Buffers
3774 //config.AllocationSizes.push_back({4, 16, 1024});
3775 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3776
3777 // Images
3778 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3779 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3780
3781 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3782 config.AdditionalOperationCount = 1024;
3783
3784 Result result{};
3785 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003786 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003787 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
3788}
3789
3790static void PerformCustomPoolTest(FILE* file)
3791{
3792 PoolTestConfig config;
3793 config.PoolSize = 100 * 1024 * 1024;
3794 config.RandSeed = 2345764;
3795 config.ThreadCount = 1;
3796 config.FrameCount = 200;
3797 config.ItemsToMakeUnusedPercent = 2;
3798
3799 AllocationSize allocSize = {};
3800 allocSize.BufferSizeMin = 1024;
3801 allocSize.BufferSizeMax = 1024 * 1024;
3802 allocSize.Probability = 1;
3803 config.AllocationSizes.push_back(allocSize);
3804
3805 allocSize.BufferSizeMin = 0;
3806 allocSize.BufferSizeMax = 0;
3807 allocSize.ImageSizeMin = 128;
3808 allocSize.ImageSizeMax = 1024;
3809 allocSize.Probability = 1;
3810 config.AllocationSizes.push_back(allocSize);
3811
3812 config.PoolSize = config.CalcAvgResourceSize() * 200;
3813 config.UsedItemCountMax = 160;
3814 config.TotalItemCount = config.UsedItemCountMax * 10;
3815 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3816
3817 g_MemoryAliasingWarningEnabled = false;
3818 PoolTestResult result = {};
3819 TestPool_Benchmark(result, config);
3820 g_MemoryAliasingWarningEnabled = true;
3821
3822 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
3823}
3824
Adam Sawickib8333fb2018-03-13 16:15:53 +01003825static void PerformMainTests(FILE* file)
3826{
3827 uint32_t repeatCount = 1;
3828 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3829
3830 Config config{};
3831 config.RandSeed = 65735476;
3832 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3833 config.FreeOrder = FREE_ORDER::FORWARD;
3834
3835 size_t threadCountCount = 1;
3836 switch(ConfigType)
3837 {
3838 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3839 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3840 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
3841 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
3842 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
3843 default: assert(0);
3844 }
Adam Sawicki0667e332018-08-24 17:26:44 +02003845
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003846 const size_t strategyCount = GetAllocationStrategyCount();
Adam Sawicki0667e332018-08-24 17:26:44 +02003847
Adam Sawickib8333fb2018-03-13 16:15:53 +01003848 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3849 {
3850 std::string desc1;
3851
3852 switch(threadCountIndex)
3853 {
3854 case 0:
3855 desc1 += "1_thread";
3856 config.ThreadCount = 1;
3857 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3858 break;
3859 case 1:
3860 desc1 += "16_threads+0%_common";
3861 config.ThreadCount = 16;
3862 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3863 break;
3864 case 2:
3865 desc1 += "16_threads+50%_common";
3866 config.ThreadCount = 16;
3867 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3868 break;
3869 case 3:
3870 desc1 += "16_threads+100%_common";
3871 config.ThreadCount = 16;
3872 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3873 break;
3874 case 4:
3875 desc1 += "2_threads+0%_common";
3876 config.ThreadCount = 2;
3877 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3878 break;
3879 case 5:
3880 desc1 += "2_threads+50%_common";
3881 config.ThreadCount = 2;
3882 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3883 break;
3884 case 6:
3885 desc1 += "2_threads+100%_common";
3886 config.ThreadCount = 2;
3887 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3888 break;
3889 default:
3890 assert(0);
3891 }
3892
3893 // 0 = buffers, 1 = images, 2 = buffers and images
3894 size_t buffersVsImagesCount = 2;
3895 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3896 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3897 {
3898 std::string desc2 = desc1;
3899 switch(buffersVsImagesIndex)
3900 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003901 case 0: desc2 += ",Buffers"; break;
3902 case 1: desc2 += ",Images"; break;
3903 case 2: desc2 += ",Buffers+Images"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003904 default: assert(0);
3905 }
3906
3907 // 0 = small, 1 = large, 2 = small and large
3908 size_t smallVsLargeCount = 2;
3909 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3910 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3911 {
3912 std::string desc3 = desc2;
3913 switch(smallVsLargeIndex)
3914 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003915 case 0: desc3 += ",Small"; break;
3916 case 1: desc3 += ",Large"; break;
3917 case 2: desc3 += ",Small+Large"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003918 default: assert(0);
3919 }
3920
3921 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3922 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3923 else
3924 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
3925
3926 // 0 = varying sizes min...max, 1 = set of constant sizes
3927 size_t constantSizesCount = 1;
3928 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3929 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3930 {
3931 std::string desc4 = desc3;
3932 switch(constantSizesIndex)
3933 {
3934 case 0: desc4 += " Varying_sizes"; break;
3935 case 1: desc4 += " Constant_sizes"; break;
3936 default: assert(0);
3937 }
3938
3939 config.AllocationSizes.clear();
3940 // Buffers present
3941 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3942 {
3943 // Small
3944 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3945 {
3946 // Varying size
3947 if(constantSizesIndex == 0)
3948 config.AllocationSizes.push_back({4, 16, 1024});
3949 // Constant sizes
3950 else
3951 {
3952 config.AllocationSizes.push_back({1, 16, 16});
3953 config.AllocationSizes.push_back({1, 64, 64});
3954 config.AllocationSizes.push_back({1, 256, 256});
3955 config.AllocationSizes.push_back({1, 1024, 1024});
3956 }
3957 }
3958 // Large
3959 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3960 {
3961 // Varying size
3962 if(constantSizesIndex == 0)
3963 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3964 // Constant sizes
3965 else
3966 {
3967 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3968 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3969 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3970 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3971 }
3972 }
3973 }
3974 // Images present
3975 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3976 {
3977 // Small
3978 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3979 {
3980 // Varying size
3981 if(constantSizesIndex == 0)
3982 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3983 // Constant sizes
3984 else
3985 {
3986 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3987 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3988 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3989 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3990 }
3991 }
3992 // Large
3993 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3994 {
3995 // Varying size
3996 if(constantSizesIndex == 0)
3997 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3998 // Constant sizes
3999 else
4000 {
4001 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
4002 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
4003 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
4004 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
4005 }
4006 }
4007 }
4008
4009 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
4010 size_t beginBytesToAllocateCount = 1;
4011 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
4012 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
4013 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
4014 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
4015 {
4016 std::string desc5 = desc4;
4017
4018 switch(beginBytesToAllocateIndex)
4019 {
4020 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02004021 desc5 += ",Allocate_100%";
Adam Sawickib8333fb2018-03-13 16:15:53 +01004022 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
4023 config.AdditionalOperationCount = 0;
4024 break;
4025 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02004026 desc5 += ",Allocate_50%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01004027 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
4028 config.AdditionalOperationCount = 1024;
4029 break;
4030 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02004031 desc5 += ",Allocate_5%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01004032 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
4033 config.AdditionalOperationCount = 1024;
4034 break;
4035 case 3:
Adam Sawicki740b08f2018-08-27 13:42:07 +02004036 desc5 += ",Allocate_95%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01004037 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
4038 config.AdditionalOperationCount = 1024;
4039 break;
4040 default:
4041 assert(0);
4042 }
4043
Adam Sawicki0667e332018-08-24 17:26:44 +02004044 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01004045 {
Adam Sawicki0667e332018-08-24 17:26:44 +02004046 std::string desc6 = desc5;
4047 switch(strategyIndex)
4048 {
4049 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02004050 desc6 += ",BestFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02004051 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
4052 break;
4053 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02004054 desc6 += ",WorstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02004055 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
4056 break;
4057 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02004058 desc6 += ",FirstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02004059 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
4060 break;
4061 default:
4062 assert(0);
4063 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01004064
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004065 desc6 += ',';
4066 desc6 += FREE_ORDER_NAMES[(uint32_t)config.FreeOrder];
Adam Sawicki740b08f2018-08-27 13:42:07 +02004067
4068 const char* testDescription = desc6.c_str();
Adam Sawicki0667e332018-08-24 17:26:44 +02004069
4070 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
4071 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02004072 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawicki0667e332018-08-24 17:26:44 +02004073
4074 Result result{};
4075 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004076 TEST(res == VK_SUCCESS);
Adam Sawicki740b08f2018-08-27 13:42:07 +02004077 if(file)
4078 {
4079 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
4080 }
Adam Sawicki0667e332018-08-24 17:26:44 +02004081 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01004082 }
4083 }
4084 }
4085 }
4086 }
4087 }
4088}
4089
4090static void PerformPoolTests(FILE* file)
4091{
4092 const size_t AVG_RESOURCES_PER_POOL = 300;
4093
4094 uint32_t repeatCount = 1;
4095 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
4096
4097 PoolTestConfig config{};
4098 config.RandSeed = 2346343;
4099 config.FrameCount = 200;
4100 config.ItemsToMakeUnusedPercent = 2;
4101
4102 size_t threadCountCount = 1;
4103 switch(ConfigType)
4104 {
4105 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
4106 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
4107 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
4108 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
4109 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
4110 default: assert(0);
4111 }
4112 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
4113 {
4114 std::string desc1;
4115
4116 switch(threadCountIndex)
4117 {
4118 case 0:
4119 desc1 += "1_thread";
4120 config.ThreadCount = 1;
4121 break;
4122 case 1:
4123 desc1 += "16_threads";
4124 config.ThreadCount = 16;
4125 break;
4126 case 2:
4127 desc1 += "2_threads";
4128 config.ThreadCount = 2;
4129 break;
4130 default:
4131 assert(0);
4132 }
4133
4134 // 0 = buffers, 1 = images, 2 = buffers and images
4135 size_t buffersVsImagesCount = 2;
4136 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
4137 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
4138 {
4139 std::string desc2 = desc1;
4140 switch(buffersVsImagesIndex)
4141 {
4142 case 0: desc2 += " Buffers"; break;
4143 case 1: desc2 += " Images"; break;
4144 case 2: desc2 += " Buffers+Images"; break;
4145 default: assert(0);
4146 }
4147
4148 // 0 = small, 1 = large, 2 = small and large
4149 size_t smallVsLargeCount = 2;
4150 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
4151 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
4152 {
4153 std::string desc3 = desc2;
4154 switch(smallVsLargeIndex)
4155 {
4156 case 0: desc3 += " Small"; break;
4157 case 1: desc3 += " Large"; break;
4158 case 2: desc3 += " Small+Large"; break;
4159 default: assert(0);
4160 }
4161
4162 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
4163 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
4164 else
4165 config.PoolSize = 4ull * 1024 * 1024;
4166
4167 // 0 = varying sizes min...max, 1 = set of constant sizes
4168 size_t constantSizesCount = 1;
4169 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
4170 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
4171 {
4172 std::string desc4 = desc3;
4173 switch(constantSizesIndex)
4174 {
4175 case 0: desc4 += " Varying_sizes"; break;
4176 case 1: desc4 += " Constant_sizes"; break;
4177 default: assert(0);
4178 }
4179
4180 config.AllocationSizes.clear();
4181 // Buffers present
4182 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
4183 {
4184 // Small
4185 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
4186 {
4187 // Varying size
4188 if(constantSizesIndex == 0)
4189 config.AllocationSizes.push_back({4, 16, 1024});
4190 // Constant sizes
4191 else
4192 {
4193 config.AllocationSizes.push_back({1, 16, 16});
4194 config.AllocationSizes.push_back({1, 64, 64});
4195 config.AllocationSizes.push_back({1, 256, 256});
4196 config.AllocationSizes.push_back({1, 1024, 1024});
4197 }
4198 }
4199 // Large
4200 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
4201 {
4202 // Varying size
4203 if(constantSizesIndex == 0)
4204 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
4205 // Constant sizes
4206 else
4207 {
4208 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
4209 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
4210 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
4211 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
4212 }
4213 }
4214 }
4215 // Images present
4216 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
4217 {
4218 // Small
4219 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
4220 {
4221 // Varying size
4222 if(constantSizesIndex == 0)
4223 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
4224 // Constant sizes
4225 else
4226 {
4227 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
4228 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
4229 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
4230 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
4231 }
4232 }
4233 // Large
4234 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
4235 {
4236 // Varying size
4237 if(constantSizesIndex == 0)
4238 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
4239 // Constant sizes
4240 else
4241 {
4242 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
4243 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
4244 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
4245 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
4246 }
4247 }
4248 }
4249
4250 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
4251 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
4252
4253 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
4254 size_t subscriptionModeCount;
4255 switch(ConfigType)
4256 {
4257 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
4258 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
4259 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
4260 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
4261 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
4262 default: assert(0);
4263 }
4264 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
4265 {
4266 std::string desc5 = desc4;
4267
4268 switch(subscriptionModeIndex)
4269 {
4270 case 0:
4271 desc5 += " Subscription_66%";
4272 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
4273 break;
4274 case 1:
4275 desc5 += " Subscription_133%";
4276 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
4277 break;
4278 case 2:
4279 desc5 += " Subscription_100%";
4280 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
4281 break;
4282 case 3:
4283 desc5 += " Subscription_33%";
4284 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
4285 break;
4286 case 4:
4287 desc5 += " Subscription_166%";
4288 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
4289 break;
4290 default:
4291 assert(0);
4292 }
4293
4294 config.TotalItemCount = config.UsedItemCountMax * 5;
4295 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
4296
4297 const char* testDescription = desc5.c_str();
4298
4299 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
4300 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02004301 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004302
4303 PoolTestResult result{};
4304 g_MemoryAliasingWarningEnabled = false;
4305 TestPool_Benchmark(result, config);
4306 g_MemoryAliasingWarningEnabled = true;
4307 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
4308 }
4309 }
4310 }
4311 }
4312 }
4313 }
4314}
4315
Adam Sawickia83793a2018-09-03 13:40:42 +02004316static void BasicTestBuddyAllocator()
4317{
4318 wprintf(L"Basic test buddy allocator\n");
4319
4320 RandomNumberGenerator rand{76543};
4321
4322 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4323 sampleBufCreateInfo.size = 1024; // Whatever.
4324 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4325
4326 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
4327 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4328
4329 VmaPoolCreateInfo poolCreateInfo = {};
4330 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004331 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004332
Adam Sawickid6e6d6b2018-09-21 14:07:02 +02004333 // Deliberately adding 1023 to test usable size smaller than memory block size.
4334 poolCreateInfo.blockSize = 1024 * 1024 + 1023;
Adam Sawickia83793a2018-09-03 13:40:42 +02004335 poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
Adam Sawicki80927152018-09-07 17:27:23 +02004336 //poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
Adam Sawickia83793a2018-09-03 13:40:42 +02004337
4338 VmaPool pool = nullptr;
4339 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004340 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004341
4342 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
4343
4344 VmaAllocationCreateInfo allocCreateInfo = {};
4345 allocCreateInfo.pool = pool;
4346
4347 std::vector<BufferInfo> bufInfo;
4348 BufferInfo newBufInfo;
4349 VmaAllocationInfo allocInfo;
4350
4351 bufCreateInfo.size = 1024 * 256;
4352 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4353 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004354 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004355 bufInfo.push_back(newBufInfo);
4356
4357 bufCreateInfo.size = 1024 * 512;
4358 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4359 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004360 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004361 bufInfo.push_back(newBufInfo);
4362
4363 bufCreateInfo.size = 1024 * 128;
4364 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4365 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004366 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004367 bufInfo.push_back(newBufInfo);
Adam Sawickia01d4582018-09-21 14:22:35 +02004368
4369 // Test very small allocation, smaller than minimum node size.
4370 bufCreateInfo.size = 1;
4371 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4372 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004373 TEST(res == VK_SUCCESS);
Adam Sawickia01d4582018-09-21 14:22:35 +02004374 bufInfo.push_back(newBufInfo);
Adam Sawickia83793a2018-09-03 13:40:42 +02004375
Adam Sawicki9933c5c2018-09-21 14:57:24 +02004376 // Test some small allocation with alignment requirement.
4377 {
4378 VkMemoryRequirements memReq;
4379 memReq.alignment = 256;
4380 memReq.memoryTypeBits = UINT32_MAX;
4381 memReq.size = 32;
4382
4383 newBufInfo.Buffer = VK_NULL_HANDLE;
4384 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo,
4385 &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004386 TEST(res == VK_SUCCESS);
4387 TEST(allocInfo.offset % memReq.alignment == 0);
Adam Sawicki9933c5c2018-09-21 14:57:24 +02004388 bufInfo.push_back(newBufInfo);
4389 }
4390
4391 //SaveAllocatorStatsToFile(L"TEST.json");
4392
Adam Sawicki21017c62018-09-07 15:26:59 +02004393 VmaPoolStats stats = {};
4394 vmaGetPoolStats(g_hAllocator, pool, &stats);
4395 int DBG = 0; // Set breakpoint here to inspect `stats`.
4396
Adam Sawicki80927152018-09-07 17:27:23 +02004397 // Allocate enough new buffers to surely fall into second block.
4398 for(uint32_t i = 0; i < 32; ++i)
4399 {
4400 bufCreateInfo.size = 1024 * (rand.Generate() % 32 + 1);
4401 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4402 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004403 TEST(res == VK_SUCCESS);
Adam Sawicki80927152018-09-07 17:27:23 +02004404 bufInfo.push_back(newBufInfo);
4405 }
4406
4407 SaveAllocatorStatsToFile(L"BuddyTest01.json");
4408
Adam Sawickia83793a2018-09-03 13:40:42 +02004409 // Destroy the buffers in random order.
4410 while(!bufInfo.empty())
4411 {
4412 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
4413 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
4414 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
4415 bufInfo.erase(bufInfo.begin() + indexToDestroy);
4416 }
4417
4418 vmaDestroyPool(g_hAllocator, pool);
4419}
4420
Adam Sawickib8333fb2018-03-13 16:15:53 +01004421void Test()
4422{
4423 wprintf(L"TESTING:\n");
4424
Adam Sawickic6432d12018-09-21 16:44:16 +02004425 if(false)
Adam Sawicki70a683e2018-08-24 15:36:32 +02004426 {
4427 // # Temporarily insert custom tests here
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02004428 // ########################################
4429 // ########################################
Adam Sawicki80927152018-09-07 17:27:23 +02004430
Adam Sawickib0c36362018-11-13 16:17:38 +01004431 TestResize();
Adam Sawicki70a683e2018-08-24 15:36:32 +02004432 return;
4433 }
4434
Adam Sawickib8333fb2018-03-13 16:15:53 +01004435 // # Simple tests
4436
4437 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02004438#if VMA_DEBUG_MARGIN
4439 TestDebugMargin();
4440#else
4441 TestPool_SameSize();
4442 TestHeapSizeLimit();
Adam Sawickib0c36362018-11-13 16:17:38 +01004443 TestResize();
Adam Sawicki212a4a62018-06-14 15:44:45 +02004444#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02004445#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
4446 TestAllocationsInitialization();
4447#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01004448 TestMapping();
4449 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02004450 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02004451 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02004452 TestLinearAllocatorMultiBlock();
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004453
Adam Sawicki4338f662018-09-07 14:12:37 +02004454 BasicTestBuddyAllocator();
4455
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004456 {
4457 FILE* file;
Adam Sawickic6432d12018-09-21 16:44:16 +02004458 fopen_s(&file, "Algorithms.csv", "w");
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004459 assert(file != NULL);
Adam Sawicki80927152018-09-07 17:27:23 +02004460 BenchmarkAlgorithms(file);
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004461 fclose(file);
4462 }
4463
Adam Sawickib8333fb2018-03-13 16:15:53 +01004464 TestDefragmentationSimple();
4465 TestDefragmentationFull();
4466
4467 // # Detailed tests
4468 FILE* file;
4469 fopen_s(&file, "Results.csv", "w");
4470 assert(file != NULL);
4471
4472 WriteMainTestResultHeader(file);
4473 PerformMainTests(file);
4474 //PerformCustomMainTest(file);
4475
4476 WritePoolTestResultHeader(file);
4477 PerformPoolTests(file);
4478 //PerformCustomPoolTest(file);
4479
4480 fclose(file);
4481
4482 wprintf(L"Done.\n");
4483}
4484
Adam Sawickif1a793c2018-03-13 15:42:22 +01004485#endif // #ifdef _WIN32