blob: e339f9ce49a5ce91df52a7ef59cd6a52d4dd5f46 [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 Sawickie44c6262018-06-15 14:30:39 +02002714static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
2715{
2716 const uint8_t* pBytes = (const uint8_t*)pMemory;
2717 for(size_t i = 0; i < size; ++i)
2718 {
2719 if(pBytes[i] != pattern)
2720 {
2721 return false;
2722 }
2723 }
2724 return true;
2725}
2726
2727static void TestAllocationsInitialization()
2728{
2729 VkResult res;
2730
2731 const size_t BUF_SIZE = 1024;
2732
2733 // Create pool.
2734
2735 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2736 bufInfo.size = BUF_SIZE;
2737 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2738
2739 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
2740 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2741
2742 VmaPoolCreateInfo poolCreateInfo = {};
2743 poolCreateInfo.blockSize = BUF_SIZE * 10;
2744 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
2745 poolCreateInfo.maxBlockCount = 1;
2746 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002747 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002748
2749 VmaAllocationCreateInfo bufAllocCreateInfo = {};
2750 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002751 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002752
2753 // Create one persistently mapped buffer to keep memory of this block mapped,
2754 // so that pointer to mapped data will remain (more or less...) valid even
2755 // after destruction of other allocations.
2756
2757 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2758 VkBuffer firstBuf;
2759 VmaAllocation firstAlloc;
2760 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002761 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002762
2763 // Test buffers.
2764
2765 for(uint32_t i = 0; i < 2; ++i)
2766 {
2767 const bool persistentlyMapped = i == 0;
2768 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
2769 VkBuffer buf;
2770 VmaAllocation alloc;
2771 VmaAllocationInfo allocInfo;
2772 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002773 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002774
2775 void* pMappedData;
2776 if(!persistentlyMapped)
2777 {
2778 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002779 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02002780 }
2781 else
2782 {
2783 pMappedData = allocInfo.pMappedData;
2784 }
2785
2786 // Validate initialized content
2787 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002788 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02002789
2790 if(!persistentlyMapped)
2791 {
2792 vmaUnmapMemory(g_hAllocator, alloc);
2793 }
2794
2795 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2796
2797 // Validate freed content
2798 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002799 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02002800 }
2801
2802 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
2803 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
2804}
2805
Adam Sawickib8333fb2018-03-13 16:15:53 +01002806static void TestPool_Benchmark(
2807 PoolTestResult& outResult,
2808 const PoolTestConfig& config)
2809{
Adam Sawickib8d34d52018-10-03 17:41:20 +02002810 TEST(config.ThreadCount > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002811
2812 RandomNumberGenerator mainRand{config.RandSeed};
2813
2814 uint32_t allocationSizeProbabilitySum = std::accumulate(
2815 config.AllocationSizes.begin(),
2816 config.AllocationSizes.end(),
2817 0u,
2818 [](uint32_t sum, const AllocationSize& allocSize) {
2819 return sum + allocSize.Probability;
2820 });
2821
2822 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2823 bufferInfo.size = 256; // Whatever.
2824 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2825
2826 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2827 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2828 imageInfo.extent.width = 256; // Whatever.
2829 imageInfo.extent.height = 256; // Whatever.
2830 imageInfo.extent.depth = 1;
2831 imageInfo.mipLevels = 1;
2832 imageInfo.arrayLayers = 1;
2833 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2834 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
2835 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2836 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
2837 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2838
2839 uint32_t bufferMemoryTypeBits = UINT32_MAX;
2840 {
2841 VkBuffer dummyBuffer;
2842 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002843 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002844
2845 VkMemoryRequirements memReq;
2846 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2847 bufferMemoryTypeBits = memReq.memoryTypeBits;
2848
2849 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2850 }
2851
2852 uint32_t imageMemoryTypeBits = UINT32_MAX;
2853 {
2854 VkImage dummyImage;
2855 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002856 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002857
2858 VkMemoryRequirements memReq;
2859 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
2860 imageMemoryTypeBits = memReq.memoryTypeBits;
2861
2862 vkDestroyImage(g_hDevice, dummyImage, nullptr);
2863 }
2864
2865 uint32_t memoryTypeBits = 0;
2866 if(config.UsesBuffers() && config.UsesImages())
2867 {
2868 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
2869 if(memoryTypeBits == 0)
2870 {
2871 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
2872 return;
2873 }
2874 }
2875 else if(config.UsesBuffers())
2876 memoryTypeBits = bufferMemoryTypeBits;
2877 else if(config.UsesImages())
2878 memoryTypeBits = imageMemoryTypeBits;
2879 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02002880 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002881
2882 VmaPoolCreateInfo poolCreateInfo = {};
2883 poolCreateInfo.memoryTypeIndex = 0;
2884 poolCreateInfo.minBlockCount = 1;
2885 poolCreateInfo.maxBlockCount = 1;
2886 poolCreateInfo.blockSize = config.PoolSize;
2887 poolCreateInfo.frameInUseCount = 1;
2888
2889 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
2890 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2891 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2892
2893 VmaPool pool;
2894 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002895 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002896
2897 // Start time measurement - after creating pool and initializing data structures.
2898 time_point timeBeg = std::chrono::high_resolution_clock::now();
2899
2900 ////////////////////////////////////////////////////////////////////////////////
2901 // ThreadProc
2902 auto ThreadProc = [&](
2903 PoolTestThreadResult* outThreadResult,
2904 uint32_t randSeed,
2905 HANDLE frameStartEvent,
2906 HANDLE frameEndEvent) -> void
2907 {
2908 RandomNumberGenerator threadRand{randSeed};
2909
2910 outThreadResult->AllocationTimeMin = duration::max();
2911 outThreadResult->AllocationTimeSum = duration::zero();
2912 outThreadResult->AllocationTimeMax = duration::min();
2913 outThreadResult->DeallocationTimeMin = duration::max();
2914 outThreadResult->DeallocationTimeSum = duration::zero();
2915 outThreadResult->DeallocationTimeMax = duration::min();
2916 outThreadResult->AllocationCount = 0;
2917 outThreadResult->DeallocationCount = 0;
2918 outThreadResult->LostAllocationCount = 0;
2919 outThreadResult->LostAllocationTotalSize = 0;
2920 outThreadResult->FailedAllocationCount = 0;
2921 outThreadResult->FailedAllocationTotalSize = 0;
2922
2923 struct Item
2924 {
2925 VkDeviceSize BufferSize;
2926 VkExtent2D ImageSize;
2927 VkBuffer Buf;
2928 VkImage Image;
2929 VmaAllocation Alloc;
2930
2931 VkDeviceSize CalcSizeBytes() const
2932 {
2933 return BufferSize +
2934 ImageSize.width * ImageSize.height * 4;
2935 }
2936 };
2937 std::vector<Item> unusedItems, usedItems;
2938
2939 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
2940
2941 // Create all items - all unused, not yet allocated.
2942 for(size_t i = 0; i < threadTotalItemCount; ++i)
2943 {
2944 Item item = {};
2945
2946 uint32_t allocSizeIndex = 0;
2947 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
2948 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
2949 r -= config.AllocationSizes[allocSizeIndex++].Probability;
2950
2951 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
2952 if(allocSize.BufferSizeMax > 0)
2953 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02002954 TEST(allocSize.BufferSizeMin > 0);
2955 TEST(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002956 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
2957 item.BufferSize = allocSize.BufferSizeMin;
2958 else
2959 {
2960 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
2961 item.BufferSize = item.BufferSize / 16 * 16;
2962 }
2963 }
2964 else
2965 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02002966 TEST(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002967 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
2968 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
2969 else
2970 {
2971 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2972 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2973 }
2974 }
2975
2976 unusedItems.push_back(item);
2977 }
2978
2979 auto Allocate = [&](Item& item) -> VkResult
2980 {
2981 VmaAllocationCreateInfo allocCreateInfo = {};
2982 allocCreateInfo.pool = pool;
2983 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2984 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2985
2986 if(item.BufferSize)
2987 {
2988 bufferInfo.size = item.BufferSize;
2989 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2990 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
2991 }
2992 else
2993 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02002994 TEST(item.ImageSize.width && item.ImageSize.height);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002995
2996 imageInfo.extent.width = item.ImageSize.width;
2997 imageInfo.extent.height = item.ImageSize.height;
2998 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2999 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
3000 }
3001 };
3002
3003 ////////////////////////////////////////////////////////////////////////////////
3004 // Frames
3005 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
3006 {
3007 WaitForSingleObject(frameStartEvent, INFINITE);
3008
3009 // Always make some percent of used bufs unused, to choose different used ones.
3010 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
3011 for(size_t i = 0; i < bufsToMakeUnused; ++i)
3012 {
3013 size_t index = threadRand.Generate() % usedItems.size();
3014 unusedItems.push_back(usedItems[index]);
3015 usedItems.erase(usedItems.begin() + index);
3016 }
3017
3018 // Determine which bufs we want to use in this frame.
3019 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
3020 / config.ThreadCount;
Adam Sawickib8d34d52018-10-03 17:41:20 +02003021 TEST(usedBufCount < usedItems.size() + unusedItems.size());
Adam Sawickib8333fb2018-03-13 16:15:53 +01003022 // Move some used to unused.
3023 while(usedBufCount < usedItems.size())
3024 {
3025 size_t index = threadRand.Generate() % usedItems.size();
3026 unusedItems.push_back(usedItems[index]);
3027 usedItems.erase(usedItems.begin() + index);
3028 }
3029 // Move some unused to used.
3030 while(usedBufCount > usedItems.size())
3031 {
3032 size_t index = threadRand.Generate() % unusedItems.size();
3033 usedItems.push_back(unusedItems[index]);
3034 unusedItems.erase(unusedItems.begin() + index);
3035 }
3036
3037 uint32_t touchExistingCount = 0;
3038 uint32_t touchLostCount = 0;
3039 uint32_t createSucceededCount = 0;
3040 uint32_t createFailedCount = 0;
3041
3042 // Touch all used bufs. If not created or lost, allocate.
3043 for(size_t i = 0; i < usedItems.size(); ++i)
3044 {
3045 Item& item = usedItems[i];
3046 // Not yet created.
3047 if(item.Alloc == VK_NULL_HANDLE)
3048 {
3049 res = Allocate(item);
3050 ++outThreadResult->AllocationCount;
3051 if(res != VK_SUCCESS)
3052 {
3053 item.Alloc = VK_NULL_HANDLE;
3054 item.Buf = VK_NULL_HANDLE;
3055 ++outThreadResult->FailedAllocationCount;
3056 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
3057 ++createFailedCount;
3058 }
3059 else
3060 ++createSucceededCount;
3061 }
3062 else
3063 {
3064 // Touch.
3065 VmaAllocationInfo allocInfo;
3066 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
3067 // Lost.
3068 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3069 {
3070 ++touchLostCount;
3071
3072 // Destroy.
3073 {
3074 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
3075 if(item.Buf)
3076 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
3077 else
3078 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
3079 ++outThreadResult->DeallocationCount;
3080 }
3081 item.Alloc = VK_NULL_HANDLE;
3082 item.Buf = VK_NULL_HANDLE;
3083
3084 ++outThreadResult->LostAllocationCount;
3085 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
3086
3087 // Recreate.
3088 res = Allocate(item);
3089 ++outThreadResult->AllocationCount;
3090 // Creation failed.
3091 if(res != VK_SUCCESS)
3092 {
3093 ++outThreadResult->FailedAllocationCount;
3094 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
3095 ++createFailedCount;
3096 }
3097 else
3098 ++createSucceededCount;
3099 }
3100 else
3101 ++touchExistingCount;
3102 }
3103 }
3104
3105 /*
3106 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
3107 randSeed, frameIndex,
3108 touchExistingCount, touchLostCount,
3109 createSucceededCount, createFailedCount);
3110 */
3111
3112 SetEvent(frameEndEvent);
3113 }
3114
3115 // Free all remaining items.
3116 for(size_t i = usedItems.size(); i--; )
3117 {
3118 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
3119 if(usedItems[i].Buf)
3120 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
3121 else
3122 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
3123 ++outThreadResult->DeallocationCount;
3124 }
3125 for(size_t i = unusedItems.size(); i--; )
3126 {
3127 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
3128 if(unusedItems[i].Buf)
3129 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
3130 else
3131 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
3132 ++outThreadResult->DeallocationCount;
3133 }
3134 };
3135
3136 // Launch threads.
3137 uint32_t threadRandSeed = mainRand.Generate();
3138 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
3139 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
3140 std::vector<std::thread> bkgThreads;
3141 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
3142 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3143 {
3144 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
3145 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
3146 bkgThreads.emplace_back(std::bind(
3147 ThreadProc,
3148 &threadResults[threadIndex],
3149 threadRandSeed + threadIndex,
3150 frameStartEvents[threadIndex],
3151 frameEndEvents[threadIndex]));
3152 }
3153
3154 // Execute frames.
Adam Sawickib8d34d52018-10-03 17:41:20 +02003155 TEST(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003156 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
3157 {
3158 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
3159 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3160 SetEvent(frameStartEvents[threadIndex]);
3161 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
3162 }
3163
3164 // Wait for threads finished
3165 for(size_t i = 0; i < bkgThreads.size(); ++i)
3166 {
3167 bkgThreads[i].join();
3168 CloseHandle(frameEndEvents[i]);
3169 CloseHandle(frameStartEvents[i]);
3170 }
3171 bkgThreads.clear();
3172
3173 // Finish time measurement - before destroying pool.
3174 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
3175
3176 vmaDestroyPool(g_hAllocator, pool);
3177
3178 outResult.AllocationTimeMin = duration::max();
3179 outResult.AllocationTimeAvg = duration::zero();
3180 outResult.AllocationTimeMax = duration::min();
3181 outResult.DeallocationTimeMin = duration::max();
3182 outResult.DeallocationTimeAvg = duration::zero();
3183 outResult.DeallocationTimeMax = duration::min();
3184 outResult.LostAllocationCount = 0;
3185 outResult.LostAllocationTotalSize = 0;
3186 outResult.FailedAllocationCount = 0;
3187 outResult.FailedAllocationTotalSize = 0;
3188 size_t allocationCount = 0;
3189 size_t deallocationCount = 0;
3190 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3191 {
3192 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
3193 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
3194 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
3195 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
3196 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
3197 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
3198 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
3199 allocationCount += threadResult.AllocationCount;
3200 deallocationCount += threadResult.DeallocationCount;
3201 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
3202 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
3203 outResult.LostAllocationCount += threadResult.LostAllocationCount;
3204 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
3205 }
3206 if(allocationCount)
3207 outResult.AllocationTimeAvg /= allocationCount;
3208 if(deallocationCount)
3209 outResult.DeallocationTimeAvg /= deallocationCount;
3210}
3211
3212static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
3213{
3214 if(ptr1 < ptr2)
3215 return ptr1 + size1 > ptr2;
3216 else if(ptr2 < ptr1)
3217 return ptr2 + size2 > ptr1;
3218 else
3219 return true;
3220}
3221
3222static void TestMapping()
3223{
3224 wprintf(L"Testing mapping...\n");
3225
3226 VkResult res;
3227 uint32_t memTypeIndex = UINT32_MAX;
3228
3229 enum TEST
3230 {
3231 TEST_NORMAL,
3232 TEST_POOL,
3233 TEST_DEDICATED,
3234 TEST_COUNT
3235 };
3236 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3237 {
3238 VmaPool pool = nullptr;
3239 if(testIndex == TEST_POOL)
3240 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003241 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003242 VmaPoolCreateInfo poolInfo = {};
3243 poolInfo.memoryTypeIndex = memTypeIndex;
3244 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003245 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003246 }
3247
3248 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3249 bufInfo.size = 0x10000;
3250 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3251
3252 VmaAllocationCreateInfo allocCreateInfo = {};
3253 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3254 allocCreateInfo.pool = pool;
3255 if(testIndex == TEST_DEDICATED)
3256 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3257
3258 VmaAllocationInfo allocInfo;
3259
3260 // Mapped manually
3261
3262 // Create 2 buffers.
3263 BufferInfo bufferInfos[3];
3264 for(size_t i = 0; i < 2; ++i)
3265 {
3266 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3267 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003268 TEST(res == VK_SUCCESS);
3269 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003270 memTypeIndex = allocInfo.memoryType;
3271 }
3272
3273 // Map buffer 0.
3274 char* data00 = nullptr;
3275 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003276 TEST(res == VK_SUCCESS && data00 != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003277 data00[0xFFFF] = data00[0];
3278
3279 // Map buffer 0 second time.
3280 char* data01 = nullptr;
3281 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003282 TEST(res == VK_SUCCESS && data01 == data00);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003283
3284 // Map buffer 1.
3285 char* data1 = nullptr;
3286 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003287 TEST(res == VK_SUCCESS && data1 != nullptr);
3288 TEST(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
Adam Sawickib8333fb2018-03-13 16:15:53 +01003289 data1[0xFFFF] = data1[0];
3290
3291 // Unmap buffer 0 two times.
3292 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3293 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3294 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003295 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003296
3297 // Unmap buffer 1.
3298 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
3299 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003300 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003301
3302 // Create 3rd buffer - persistently mapped.
3303 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3304 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3305 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003306 TEST(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003307
3308 // Map buffer 2.
3309 char* data2 = nullptr;
3310 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003311 TEST(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003312 data2[0xFFFF] = data2[0];
3313
3314 // Unmap buffer 2.
3315 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
3316 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003317 TEST(allocInfo.pMappedData == data2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003318
3319 // Destroy all buffers.
3320 for(size_t i = 3; i--; )
3321 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
3322
3323 vmaDestroyPool(g_hAllocator, pool);
3324 }
3325}
3326
3327static void TestMappingMultithreaded()
3328{
3329 wprintf(L"Testing mapping multithreaded...\n");
3330
3331 static const uint32_t threadCount = 16;
3332 static const uint32_t bufferCount = 1024;
3333 static const uint32_t threadBufferCount = bufferCount / threadCount;
3334
3335 VkResult res;
3336 volatile uint32_t memTypeIndex = UINT32_MAX;
3337
3338 enum TEST
3339 {
3340 TEST_NORMAL,
3341 TEST_POOL,
3342 TEST_DEDICATED,
3343 TEST_COUNT
3344 };
3345 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3346 {
3347 VmaPool pool = nullptr;
3348 if(testIndex == TEST_POOL)
3349 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003350 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003351 VmaPoolCreateInfo poolInfo = {};
3352 poolInfo.memoryTypeIndex = memTypeIndex;
3353 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003354 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003355 }
3356
3357 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3358 bufCreateInfo.size = 0x10000;
3359 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3360
3361 VmaAllocationCreateInfo allocCreateInfo = {};
3362 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3363 allocCreateInfo.pool = pool;
3364 if(testIndex == TEST_DEDICATED)
3365 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3366
3367 std::thread threads[threadCount];
3368 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3369 {
3370 threads[threadIndex] = std::thread([=, &memTypeIndex](){
3371 // ======== THREAD FUNCTION ========
3372
3373 RandomNumberGenerator rand{threadIndex};
3374
3375 enum class MODE
3376 {
3377 // Don't map this buffer at all.
3378 DONT_MAP,
3379 // Map and quickly unmap.
3380 MAP_FOR_MOMENT,
3381 // Map and unmap before destruction.
3382 MAP_FOR_LONGER,
3383 // Map two times. Quickly unmap, second unmap before destruction.
3384 MAP_TWO_TIMES,
3385 // Create this buffer as persistently mapped.
3386 PERSISTENTLY_MAPPED,
3387 COUNT
3388 };
3389 std::vector<BufferInfo> bufInfos{threadBufferCount};
3390 std::vector<MODE> bufModes{threadBufferCount};
3391
3392 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
3393 {
3394 BufferInfo& bufInfo = bufInfos[bufferIndex];
3395 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
3396 bufModes[bufferIndex] = mode;
3397
3398 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
3399 if(mode == MODE::PERSISTENTLY_MAPPED)
3400 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3401
3402 VmaAllocationInfo allocInfo;
3403 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
3404 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003405 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003406
3407 if(memTypeIndex == UINT32_MAX)
3408 memTypeIndex = allocInfo.memoryType;
3409
3410 char* data = nullptr;
3411
3412 if(mode == MODE::PERSISTENTLY_MAPPED)
3413 {
3414 data = (char*)allocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02003415 TEST(data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003416 }
3417 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
3418 mode == MODE::MAP_TWO_TIMES)
3419 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003420 TEST(data == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003421 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003422 TEST(res == VK_SUCCESS && data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003423
3424 if(mode == MODE::MAP_TWO_TIMES)
3425 {
3426 char* data2 = nullptr;
3427 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003428 TEST(res == VK_SUCCESS && data2 == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003429 }
3430 }
3431 else if(mode == MODE::DONT_MAP)
3432 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003433 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003434 }
3435 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02003436 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003437
3438 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3439 if(data)
3440 data[0xFFFF] = data[0];
3441
3442 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
3443 {
3444 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
3445
3446 VmaAllocationInfo allocInfo;
3447 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
3448 if(mode == MODE::MAP_FOR_MOMENT)
Adam Sawickib8d34d52018-10-03 17:41:20 +02003449 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003450 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02003451 TEST(allocInfo.pMappedData == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003452 }
3453
3454 switch(rand.Generate() % 3)
3455 {
3456 case 0: Sleep(0); break; // Yield.
3457 case 1: Sleep(10); break; // 10 ms
3458 // default: No sleep.
3459 }
3460
3461 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3462 if(data)
3463 data[0xFFFF] = data[0];
3464 }
3465
3466 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
3467 {
3468 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
3469 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
3470 {
3471 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
3472
3473 VmaAllocationInfo allocInfo;
3474 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003475 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003476 }
3477
3478 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
3479 }
3480 });
3481 }
3482
3483 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3484 threads[threadIndex].join();
3485
3486 vmaDestroyPool(g_hAllocator, pool);
3487 }
3488}
3489
3490static void WriteMainTestResultHeader(FILE* file)
3491{
3492 fprintf(file,
Adam Sawicki740b08f2018-08-27 13:42:07 +02003493 "Code,Time,"
3494 "Threads,Buffers and images,Sizes,Operations,Allocation strategy,Free order,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003495 "Total Time (us),"
3496 "Allocation Time Min (us),"
3497 "Allocation Time Avg (us),"
3498 "Allocation Time Max (us),"
3499 "Deallocation Time Min (us),"
3500 "Deallocation Time Avg (us),"
3501 "Deallocation Time Max (us),"
3502 "Total Memory Allocated (B),"
3503 "Free Range Size Avg (B),"
3504 "Free Range Size Max (B)\n");
3505}
3506
3507static void WriteMainTestResult(
3508 FILE* file,
3509 const char* codeDescription,
3510 const char* testDescription,
3511 const Config& config, const Result& result)
3512{
3513 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3514 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3515 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3516 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3517 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3518 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3519 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3520
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003521 std::string currTime;
3522 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003523
3524 fprintf(file,
3525 "%s,%s,%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003526 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
3527 codeDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003528 currTime.c_str(),
Adam Sawicki740b08f2018-08-27 13:42:07 +02003529 testDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01003530 totalTimeSeconds * 1e6f,
3531 allocationTimeMinSeconds * 1e6f,
3532 allocationTimeAvgSeconds * 1e6f,
3533 allocationTimeMaxSeconds * 1e6f,
3534 deallocationTimeMinSeconds * 1e6f,
3535 deallocationTimeAvgSeconds * 1e6f,
3536 deallocationTimeMaxSeconds * 1e6f,
3537 result.TotalMemoryAllocated,
3538 result.FreeRangeSizeAvg,
3539 result.FreeRangeSizeMax);
3540}
3541
3542static void WritePoolTestResultHeader(FILE* file)
3543{
3544 fprintf(file,
3545 "Code,Test,Time,"
3546 "Config,"
3547 "Total Time (us),"
3548 "Allocation Time Min (us),"
3549 "Allocation Time Avg (us),"
3550 "Allocation Time Max (us),"
3551 "Deallocation Time Min (us),"
3552 "Deallocation Time Avg (us),"
3553 "Deallocation Time Max (us),"
3554 "Lost Allocation Count,"
3555 "Lost Allocation Total Size (B),"
3556 "Failed Allocation Count,"
3557 "Failed Allocation Total Size (B)\n");
3558}
3559
3560static void WritePoolTestResult(
3561 FILE* file,
3562 const char* codeDescription,
3563 const char* testDescription,
3564 const PoolTestConfig& config,
3565 const PoolTestResult& result)
3566{
3567 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3568 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3569 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3570 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3571 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3572 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3573 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3574
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003575 std::string currTime;
3576 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003577
3578 fprintf(file,
3579 "%s,%s,%s,"
3580 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
3581 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
3582 // General
3583 codeDescription,
3584 testDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003585 currTime.c_str(),
Adam Sawickib8333fb2018-03-13 16:15:53 +01003586 // Config
3587 config.ThreadCount,
3588 (unsigned long long)config.PoolSize,
3589 config.FrameCount,
3590 config.TotalItemCount,
3591 config.UsedItemCountMin,
3592 config.UsedItemCountMax,
3593 config.ItemsToMakeUnusedPercent,
3594 // Results
3595 totalTimeSeconds * 1e6f,
3596 allocationTimeMinSeconds * 1e6f,
3597 allocationTimeAvgSeconds * 1e6f,
3598 allocationTimeMaxSeconds * 1e6f,
3599 deallocationTimeMinSeconds * 1e6f,
3600 deallocationTimeAvgSeconds * 1e6f,
3601 deallocationTimeMaxSeconds * 1e6f,
3602 result.LostAllocationCount,
3603 result.LostAllocationTotalSize,
3604 result.FailedAllocationCount,
3605 result.FailedAllocationTotalSize);
3606}
3607
3608static void PerformCustomMainTest(FILE* file)
3609{
3610 Config config{};
3611 config.RandSeed = 65735476;
3612 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
3613 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3614 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3615 config.FreeOrder = FREE_ORDER::FORWARD;
3616 config.ThreadCount = 16;
3617 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02003618 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003619
3620 // Buffers
3621 //config.AllocationSizes.push_back({4, 16, 1024});
3622 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3623
3624 // Images
3625 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3626 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3627
3628 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3629 config.AdditionalOperationCount = 1024;
3630
3631 Result result{};
3632 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003633 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003634 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
3635}
3636
3637static void PerformCustomPoolTest(FILE* file)
3638{
3639 PoolTestConfig config;
3640 config.PoolSize = 100 * 1024 * 1024;
3641 config.RandSeed = 2345764;
3642 config.ThreadCount = 1;
3643 config.FrameCount = 200;
3644 config.ItemsToMakeUnusedPercent = 2;
3645
3646 AllocationSize allocSize = {};
3647 allocSize.BufferSizeMin = 1024;
3648 allocSize.BufferSizeMax = 1024 * 1024;
3649 allocSize.Probability = 1;
3650 config.AllocationSizes.push_back(allocSize);
3651
3652 allocSize.BufferSizeMin = 0;
3653 allocSize.BufferSizeMax = 0;
3654 allocSize.ImageSizeMin = 128;
3655 allocSize.ImageSizeMax = 1024;
3656 allocSize.Probability = 1;
3657 config.AllocationSizes.push_back(allocSize);
3658
3659 config.PoolSize = config.CalcAvgResourceSize() * 200;
3660 config.UsedItemCountMax = 160;
3661 config.TotalItemCount = config.UsedItemCountMax * 10;
3662 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3663
3664 g_MemoryAliasingWarningEnabled = false;
3665 PoolTestResult result = {};
3666 TestPool_Benchmark(result, config);
3667 g_MemoryAliasingWarningEnabled = true;
3668
3669 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
3670}
3671
Adam Sawickib8333fb2018-03-13 16:15:53 +01003672static void PerformMainTests(FILE* file)
3673{
3674 uint32_t repeatCount = 1;
3675 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3676
3677 Config config{};
3678 config.RandSeed = 65735476;
3679 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3680 config.FreeOrder = FREE_ORDER::FORWARD;
3681
3682 size_t threadCountCount = 1;
3683 switch(ConfigType)
3684 {
3685 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3686 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3687 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
3688 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
3689 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
3690 default: assert(0);
3691 }
Adam Sawicki0667e332018-08-24 17:26:44 +02003692
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003693 const size_t strategyCount = GetAllocationStrategyCount();
Adam Sawicki0667e332018-08-24 17:26:44 +02003694
Adam Sawickib8333fb2018-03-13 16:15:53 +01003695 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3696 {
3697 std::string desc1;
3698
3699 switch(threadCountIndex)
3700 {
3701 case 0:
3702 desc1 += "1_thread";
3703 config.ThreadCount = 1;
3704 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3705 break;
3706 case 1:
3707 desc1 += "16_threads+0%_common";
3708 config.ThreadCount = 16;
3709 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3710 break;
3711 case 2:
3712 desc1 += "16_threads+50%_common";
3713 config.ThreadCount = 16;
3714 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3715 break;
3716 case 3:
3717 desc1 += "16_threads+100%_common";
3718 config.ThreadCount = 16;
3719 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3720 break;
3721 case 4:
3722 desc1 += "2_threads+0%_common";
3723 config.ThreadCount = 2;
3724 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3725 break;
3726 case 5:
3727 desc1 += "2_threads+50%_common";
3728 config.ThreadCount = 2;
3729 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3730 break;
3731 case 6:
3732 desc1 += "2_threads+100%_common";
3733 config.ThreadCount = 2;
3734 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3735 break;
3736 default:
3737 assert(0);
3738 }
3739
3740 // 0 = buffers, 1 = images, 2 = buffers and images
3741 size_t buffersVsImagesCount = 2;
3742 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3743 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3744 {
3745 std::string desc2 = desc1;
3746 switch(buffersVsImagesIndex)
3747 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003748 case 0: desc2 += ",Buffers"; break;
3749 case 1: desc2 += ",Images"; break;
3750 case 2: desc2 += ",Buffers+Images"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003751 default: assert(0);
3752 }
3753
3754 // 0 = small, 1 = large, 2 = small and large
3755 size_t smallVsLargeCount = 2;
3756 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3757 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3758 {
3759 std::string desc3 = desc2;
3760 switch(smallVsLargeIndex)
3761 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003762 case 0: desc3 += ",Small"; break;
3763 case 1: desc3 += ",Large"; break;
3764 case 2: desc3 += ",Small+Large"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003765 default: assert(0);
3766 }
3767
3768 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3769 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3770 else
3771 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
3772
3773 // 0 = varying sizes min...max, 1 = set of constant sizes
3774 size_t constantSizesCount = 1;
3775 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3776 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3777 {
3778 std::string desc4 = desc3;
3779 switch(constantSizesIndex)
3780 {
3781 case 0: desc4 += " Varying_sizes"; break;
3782 case 1: desc4 += " Constant_sizes"; break;
3783 default: assert(0);
3784 }
3785
3786 config.AllocationSizes.clear();
3787 // Buffers present
3788 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3789 {
3790 // Small
3791 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3792 {
3793 // Varying size
3794 if(constantSizesIndex == 0)
3795 config.AllocationSizes.push_back({4, 16, 1024});
3796 // Constant sizes
3797 else
3798 {
3799 config.AllocationSizes.push_back({1, 16, 16});
3800 config.AllocationSizes.push_back({1, 64, 64});
3801 config.AllocationSizes.push_back({1, 256, 256});
3802 config.AllocationSizes.push_back({1, 1024, 1024});
3803 }
3804 }
3805 // Large
3806 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3807 {
3808 // Varying size
3809 if(constantSizesIndex == 0)
3810 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3811 // Constant sizes
3812 else
3813 {
3814 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3815 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3816 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3817 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3818 }
3819 }
3820 }
3821 // Images present
3822 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3823 {
3824 // Small
3825 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3826 {
3827 // Varying size
3828 if(constantSizesIndex == 0)
3829 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3830 // Constant sizes
3831 else
3832 {
3833 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3834 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3835 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3836 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3837 }
3838 }
3839 // Large
3840 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3841 {
3842 // Varying size
3843 if(constantSizesIndex == 0)
3844 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3845 // Constant sizes
3846 else
3847 {
3848 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3849 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3850 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3851 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3852 }
3853 }
3854 }
3855
3856 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
3857 size_t beginBytesToAllocateCount = 1;
3858 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
3859 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
3860 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
3861 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
3862 {
3863 std::string desc5 = desc4;
3864
3865 switch(beginBytesToAllocateIndex)
3866 {
3867 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003868 desc5 += ",Allocate_100%";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003869 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
3870 config.AdditionalOperationCount = 0;
3871 break;
3872 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003873 desc5 += ",Allocate_50%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003874 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
3875 config.AdditionalOperationCount = 1024;
3876 break;
3877 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003878 desc5 += ",Allocate_5%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003879 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3880 config.AdditionalOperationCount = 1024;
3881 break;
3882 case 3:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003883 desc5 += ",Allocate_95%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003884 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
3885 config.AdditionalOperationCount = 1024;
3886 break;
3887 default:
3888 assert(0);
3889 }
3890
Adam Sawicki0667e332018-08-24 17:26:44 +02003891 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01003892 {
Adam Sawicki0667e332018-08-24 17:26:44 +02003893 std::string desc6 = desc5;
3894 switch(strategyIndex)
3895 {
3896 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003897 desc6 += ",BestFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003898 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
3899 break;
3900 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003901 desc6 += ",WorstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003902 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
3903 break;
3904 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003905 desc6 += ",FirstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003906 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
3907 break;
3908 default:
3909 assert(0);
3910 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01003911
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003912 desc6 += ',';
3913 desc6 += FREE_ORDER_NAMES[(uint32_t)config.FreeOrder];
Adam Sawicki740b08f2018-08-27 13:42:07 +02003914
3915 const char* testDescription = desc6.c_str();
Adam Sawicki0667e332018-08-24 17:26:44 +02003916
3917 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3918 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003919 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawicki0667e332018-08-24 17:26:44 +02003920
3921 Result result{};
3922 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003923 TEST(res == VK_SUCCESS);
Adam Sawicki740b08f2018-08-27 13:42:07 +02003924 if(file)
3925 {
3926 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3927 }
Adam Sawicki0667e332018-08-24 17:26:44 +02003928 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01003929 }
3930 }
3931 }
3932 }
3933 }
3934 }
3935}
3936
3937static void PerformPoolTests(FILE* file)
3938{
3939 const size_t AVG_RESOURCES_PER_POOL = 300;
3940
3941 uint32_t repeatCount = 1;
3942 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3943
3944 PoolTestConfig config{};
3945 config.RandSeed = 2346343;
3946 config.FrameCount = 200;
3947 config.ItemsToMakeUnusedPercent = 2;
3948
3949 size_t threadCountCount = 1;
3950 switch(ConfigType)
3951 {
3952 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3953 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3954 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
3955 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
3956 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
3957 default: assert(0);
3958 }
3959 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3960 {
3961 std::string desc1;
3962
3963 switch(threadCountIndex)
3964 {
3965 case 0:
3966 desc1 += "1_thread";
3967 config.ThreadCount = 1;
3968 break;
3969 case 1:
3970 desc1 += "16_threads";
3971 config.ThreadCount = 16;
3972 break;
3973 case 2:
3974 desc1 += "2_threads";
3975 config.ThreadCount = 2;
3976 break;
3977 default:
3978 assert(0);
3979 }
3980
3981 // 0 = buffers, 1 = images, 2 = buffers and images
3982 size_t buffersVsImagesCount = 2;
3983 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3984 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3985 {
3986 std::string desc2 = desc1;
3987 switch(buffersVsImagesIndex)
3988 {
3989 case 0: desc2 += " Buffers"; break;
3990 case 1: desc2 += " Images"; break;
3991 case 2: desc2 += " Buffers+Images"; break;
3992 default: assert(0);
3993 }
3994
3995 // 0 = small, 1 = large, 2 = small and large
3996 size_t smallVsLargeCount = 2;
3997 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3998 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3999 {
4000 std::string desc3 = desc2;
4001 switch(smallVsLargeIndex)
4002 {
4003 case 0: desc3 += " Small"; break;
4004 case 1: desc3 += " Large"; break;
4005 case 2: desc3 += " Small+Large"; break;
4006 default: assert(0);
4007 }
4008
4009 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
4010 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
4011 else
4012 config.PoolSize = 4ull * 1024 * 1024;
4013
4014 // 0 = varying sizes min...max, 1 = set of constant sizes
4015 size_t constantSizesCount = 1;
4016 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
4017 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
4018 {
4019 std::string desc4 = desc3;
4020 switch(constantSizesIndex)
4021 {
4022 case 0: desc4 += " Varying_sizes"; break;
4023 case 1: desc4 += " Constant_sizes"; break;
4024 default: assert(0);
4025 }
4026
4027 config.AllocationSizes.clear();
4028 // Buffers present
4029 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
4030 {
4031 // Small
4032 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
4033 {
4034 // Varying size
4035 if(constantSizesIndex == 0)
4036 config.AllocationSizes.push_back({4, 16, 1024});
4037 // Constant sizes
4038 else
4039 {
4040 config.AllocationSizes.push_back({1, 16, 16});
4041 config.AllocationSizes.push_back({1, 64, 64});
4042 config.AllocationSizes.push_back({1, 256, 256});
4043 config.AllocationSizes.push_back({1, 1024, 1024});
4044 }
4045 }
4046 // Large
4047 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
4048 {
4049 // Varying size
4050 if(constantSizesIndex == 0)
4051 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
4052 // Constant sizes
4053 else
4054 {
4055 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
4056 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
4057 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
4058 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
4059 }
4060 }
4061 }
4062 // Images present
4063 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
4064 {
4065 // Small
4066 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
4067 {
4068 // Varying size
4069 if(constantSizesIndex == 0)
4070 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
4071 // Constant sizes
4072 else
4073 {
4074 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
4075 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
4076 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
4077 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
4078 }
4079 }
4080 // Large
4081 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
4082 {
4083 // Varying size
4084 if(constantSizesIndex == 0)
4085 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
4086 // Constant sizes
4087 else
4088 {
4089 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
4090 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
4091 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
4092 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
4093 }
4094 }
4095 }
4096
4097 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
4098 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
4099
4100 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
4101 size_t subscriptionModeCount;
4102 switch(ConfigType)
4103 {
4104 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
4105 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
4106 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
4107 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
4108 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
4109 default: assert(0);
4110 }
4111 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
4112 {
4113 std::string desc5 = desc4;
4114
4115 switch(subscriptionModeIndex)
4116 {
4117 case 0:
4118 desc5 += " Subscription_66%";
4119 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
4120 break;
4121 case 1:
4122 desc5 += " Subscription_133%";
4123 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
4124 break;
4125 case 2:
4126 desc5 += " Subscription_100%";
4127 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
4128 break;
4129 case 3:
4130 desc5 += " Subscription_33%";
4131 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
4132 break;
4133 case 4:
4134 desc5 += " Subscription_166%";
4135 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
4136 break;
4137 default:
4138 assert(0);
4139 }
4140
4141 config.TotalItemCount = config.UsedItemCountMax * 5;
4142 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
4143
4144 const char* testDescription = desc5.c_str();
4145
4146 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
4147 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02004148 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004149
4150 PoolTestResult result{};
4151 g_MemoryAliasingWarningEnabled = false;
4152 TestPool_Benchmark(result, config);
4153 g_MemoryAliasingWarningEnabled = true;
4154 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
4155 }
4156 }
4157 }
4158 }
4159 }
4160 }
4161}
4162
Adam Sawickia83793a2018-09-03 13:40:42 +02004163static void BasicTestBuddyAllocator()
4164{
4165 wprintf(L"Basic test buddy allocator\n");
4166
4167 RandomNumberGenerator rand{76543};
4168
4169 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4170 sampleBufCreateInfo.size = 1024; // Whatever.
4171 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4172
4173 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
4174 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4175
4176 VmaPoolCreateInfo poolCreateInfo = {};
4177 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004178 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004179
Adam Sawickid6e6d6b2018-09-21 14:07:02 +02004180 // Deliberately adding 1023 to test usable size smaller than memory block size.
4181 poolCreateInfo.blockSize = 1024 * 1024 + 1023;
Adam Sawickia83793a2018-09-03 13:40:42 +02004182 poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
Adam Sawicki80927152018-09-07 17:27:23 +02004183 //poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
Adam Sawickia83793a2018-09-03 13:40:42 +02004184
4185 VmaPool pool = nullptr;
4186 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004187 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004188
4189 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
4190
4191 VmaAllocationCreateInfo allocCreateInfo = {};
4192 allocCreateInfo.pool = pool;
4193
4194 std::vector<BufferInfo> bufInfo;
4195 BufferInfo newBufInfo;
4196 VmaAllocationInfo allocInfo;
4197
4198 bufCreateInfo.size = 1024 * 256;
4199 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4200 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004201 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004202 bufInfo.push_back(newBufInfo);
4203
4204 bufCreateInfo.size = 1024 * 512;
4205 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4206 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004207 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004208 bufInfo.push_back(newBufInfo);
4209
4210 bufCreateInfo.size = 1024 * 128;
4211 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4212 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004213 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02004214 bufInfo.push_back(newBufInfo);
Adam Sawickia01d4582018-09-21 14:22:35 +02004215
4216 // Test very small allocation, smaller than minimum node size.
4217 bufCreateInfo.size = 1;
4218 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4219 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004220 TEST(res == VK_SUCCESS);
Adam Sawickia01d4582018-09-21 14:22:35 +02004221 bufInfo.push_back(newBufInfo);
Adam Sawickia83793a2018-09-03 13:40:42 +02004222
Adam Sawicki9933c5c2018-09-21 14:57:24 +02004223 // Test some small allocation with alignment requirement.
4224 {
4225 VkMemoryRequirements memReq;
4226 memReq.alignment = 256;
4227 memReq.memoryTypeBits = UINT32_MAX;
4228 memReq.size = 32;
4229
4230 newBufInfo.Buffer = VK_NULL_HANDLE;
4231 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo,
4232 &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004233 TEST(res == VK_SUCCESS);
4234 TEST(allocInfo.offset % memReq.alignment == 0);
Adam Sawicki9933c5c2018-09-21 14:57:24 +02004235 bufInfo.push_back(newBufInfo);
4236 }
4237
4238 //SaveAllocatorStatsToFile(L"TEST.json");
4239
Adam Sawicki21017c62018-09-07 15:26:59 +02004240 VmaPoolStats stats = {};
4241 vmaGetPoolStats(g_hAllocator, pool, &stats);
4242 int DBG = 0; // Set breakpoint here to inspect `stats`.
4243
Adam Sawicki80927152018-09-07 17:27:23 +02004244 // Allocate enough new buffers to surely fall into second block.
4245 for(uint32_t i = 0; i < 32; ++i)
4246 {
4247 bufCreateInfo.size = 1024 * (rand.Generate() % 32 + 1);
4248 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4249 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004250 TEST(res == VK_SUCCESS);
Adam Sawicki80927152018-09-07 17:27:23 +02004251 bufInfo.push_back(newBufInfo);
4252 }
4253
4254 SaveAllocatorStatsToFile(L"BuddyTest01.json");
4255
Adam Sawickia83793a2018-09-03 13:40:42 +02004256 // Destroy the buffers in random order.
4257 while(!bufInfo.empty())
4258 {
4259 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
4260 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
4261 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
4262 bufInfo.erase(bufInfo.begin() + indexToDestroy);
4263 }
4264
4265 vmaDestroyPool(g_hAllocator, pool);
4266}
4267
Adam Sawickib8333fb2018-03-13 16:15:53 +01004268void Test()
4269{
4270 wprintf(L"TESTING:\n");
4271
Adam Sawickic6432d12018-09-21 16:44:16 +02004272 if(false)
Adam Sawicki70a683e2018-08-24 15:36:32 +02004273 {
4274 // # Temporarily insert custom tests here
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02004275 // ########################################
4276 // ########################################
Adam Sawicki80927152018-09-07 17:27:23 +02004277
Adam Sawickia83793a2018-09-03 13:40:42 +02004278 BasicTestBuddyAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02004279 return;
4280 }
4281
Adam Sawickib8333fb2018-03-13 16:15:53 +01004282 // # Simple tests
4283
4284 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02004285#if VMA_DEBUG_MARGIN
4286 TestDebugMargin();
4287#else
4288 TestPool_SameSize();
4289 TestHeapSizeLimit();
4290#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02004291#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
4292 TestAllocationsInitialization();
4293#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01004294 TestMapping();
4295 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02004296 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02004297 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02004298 TestLinearAllocatorMultiBlock();
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004299
Adam Sawicki4338f662018-09-07 14:12:37 +02004300 BasicTestBuddyAllocator();
4301
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004302 {
4303 FILE* file;
Adam Sawickic6432d12018-09-21 16:44:16 +02004304 fopen_s(&file, "Algorithms.csv", "w");
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004305 assert(file != NULL);
Adam Sawicki80927152018-09-07 17:27:23 +02004306 BenchmarkAlgorithms(file);
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004307 fclose(file);
4308 }
4309
Adam Sawickib8333fb2018-03-13 16:15:53 +01004310 TestDefragmentationSimple();
4311 TestDefragmentationFull();
4312
4313 // # Detailed tests
4314 FILE* file;
4315 fopen_s(&file, "Results.csv", "w");
4316 assert(file != NULL);
4317
4318 WriteMainTestResultHeader(file);
4319 PerformMainTests(file);
4320 //PerformCustomMainTest(file);
4321
4322 WritePoolTestResultHeader(file);
4323 PerformPoolTests(file);
4324 //PerformCustomPoolTest(file);
4325
4326 fclose(file);
4327
4328 wprintf(L"Done.\n");
4329}
4330
Adam Sawickif1a793c2018-03-13 15:42:22 +01004331#endif // #ifdef _WIN32