blob: 0940573450aa397a07eaa9992035b1173929983c [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 Sawicki51fa9662018-10-03 13:44:29 +0200142uint32_t g_FrameIndex = 0;
Adam Sawicki8cfe05f2018-08-22 16:48:17 +0200143
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 {
404 assert(0);
405 }
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 Sawicki51fa9662018-10-03 13:44:29 +0200638void 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;
687 assert((data != nullptr) == persistentlyMapped);
688 if(!persistentlyMapped)
689 {
690 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
691 }
692
693 uint32_t value = outAllocInfo.m_StartValue;
694 assert(bufCreateInfo.size % 4 == 0);
695 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;
729 assert(res == VK_SUCCESS);
730 }
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;
755 assert(res == VK_SUCCESS);
756 }
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);
762 assert(res == VK_SUCCESS);
763 }
764
765 uint32_t value = outAllocation.m_StartValue;
766 assert(allocInfo.size % 4 == 0);
767 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);
798 assert(res == VK_SUCCESS);
799 }
800
801 uint32_t value = allocation.m_StartValue;
802 bool ok = true;
803 size_t i;
804 assert(allocInfo.size % 4 == 0);
805 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
806 {
807 if(data[i] != value++)
808 {
809 ok = false;
810 break;
811 }
812 }
813 assert(ok);
814
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);
829 assert(res == VK_SUCCESS);
830
831 // Just to silence validation layer warnings.
832 VkMemoryRequirements vkMemReq;
833 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
834 assert(vkMemReq.size == allocation.m_BufferInfo.size);
835
836 res = vkBindBufferMemory(g_hDevice, allocation.m_Buffer, allocInfo.deviceMemory, allocInfo.offset);
837 assert(res == VK_SUCCESS);
838 }
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);
844 assert(res == VK_SUCCESS);
845
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);
851 assert(res == VK_SUCCESS);
852 }
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);
945 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
946 assert(defragStats.deviceMemoryBlocksFreed >= 1);
947
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);
979 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
980 }
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);
1092 assert(res >= 0);
1093
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);
1145 assert(res == VK_SUCCESS);
1146 assert(allocInfo.pUserData = numberAsPointer);
1147
1148 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1149 assert(allocInfo.pUserData == numberAsPointer);
1150
1151 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
1152 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1153 assert(allocInfo.pUserData == pointerToSomething);
1154
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);
1176 assert(res == VK_SUCCESS);
1177 assert(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
1178 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1179
1180 delete[] name1Buf;
1181
1182 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1183 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1184
1185 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
1186 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1187 assert(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
1188
1189 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
1190 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1191 assert(allocInfo.pUserData == nullptr);
1192
1193 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1194 }
1195 }
1196}
1197
1198static void TestMemoryRequirements()
1199{
1200 VkResult res;
1201 VkBuffer buf;
1202 VmaAllocation alloc;
1203 VmaAllocationInfo allocInfo;
1204
1205 const VkPhysicalDeviceMemoryProperties* memProps;
1206 vmaGetMemoryProperties(g_hAllocator, &memProps);
1207
1208 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1209 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1210 bufInfo.size = 128;
1211
1212 VmaAllocationCreateInfo allocCreateInfo = {};
1213
1214 // No requirements.
1215 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1216 assert(res == VK_SUCCESS);
1217 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1218
1219 // Usage.
1220 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1221 allocCreateInfo.requiredFlags = 0;
1222 allocCreateInfo.preferredFlags = 0;
1223 allocCreateInfo.memoryTypeBits = UINT32_MAX;
1224
1225 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1226 assert(res == VK_SUCCESS);
1227 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1228 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1229
1230 // Required flags, preferred flags.
1231 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
1232 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1233 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1234 allocCreateInfo.memoryTypeBits = 0;
1235
1236 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1237 assert(res == VK_SUCCESS);
1238 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1239 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
1240 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1241
1242 // memoryTypeBits.
1243 const uint32_t memType = allocInfo.memoryType;
1244 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1245 allocCreateInfo.requiredFlags = 0;
1246 allocCreateInfo.preferredFlags = 0;
1247 allocCreateInfo.memoryTypeBits = 1u << memType;
1248
1249 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1250 assert(res == VK_SUCCESS);
1251 assert(allocInfo.memoryType == memType);
1252 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1253
1254}
1255
1256static void TestBasics()
1257{
1258 VkResult res;
1259
1260 TestMemoryRequirements();
1261
1262 // Lost allocation
1263 {
1264 VmaAllocation alloc = VK_NULL_HANDLE;
1265 vmaCreateLostAllocation(g_hAllocator, &alloc);
1266 assert(alloc != VK_NULL_HANDLE);
1267
1268 VmaAllocationInfo allocInfo;
1269 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1270 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1271 assert(allocInfo.size == 0);
1272
1273 vmaFreeMemory(g_hAllocator, alloc);
1274 }
1275
1276 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
1277 {
1278 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1279 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1280 bufCreateInfo.size = 128;
1281
1282 VmaAllocationCreateInfo allocCreateInfo = {};
1283 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1284 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1285
1286 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1287 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1288 assert(res == VK_SUCCESS);
1289
1290 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1291
1292 // Same with OWN_MEMORY.
1293 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1294
1295 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1296 assert(res == VK_SUCCESS);
1297
1298 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1299 }
1300
1301 TestUserData();
1302}
1303
1304void TestHeapSizeLimit()
1305{
1306 const VkDeviceSize HEAP_SIZE_LIMIT = 1ull * 1024 * 1024 * 1024; // 1 GB
1307 const VkDeviceSize BLOCK_SIZE = 128ull * 1024 * 1024; // 128 MB
1308
1309 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
1310 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
1311 {
1312 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
1313 }
1314
1315 VmaAllocatorCreateInfo allocatorCreateInfo = {};
1316 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
1317 allocatorCreateInfo.device = g_hDevice;
1318 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
1319
1320 VmaAllocator hAllocator;
1321 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
1322 assert(res == VK_SUCCESS);
1323
1324 struct Item
1325 {
1326 VkBuffer hBuf;
1327 VmaAllocation hAlloc;
1328 };
1329 std::vector<Item> items;
1330
1331 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1332 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1333
1334 // 1. Allocate two blocks of Own Memory, half the size of BLOCK_SIZE.
1335 VmaAllocationInfo ownAllocInfo;
1336 {
1337 VmaAllocationCreateInfo allocCreateInfo = {};
1338 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1339 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1340
1341 bufCreateInfo.size = BLOCK_SIZE / 2;
1342
1343 for(size_t i = 0; i < 2; ++i)
1344 {
1345 Item item;
1346 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &ownAllocInfo);
1347 assert(res == VK_SUCCESS);
1348 items.push_back(item);
1349 }
1350 }
1351
1352 // Create pool to make sure allocations must be out of this memory type.
1353 VmaPoolCreateInfo poolCreateInfo = {};
1354 poolCreateInfo.memoryTypeIndex = ownAllocInfo.memoryType;
1355 poolCreateInfo.blockSize = BLOCK_SIZE;
1356
1357 VmaPool hPool;
1358 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
1359 assert(res == VK_SUCCESS);
1360
1361 // 2. Allocate normal buffers from all the remaining memory.
1362 {
1363 VmaAllocationCreateInfo allocCreateInfo = {};
1364 allocCreateInfo.pool = hPool;
1365
1366 bufCreateInfo.size = BLOCK_SIZE / 2;
1367
1368 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
1369 for(size_t i = 0; i < bufCount; ++i)
1370 {
1371 Item item;
1372 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
1373 assert(res == VK_SUCCESS);
1374 items.push_back(item);
1375 }
1376 }
1377
1378 // 3. Allocation of one more (even small) buffer should fail.
1379 {
1380 VmaAllocationCreateInfo allocCreateInfo = {};
1381 allocCreateInfo.pool = hPool;
1382
1383 bufCreateInfo.size = 128;
1384
1385 VkBuffer hBuf;
1386 VmaAllocation hAlloc;
1387 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
1388 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1389 }
1390
1391 // Destroy everything.
1392 for(size_t i = items.size(); i--; )
1393 {
1394 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
1395 }
1396
1397 vmaDestroyPool(hAllocator, hPool);
1398
1399 vmaDestroyAllocator(hAllocator);
1400}
1401
Adam Sawicki212a4a62018-06-14 15:44:45 +02001402#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02001403static void TestDebugMargin()
1404{
1405 if(VMA_DEBUG_MARGIN == 0)
1406 {
1407 return;
1408 }
1409
1410 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02001411 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02001412
1413 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02001414 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02001415
1416 // Create few buffers of different size.
1417 const size_t BUF_COUNT = 10;
1418 BufferInfo buffers[BUF_COUNT];
1419 VmaAllocationInfo allocInfo[BUF_COUNT];
1420 for(size_t i = 0; i < 10; ++i)
1421 {
1422 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02001423 // Last one will be mapped.
1424 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02001425
1426 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
1427 assert(res == VK_SUCCESS);
1428 // Margin is preserved also at the beginning of a block.
1429 assert(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02001430
1431 if(i == BUF_COUNT - 1)
1432 {
1433 // Fill with data.
1434 assert(allocInfo[i].pMappedData != nullptr);
1435 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
1436 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
1437 }
Adam Sawicki73b16652018-06-11 16:39:25 +02001438 }
1439
1440 // Check if their offsets preserve margin between them.
1441 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
1442 {
1443 if(lhs.deviceMemory != rhs.deviceMemory)
1444 {
1445 return lhs.deviceMemory < rhs.deviceMemory;
1446 }
1447 return lhs.offset < rhs.offset;
1448 });
1449 for(size_t i = 1; i < BUF_COUNT; ++i)
1450 {
1451 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
1452 {
1453 assert(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
1454 }
1455 }
1456
Adam Sawicki212a4a62018-06-14 15:44:45 +02001457 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
1458 assert(res == VK_SUCCESS);
1459
Adam Sawicki73b16652018-06-11 16:39:25 +02001460 // Destroy all buffers.
1461 for(size_t i = BUF_COUNT; i--; )
1462 {
1463 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
1464 }
1465}
Adam Sawicki212a4a62018-06-14 15:44:45 +02001466#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02001467
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001468static void TestLinearAllocator()
1469{
1470 wprintf(L"Test linear allocator\n");
1471
1472 RandomNumberGenerator rand{645332};
1473
1474 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1475 sampleBufCreateInfo.size = 1024; // Whatever.
1476 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1477
1478 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1479 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1480
1481 VmaPoolCreateInfo poolCreateInfo = {};
1482 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1483 assert(res == VK_SUCCESS);
1484
Adam Sawickiee082772018-06-20 17:45:49 +02001485 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001486 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1487 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
1488
1489 VmaPool pool = nullptr;
1490 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1491 assert(res == VK_SUCCESS);
1492
1493 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1494
1495 VmaAllocationCreateInfo allocCreateInfo = {};
1496 allocCreateInfo.pool = pool;
1497
1498 constexpr size_t maxBufCount = 100;
1499 std::vector<BufferInfo> bufInfo;
1500
1501 constexpr VkDeviceSize bufSizeMin = 16;
1502 constexpr VkDeviceSize bufSizeMax = 1024;
1503 VmaAllocationInfo allocInfo;
1504 VkDeviceSize prevOffset = 0;
1505
1506 // Test one-time free.
1507 for(size_t i = 0; i < 2; ++i)
1508 {
1509 // Allocate number of buffers of varying size that surely fit into this block.
1510 VkDeviceSize bufSumSize = 0;
1511 for(size_t i = 0; i < maxBufCount; ++i)
1512 {
1513 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1514 BufferInfo newBufInfo;
1515 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1516 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1517 assert(res == VK_SUCCESS);
1518 assert(i == 0 || allocInfo.offset > prevOffset);
1519 bufInfo.push_back(newBufInfo);
1520 prevOffset = allocInfo.offset;
1521 bufSumSize += bufCreateInfo.size;
1522 }
1523
1524 // Validate pool stats.
1525 VmaPoolStats stats;
1526 vmaGetPoolStats(g_hAllocator, pool, &stats);
1527 assert(stats.size == poolCreateInfo.blockSize);
1528 assert(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
1529 assert(stats.allocationCount == bufInfo.size());
1530
1531 // Destroy the buffers in random order.
1532 while(!bufInfo.empty())
1533 {
1534 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1535 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1536 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1537 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1538 }
1539 }
1540
1541 // Test stack.
1542 {
1543 // Allocate number of buffers of varying size that surely fit into this block.
1544 for(size_t i = 0; i < maxBufCount; ++i)
1545 {
1546 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1547 BufferInfo newBufInfo;
1548 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1549 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1550 assert(res == VK_SUCCESS);
1551 assert(i == 0 || allocInfo.offset > prevOffset);
1552 bufInfo.push_back(newBufInfo);
1553 prevOffset = allocInfo.offset;
1554 }
1555
1556 // Destroy few buffers from top of the stack.
1557 for(size_t i = 0; i < maxBufCount / 5; ++i)
1558 {
1559 const BufferInfo& currBufInfo = bufInfo.back();
1560 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1561 bufInfo.pop_back();
1562 }
1563
1564 // Create some more
1565 for(size_t i = 0; i < maxBufCount / 5; ++i)
1566 {
1567 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1568 BufferInfo newBufInfo;
1569 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1570 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1571 assert(res == VK_SUCCESS);
1572 assert(i == 0 || allocInfo.offset > prevOffset);
1573 bufInfo.push_back(newBufInfo);
1574 prevOffset = allocInfo.offset;
1575 }
1576
1577 // Destroy the buffers in reverse order.
1578 while(!bufInfo.empty())
1579 {
1580 const BufferInfo& currBufInfo = bufInfo.back();
1581 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1582 bufInfo.pop_back();
1583 }
1584 }
1585
Adam Sawickiee082772018-06-20 17:45:49 +02001586 // Test ring buffer.
1587 {
1588 // Allocate number of buffers that surely fit into this block.
1589 bufCreateInfo.size = bufSizeMax;
1590 for(size_t i = 0; i < maxBufCount; ++i)
1591 {
1592 BufferInfo newBufInfo;
1593 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1594 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1595 assert(res == VK_SUCCESS);
1596 assert(i == 0 || allocInfo.offset > prevOffset);
1597 bufInfo.push_back(newBufInfo);
1598 prevOffset = allocInfo.offset;
1599 }
1600
1601 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
1602 const size_t buffersPerIter = maxBufCount / 10 - 1;
1603 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
1604 for(size_t iter = 0; iter < iterCount; ++iter)
1605 {
1606 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1607 {
1608 const BufferInfo& currBufInfo = bufInfo.front();
1609 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1610 bufInfo.erase(bufInfo.begin());
1611 }
1612 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1613 {
1614 BufferInfo newBufInfo;
1615 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1616 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1617 assert(res == VK_SUCCESS);
1618 bufInfo.push_back(newBufInfo);
1619 }
1620 }
1621
1622 // Allocate buffers until we reach out-of-memory.
1623 uint32_t debugIndex = 0;
1624 while(res == VK_SUCCESS)
1625 {
1626 BufferInfo newBufInfo;
1627 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1628 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1629 if(res == VK_SUCCESS)
1630 {
1631 bufInfo.push_back(newBufInfo);
1632 }
1633 else
1634 {
1635 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1636 }
1637 ++debugIndex;
1638 }
1639
1640 // Destroy the buffers in random order.
1641 while(!bufInfo.empty())
1642 {
1643 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1644 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1645 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1646 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1647 }
1648 }
1649
Adam Sawicki680b2252018-08-22 14:47:32 +02001650 // Test double stack.
1651 {
1652 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
1653 VkDeviceSize prevOffsetLower = 0;
1654 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
1655 for(size_t i = 0; i < maxBufCount; ++i)
1656 {
1657 const bool upperAddress = (i % 2) != 0;
1658 if(upperAddress)
1659 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1660 else
1661 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1662 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1663 BufferInfo newBufInfo;
1664 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1665 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1666 assert(res == VK_SUCCESS);
1667 if(upperAddress)
1668 {
1669 assert(allocInfo.offset < prevOffsetUpper);
1670 prevOffsetUpper = allocInfo.offset;
1671 }
1672 else
1673 {
1674 assert(allocInfo.offset >= prevOffsetLower);
1675 prevOffsetLower = allocInfo.offset;
1676 }
1677 assert(prevOffsetLower < prevOffsetUpper);
1678 bufInfo.push_back(newBufInfo);
1679 }
1680
1681 // Destroy few buffers from top of the stack.
1682 for(size_t i = 0; i < maxBufCount / 5; ++i)
1683 {
1684 const BufferInfo& currBufInfo = bufInfo.back();
1685 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1686 bufInfo.pop_back();
1687 }
1688
1689 // Create some more
1690 for(size_t i = 0; i < maxBufCount / 5; ++i)
1691 {
1692 const bool upperAddress = (i % 2) != 0;
1693 if(upperAddress)
1694 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1695 else
1696 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1697 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1698 BufferInfo newBufInfo;
1699 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1700 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1701 assert(res == VK_SUCCESS);
1702 bufInfo.push_back(newBufInfo);
1703 }
1704
1705 // Destroy the buffers in reverse order.
1706 while(!bufInfo.empty())
1707 {
1708 const BufferInfo& currBufInfo = bufInfo.back();
1709 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1710 bufInfo.pop_back();
1711 }
1712
1713 // Create buffers on both sides until we reach out of memory.
1714 prevOffsetLower = 0;
1715 prevOffsetUpper = poolCreateInfo.blockSize;
1716 res = VK_SUCCESS;
1717 for(size_t i = 0; res == VK_SUCCESS; ++i)
1718 {
1719 const bool upperAddress = (i % 2) != 0;
1720 if(upperAddress)
1721 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1722 else
1723 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1724 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1725 BufferInfo newBufInfo;
1726 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1727 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1728 if(res == VK_SUCCESS)
1729 {
1730 if(upperAddress)
1731 {
1732 assert(allocInfo.offset < prevOffsetUpper);
1733 prevOffsetUpper = allocInfo.offset;
1734 }
1735 else
1736 {
1737 assert(allocInfo.offset >= prevOffsetLower);
1738 prevOffsetLower = allocInfo.offset;
1739 }
1740 assert(prevOffsetLower < prevOffsetUpper);
1741 bufInfo.push_back(newBufInfo);
1742 }
1743 }
1744
1745 // Destroy the buffers in random order.
1746 while(!bufInfo.empty())
1747 {
1748 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1749 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1750 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1751 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1752 }
1753
1754 // Create buffers on upper side only, constant size, until we reach out of memory.
1755 prevOffsetUpper = poolCreateInfo.blockSize;
1756 res = VK_SUCCESS;
1757 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1758 bufCreateInfo.size = bufSizeMax;
1759 for(size_t i = 0; res == VK_SUCCESS; ++i)
1760 {
1761 BufferInfo newBufInfo;
1762 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1763 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1764 if(res == VK_SUCCESS)
1765 {
1766 assert(allocInfo.offset < prevOffsetUpper);
1767 prevOffsetUpper = allocInfo.offset;
1768 bufInfo.push_back(newBufInfo);
1769 }
1770 }
1771
1772 // Destroy the buffers in reverse order.
1773 while(!bufInfo.empty())
1774 {
1775 const BufferInfo& currBufInfo = bufInfo.back();
1776 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1777 bufInfo.pop_back();
1778 }
1779 }
1780
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001781 // Test ring buffer with lost allocations.
1782 {
1783 // Allocate number of buffers until pool is full.
1784 // Notice CAN_BECOME_LOST flag and call to vmaSetCurrentFrameIndex.
1785 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT;
1786 res = VK_SUCCESS;
1787 for(size_t i = 0; res == VK_SUCCESS; ++i)
1788 {
1789 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1790
1791 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1792
1793 BufferInfo newBufInfo;
1794 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1795 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1796 if(res == VK_SUCCESS)
1797 bufInfo.push_back(newBufInfo);
1798 }
1799
1800 // Free first half of it.
1801 {
1802 const size_t buffersToDelete = bufInfo.size() / 2;
1803 for(size_t i = 0; i < buffersToDelete; ++i)
1804 {
1805 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1806 }
1807 bufInfo.erase(bufInfo.begin(), bufInfo.begin() + buffersToDelete);
1808 }
1809
1810 // Allocate number of buffers until pool is full again.
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001811 // This way we make sure ring buffers wraps around, front in in the middle.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001812 res = VK_SUCCESS;
1813 for(size_t i = 0; res == VK_SUCCESS; ++i)
1814 {
1815 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1816
1817 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1818
1819 BufferInfo newBufInfo;
1820 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1821 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1822 if(res == VK_SUCCESS)
1823 bufInfo.push_back(newBufInfo);
1824 }
1825
1826 VkDeviceSize firstNewOffset;
1827 {
1828 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1829
1830 // Allocate a large buffer with CAN_MAKE_OTHER_LOST.
1831 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1832 bufCreateInfo.size = bufSizeMax;
1833
1834 BufferInfo newBufInfo;
1835 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1836 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1837 assert(res == VK_SUCCESS);
1838 bufInfo.push_back(newBufInfo);
1839 firstNewOffset = allocInfo.offset;
1840
1841 // Make sure at least one buffer from the beginning became lost.
1842 vmaGetAllocationInfo(g_hAllocator, bufInfo[0].Allocation, &allocInfo);
1843 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1844 }
1845
1846 // Allocate more buffers that CAN_MAKE_OTHER_LOST until we wrap-around with this.
1847 size_t newCount = 1;
1848 for(;;)
1849 {
1850 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1851
1852 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1853
1854 BufferInfo newBufInfo;
1855 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1856 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1857 assert(res == VK_SUCCESS);
1858 bufInfo.push_back(newBufInfo);
1859 ++newCount;
1860 if(allocInfo.offset < firstNewOffset)
1861 break;
1862 }
1863
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001864 // Delete buffers that are lost.
1865 for(size_t i = bufInfo.size(); i--; )
1866 {
1867 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
1868 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1869 {
1870 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1871 bufInfo.erase(bufInfo.begin() + i);
1872 }
1873 }
1874
1875 // Test vmaMakePoolAllocationsLost
1876 {
1877 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1878
1879 size_t lostAllocCount = SIZE_MAX;
1880 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostAllocCount);
1881 assert(lostAllocCount > 0);
1882
1883 size_t realLostAllocCount = 0;
1884 for(size_t i = 0; i < bufInfo.size(); ++i)
1885 {
1886 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
1887 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1888 ++realLostAllocCount;
1889 }
1890 assert(realLostAllocCount == lostAllocCount);
1891 }
1892
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001893 // Destroy all the buffers in forward order.
1894 for(size_t i = 0; i < bufInfo.size(); ++i)
1895 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1896 bufInfo.clear();
1897 }
1898
Adam Sawicki70a683e2018-08-24 15:36:32 +02001899 vmaDestroyPool(g_hAllocator, pool);
1900}
Adam Sawickif799c4f2018-08-23 10:40:30 +02001901
Adam Sawicki70a683e2018-08-24 15:36:32 +02001902static void TestLinearAllocatorMultiBlock()
1903{
1904 wprintf(L"Test linear allocator multi block\n");
1905
1906 RandomNumberGenerator rand{345673};
1907
1908 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1909 sampleBufCreateInfo.size = 1024 * 1024;
1910 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1911
1912 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1913 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1914
1915 VmaPoolCreateInfo poolCreateInfo = {};
1916 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1917 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1918 assert(res == VK_SUCCESS);
1919
1920 VmaPool pool = nullptr;
1921 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1922 assert(res == VK_SUCCESS);
1923
1924 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1925
1926 VmaAllocationCreateInfo allocCreateInfo = {};
1927 allocCreateInfo.pool = pool;
1928
1929 std::vector<BufferInfo> bufInfo;
1930 VmaAllocationInfo allocInfo;
1931
1932 // Test one-time free.
1933 {
1934 // Allocate buffers until we move to a second block.
1935 VkDeviceMemory lastMem = VK_NULL_HANDLE;
1936 for(uint32_t i = 0; ; ++i)
1937 {
1938 BufferInfo newBufInfo;
1939 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1940 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1941 assert(res == VK_SUCCESS);
1942 bufInfo.push_back(newBufInfo);
1943 if(lastMem && allocInfo.deviceMemory != lastMem)
1944 {
1945 break;
1946 }
1947 lastMem = allocInfo.deviceMemory;
1948 }
1949
1950 assert(bufInfo.size() > 2);
1951
1952 // Make sure that pool has now two blocks.
1953 VmaPoolStats poolStats = {};
1954 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1955 assert(poolStats.blockCount == 2);
1956
1957 // Destroy all the buffers in random order.
1958 while(!bufInfo.empty())
1959 {
1960 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1961 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1962 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1963 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1964 }
1965
1966 // Make sure that pool has now at most one block.
1967 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1968 assert(poolStats.blockCount <= 1);
1969 }
1970
1971 // Test stack.
1972 {
1973 // Allocate buffers until we move to a second block.
1974 VkDeviceMemory lastMem = VK_NULL_HANDLE;
1975 for(uint32_t i = 0; ; ++i)
1976 {
1977 BufferInfo newBufInfo;
1978 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1979 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1980 assert(res == VK_SUCCESS);
1981 bufInfo.push_back(newBufInfo);
1982 if(lastMem && allocInfo.deviceMemory != lastMem)
1983 {
1984 break;
1985 }
1986 lastMem = allocInfo.deviceMemory;
1987 }
1988
1989 assert(bufInfo.size() > 2);
1990
1991 // Add few more buffers.
1992 for(uint32_t i = 0; i < 5; ++i)
1993 {
1994 BufferInfo newBufInfo;
1995 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1996 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1997 assert(res == VK_SUCCESS);
1998 bufInfo.push_back(newBufInfo);
1999 }
2000
2001 // Make sure that pool has now two blocks.
2002 VmaPoolStats poolStats = {};
2003 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2004 assert(poolStats.blockCount == 2);
2005
2006 // Delete half of buffers, LIFO.
2007 for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
2008 {
2009 const BufferInfo& currBufInfo = bufInfo.back();
2010 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2011 bufInfo.pop_back();
2012 }
2013
2014 // Add one more buffer.
2015 BufferInfo newBufInfo;
2016 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2017 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2018 assert(res == VK_SUCCESS);
2019 bufInfo.push_back(newBufInfo);
2020
2021 // Make sure that pool has now one block.
2022 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2023 assert(poolStats.blockCount == 1);
2024
2025 // Delete all the remaining buffers, LIFO.
2026 while(!bufInfo.empty())
2027 {
2028 const BufferInfo& currBufInfo = bufInfo.back();
2029 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2030 bufInfo.pop_back();
2031 }
Adam Sawickif799c4f2018-08-23 10:40:30 +02002032 }
2033
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002034 vmaDestroyPool(g_hAllocator, pool);
2035}
2036
Adam Sawickifd11d752018-08-22 15:02:10 +02002037static void ManuallyTestLinearAllocator()
2038{
2039 VmaStats origStats;
2040 vmaCalculateStats(g_hAllocator, &origStats);
2041
2042 wprintf(L"Manually test linear allocator\n");
2043
2044 RandomNumberGenerator rand{645332};
2045
2046 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2047 sampleBufCreateInfo.size = 1024; // Whatever.
2048 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2049
2050 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2051 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2052
2053 VmaPoolCreateInfo poolCreateInfo = {};
2054 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2055 assert(res == VK_SUCCESS);
2056
2057 poolCreateInfo.blockSize = 10 * 1024;
2058 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2059 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2060
2061 VmaPool pool = nullptr;
2062 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2063 assert(res == VK_SUCCESS);
2064
2065 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
2066
2067 VmaAllocationCreateInfo allocCreateInfo = {};
2068 allocCreateInfo.pool = pool;
2069
2070 std::vector<BufferInfo> bufInfo;
2071 VmaAllocationInfo allocInfo;
2072 BufferInfo newBufInfo;
2073
2074 // Test double stack.
2075 {
2076 /*
2077 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
2078 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
2079
2080 Totally:
2081 1 block allocated
2082 10240 Vulkan bytes
2083 6 new allocations
2084 2256 bytes in allocations
2085 */
2086
2087 bufCreateInfo.size = 32;
2088 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2089 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2090 assert(res == VK_SUCCESS);
2091 bufInfo.push_back(newBufInfo);
2092
2093 bufCreateInfo.size = 1024;
2094 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2095 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2096 assert(res == VK_SUCCESS);
2097 bufInfo.push_back(newBufInfo);
2098
2099 bufCreateInfo.size = 32;
2100 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2101 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2102 assert(res == VK_SUCCESS);
2103 bufInfo.push_back(newBufInfo);
2104
2105 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
2106
2107 bufCreateInfo.size = 128;
2108 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2109 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2110 assert(res == VK_SUCCESS);
2111 bufInfo.push_back(newBufInfo);
2112
2113 bufCreateInfo.size = 1024;
2114 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2115 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2116 assert(res == VK_SUCCESS);
2117 bufInfo.push_back(newBufInfo);
2118
2119 bufCreateInfo.size = 16;
2120 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2121 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2122 assert(res == VK_SUCCESS);
2123 bufInfo.push_back(newBufInfo);
2124
2125 VmaStats currStats;
2126 vmaCalculateStats(g_hAllocator, &currStats);
2127 VmaPoolStats poolStats;
2128 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2129
2130 char* statsStr = nullptr;
2131 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
2132
2133 // PUT BREAKPOINT HERE TO CHECK.
2134 // Inspect: currStats versus origStats, poolStats, statsStr.
2135 int I = 0;
2136
2137 vmaFreeStatsString(g_hAllocator, statsStr);
2138
2139 // Destroy the buffers in reverse order.
2140 while(!bufInfo.empty())
2141 {
2142 const BufferInfo& currBufInfo = bufInfo.back();
2143 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2144 bufInfo.pop_back();
2145 }
2146 }
2147
2148 vmaDestroyPool(g_hAllocator, pool);
2149}
2150
Adam Sawicki80927152018-09-07 17:27:23 +02002151static void BenchmarkAlgorithmsCase(FILE* file,
2152 uint32_t algorithm,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002153 bool empty,
2154 VmaAllocationCreateFlags allocStrategy,
2155 FREE_ORDER freeOrder)
Adam Sawicki0a607132018-08-24 11:18:41 +02002156{
2157 RandomNumberGenerator rand{16223};
2158
2159 const VkDeviceSize bufSizeMin = 32;
2160 const VkDeviceSize bufSizeMax = 1024;
2161 const size_t maxBufCapacity = 10000;
2162 const uint32_t iterationCount = 10;
2163
2164 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2165 sampleBufCreateInfo.size = bufSizeMax;
2166 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2167
2168 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2169 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2170
2171 VmaPoolCreateInfo poolCreateInfo = {};
2172 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2173 assert(res == VK_SUCCESS);
2174
2175 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
Adam Sawicki80927152018-09-07 17:27:23 +02002176 poolCreateInfo.flags |= algorithm;
Adam Sawicki0a607132018-08-24 11:18:41 +02002177 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2178
2179 VmaPool pool = nullptr;
2180 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2181 assert(res == VK_SUCCESS);
2182
2183 // Buffer created just to get memory requirements. Never bound to any memory.
2184 VkBuffer dummyBuffer = VK_NULL_HANDLE;
2185 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, nullptr, &dummyBuffer);
2186 assert(res == VK_SUCCESS && dummyBuffer);
2187
2188 VkMemoryRequirements memReq = {};
2189 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2190
2191 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2192
2193 VmaAllocationCreateInfo allocCreateInfo = {};
2194 allocCreateInfo.pool = pool;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002195 allocCreateInfo.flags = allocStrategy;
Adam Sawicki0a607132018-08-24 11:18:41 +02002196
2197 VmaAllocation alloc;
2198 std::vector<VmaAllocation> baseAllocations;
2199
2200 if(!empty)
2201 {
2202 // Make allocations up to half of pool size.
2203 VkDeviceSize totalSize = 0;
2204 while(totalSize < poolCreateInfo.blockSize / 2)
2205 {
2206 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2207 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2208 assert(res == VK_SUCCESS);
2209 baseAllocations.push_back(alloc);
2210 totalSize += memReq.size;
2211 }
2212
2213 // Delete half of them, choose randomly.
2214 size_t allocsToDelete = baseAllocations.size() / 2;
2215 for(size_t i = 0; i < allocsToDelete; ++i)
2216 {
2217 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
2218 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
2219 baseAllocations.erase(baseAllocations.begin() + index);
2220 }
2221 }
2222
2223 // BENCHMARK
2224 const size_t allocCount = maxBufCapacity / 2;
2225 std::vector<VmaAllocation> testAllocations;
2226 testAllocations.reserve(allocCount);
2227 duration allocTotalDuration = duration::zero();
2228 duration freeTotalDuration = duration::zero();
2229 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
2230 {
2231 // Allocations
2232 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
2233 for(size_t i = 0; i < allocCount; ++i)
2234 {
2235 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2236 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2237 assert(res == VK_SUCCESS);
2238 testAllocations.push_back(alloc);
2239 }
2240 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
2241
2242 // Deallocations
2243 switch(freeOrder)
2244 {
2245 case FREE_ORDER::FORWARD:
2246 // Leave testAllocations unchanged.
2247 break;
2248 case FREE_ORDER::BACKWARD:
2249 std::reverse(testAllocations.begin(), testAllocations.end());
2250 break;
2251 case FREE_ORDER::RANDOM:
2252 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
2253 break;
2254 default: assert(0);
2255 }
2256
2257 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
2258 for(size_t i = 0; i < allocCount; ++i)
2259 vmaFreeMemory(g_hAllocator, testAllocations[i]);
2260 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
2261
2262 testAllocations.clear();
2263 }
2264
2265 // Delete baseAllocations
2266 while(!baseAllocations.empty())
2267 {
2268 vmaFreeMemory(g_hAllocator, baseAllocations.back());
2269 baseAllocations.pop_back();
2270 }
2271
2272 vmaDestroyPool(g_hAllocator, pool);
2273
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002274 const float allocTotalSeconds = ToFloatSeconds(allocTotalDuration);
2275 const float freeTotalSeconds = ToFloatSeconds(freeTotalDuration);
2276
Adam Sawicki80927152018-09-07 17:27:23 +02002277 printf(" Algorithm=%s %s Allocation=%s FreeOrder=%s: allocations %g s, free %g s\n",
2278 AlgorithmToStr(algorithm),
Adam Sawicki0667e332018-08-24 17:26:44 +02002279 empty ? "Empty" : "Not empty",
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002280 GetAllocationStrategyName(allocStrategy),
Adam Sawicki0a607132018-08-24 11:18:41 +02002281 FREE_ORDER_NAMES[(size_t)freeOrder],
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002282 allocTotalSeconds,
2283 freeTotalSeconds);
2284
2285 if(file)
2286 {
2287 std::string currTime;
2288 CurrentTimeToStr(currTime);
2289
Adam Sawicki80927152018-09-07 17:27:23 +02002290 fprintf(file, "%s,%s,%s,%u,%s,%s,%g,%g\n",
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002291 CODE_DESCRIPTION, currTime.c_str(),
Adam Sawicki80927152018-09-07 17:27:23 +02002292 AlgorithmToStr(algorithm),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002293 empty ? 1 : 0,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002294 GetAllocationStrategyName(allocStrategy),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002295 FREE_ORDER_NAMES[(uint32_t)freeOrder],
2296 allocTotalSeconds,
2297 freeTotalSeconds);
2298 }
Adam Sawicki0a607132018-08-24 11:18:41 +02002299}
2300
Adam Sawicki80927152018-09-07 17:27:23 +02002301static void BenchmarkAlgorithms(FILE* file)
Adam Sawicki0a607132018-08-24 11:18:41 +02002302{
Adam Sawicki80927152018-09-07 17:27:23 +02002303 wprintf(L"Benchmark algorithms\n");
Adam Sawicki0a607132018-08-24 11:18:41 +02002304
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002305 if(file)
2306 {
2307 fprintf(file,
2308 "Code,Time,"
Adam Sawicki80927152018-09-07 17:27:23 +02002309 "Algorithm,Empty,Allocation strategy,Free order,"
Adam Sawicki33d2ce72018-08-27 13:59:13 +02002310 "Allocation time (s),Deallocation time (s)\n");
2311 }
2312
Adam Sawicki0a607132018-08-24 11:18:41 +02002313 uint32_t freeOrderCount = 1;
2314 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
2315 freeOrderCount = 3;
2316 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
2317 freeOrderCount = 2;
2318
2319 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002320 const uint32_t allocStrategyCount = GetAllocationStrategyCount();
Adam Sawicki0a607132018-08-24 11:18:41 +02002321
2322 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
2323 {
2324 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
2325 switch(freeOrderIndex)
2326 {
2327 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
2328 case 1: freeOrder = FREE_ORDER::FORWARD; break;
2329 case 2: freeOrder = FREE_ORDER::RANDOM; break;
2330 default: assert(0);
2331 }
2332
2333 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
2334 {
Adam Sawicki80927152018-09-07 17:27:23 +02002335 for(uint32_t algorithmIndex = 0; algorithmIndex < 3; ++algorithmIndex)
Adam Sawicki0a607132018-08-24 11:18:41 +02002336 {
Adam Sawicki80927152018-09-07 17:27:23 +02002337 uint32_t algorithm = 0;
2338 switch(algorithmIndex)
2339 {
2340 case 0:
2341 break;
2342 case 1:
2343 algorithm = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
2344 break;
2345 case 2:
2346 algorithm = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2347 break;
2348 default:
2349 assert(0);
2350 }
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002351
Adam Sawicki80927152018-09-07 17:27:23 +02002352 uint32_t currAllocStrategyCount = algorithm != 0 ? 1 : allocStrategyCount;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002353 for(uint32_t allocStrategyIndex = 0; allocStrategyIndex < currAllocStrategyCount; ++allocStrategyIndex)
2354 {
2355 VmaAllocatorCreateFlags strategy = 0;
Adam Sawicki80927152018-09-07 17:27:23 +02002356 if(currAllocStrategyCount > 1)
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002357 {
2358 switch(allocStrategyIndex)
2359 {
2360 case 0: strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; break;
2361 case 1: strategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT; break;
2362 case 2: strategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT; break;
2363 default: assert(0);
2364 }
2365 }
2366
Adam Sawicki80927152018-09-07 17:27:23 +02002367 BenchmarkAlgorithmsCase(
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002368 file,
Adam Sawicki80927152018-09-07 17:27:23 +02002369 algorithm,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02002370 emptyIndex ? 0 : 1, // empty
2371 strategy,
2372 freeOrder); // freeOrder
2373 }
Adam Sawicki0a607132018-08-24 11:18:41 +02002374 }
2375 }
2376 }
2377}
2378
Adam Sawickib8333fb2018-03-13 16:15:53 +01002379static void TestPool_SameSize()
2380{
2381 const VkDeviceSize BUF_SIZE = 1024 * 1024;
2382 const size_t BUF_COUNT = 100;
2383 VkResult res;
2384
2385 RandomNumberGenerator rand{123};
2386
2387 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2388 bufferInfo.size = BUF_SIZE;
2389 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2390
2391 uint32_t memoryTypeBits = UINT32_MAX;
2392 {
2393 VkBuffer dummyBuffer;
2394 res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2395 assert(res == VK_SUCCESS);
2396
2397 VkMemoryRequirements memReq;
2398 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2399 memoryTypeBits = memReq.memoryTypeBits;
2400
2401 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2402 }
2403
2404 VmaAllocationCreateInfo poolAllocInfo = {};
2405 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2406 uint32_t memTypeIndex;
2407 res = vmaFindMemoryTypeIndex(
2408 g_hAllocator,
2409 memoryTypeBits,
2410 &poolAllocInfo,
2411 &memTypeIndex);
2412
2413 VmaPoolCreateInfo poolCreateInfo = {};
2414 poolCreateInfo.memoryTypeIndex = memTypeIndex;
2415 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
2416 poolCreateInfo.minBlockCount = 1;
2417 poolCreateInfo.maxBlockCount = 4;
2418 poolCreateInfo.frameInUseCount = 0;
2419
2420 VmaPool pool;
2421 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2422 assert(res == VK_SUCCESS);
2423
2424 vmaSetCurrentFrameIndex(g_hAllocator, 1);
2425
2426 VmaAllocationCreateInfo allocInfo = {};
2427 allocInfo.pool = pool;
2428 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2429 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2430
2431 struct BufItem
2432 {
2433 VkBuffer Buf;
2434 VmaAllocation Alloc;
2435 };
2436 std::vector<BufItem> items;
2437
2438 // Fill entire pool.
2439 for(size_t i = 0; i < BUF_COUNT; ++i)
2440 {
2441 BufItem item;
2442 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2443 assert(res == VK_SUCCESS);
2444 items.push_back(item);
2445 }
2446
2447 // Make sure that another allocation would fail.
2448 {
2449 BufItem item;
2450 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2451 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
2452 }
2453
2454 // Validate that no buffer is lost. Also check that they are not mapped.
2455 for(size_t i = 0; i < items.size(); ++i)
2456 {
2457 VmaAllocationInfo allocInfo;
2458 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2459 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2460 assert(allocInfo.pMappedData == nullptr);
2461 }
2462
2463 // Free some percent of random items.
2464 {
2465 const size_t PERCENT_TO_FREE = 10;
2466 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
2467 for(size_t i = 0; i < itemsToFree; ++i)
2468 {
2469 size_t index = (size_t)rand.Generate() % items.size();
2470 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2471 items.erase(items.begin() + index);
2472 }
2473 }
2474
2475 // Randomly allocate and free items.
2476 {
2477 const size_t OPERATION_COUNT = BUF_COUNT;
2478 for(size_t i = 0; i < OPERATION_COUNT; ++i)
2479 {
2480 bool allocate = rand.Generate() % 2 != 0;
2481 if(allocate)
2482 {
2483 if(items.size() < BUF_COUNT)
2484 {
2485 BufItem item;
2486 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2487 assert(res == VK_SUCCESS);
2488 items.push_back(item);
2489 }
2490 }
2491 else // Free
2492 {
2493 if(!items.empty())
2494 {
2495 size_t index = (size_t)rand.Generate() % items.size();
2496 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2497 items.erase(items.begin() + index);
2498 }
2499 }
2500 }
2501 }
2502
2503 // Allocate up to maximum.
2504 while(items.size() < BUF_COUNT)
2505 {
2506 BufItem item;
2507 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2508 assert(res == VK_SUCCESS);
2509 items.push_back(item);
2510 }
2511
2512 // Validate that no buffer is lost.
2513 for(size_t i = 0; i < items.size(); ++i)
2514 {
2515 VmaAllocationInfo allocInfo;
2516 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2517 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2518 }
2519
2520 // Next frame.
2521 vmaSetCurrentFrameIndex(g_hAllocator, 2);
2522
2523 // Allocate another BUF_COUNT buffers.
2524 for(size_t i = 0; i < BUF_COUNT; ++i)
2525 {
2526 BufItem item;
2527 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2528 assert(res == VK_SUCCESS);
2529 items.push_back(item);
2530 }
2531
2532 // Make sure the first BUF_COUNT is lost. Delete them.
2533 for(size_t i = 0; i < BUF_COUNT; ++i)
2534 {
2535 VmaAllocationInfo allocInfo;
2536 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2537 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
2538 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2539 }
2540 items.erase(items.begin(), items.begin() + BUF_COUNT);
2541
2542 // Validate that no buffer is lost.
2543 for(size_t i = 0; i < items.size(); ++i)
2544 {
2545 VmaAllocationInfo allocInfo;
2546 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2547 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2548 }
2549
2550 // Free one item.
2551 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
2552 items.pop_back();
2553
2554 // Validate statistics.
2555 {
2556 VmaPoolStats poolStats = {};
2557 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2558 assert(poolStats.allocationCount == items.size());
2559 assert(poolStats.size = BUF_COUNT * BUF_SIZE);
2560 assert(poolStats.unusedRangeCount == 1);
2561 assert(poolStats.unusedRangeSizeMax == BUF_SIZE);
2562 assert(poolStats.unusedSize == BUF_SIZE);
2563 }
2564
2565 // Free all remaining items.
2566 for(size_t i = items.size(); i--; )
2567 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2568 items.clear();
2569
2570 // Allocate maximum items again.
2571 for(size_t i = 0; i < BUF_COUNT; ++i)
2572 {
2573 BufItem item;
2574 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2575 assert(res == VK_SUCCESS);
2576 items.push_back(item);
2577 }
2578
2579 // Delete every other item.
2580 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
2581 {
2582 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2583 items.erase(items.begin() + i);
2584 }
2585
2586 // Defragment!
2587 {
2588 std::vector<VmaAllocation> allocationsToDefragment(items.size());
2589 for(size_t i = 0; i < items.size(); ++i)
2590 allocationsToDefragment[i] = items[i].Alloc;
2591
2592 VmaDefragmentationStats defragmentationStats;
2593 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
2594 assert(res == VK_SUCCESS);
2595 assert(defragmentationStats.deviceMemoryBlocksFreed == 2);
2596 }
2597
2598 // Free all remaining items.
2599 for(size_t i = items.size(); i--; )
2600 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2601 items.clear();
2602
2603 ////////////////////////////////////////////////////////////////////////////////
2604 // Test for vmaMakePoolAllocationsLost
2605
2606 // Allocate 4 buffers on frame 10.
2607 vmaSetCurrentFrameIndex(g_hAllocator, 10);
2608 for(size_t i = 0; i < 4; ++i)
2609 {
2610 BufItem item;
2611 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2612 assert(res == VK_SUCCESS);
2613 items.push_back(item);
2614 }
2615
2616 // Touch first 2 of them on frame 11.
2617 vmaSetCurrentFrameIndex(g_hAllocator, 11);
2618 for(size_t i = 0; i < 2; ++i)
2619 {
2620 VmaAllocationInfo allocInfo;
2621 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2622 }
2623
2624 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
2625 size_t lostCount = 0xDEADC0DE;
2626 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
2627 assert(lostCount == 2);
2628
2629 // Make another call. Now 0 should be lost.
2630 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
2631 assert(lostCount == 0);
2632
2633 // Make another call, with null count. Should not crash.
2634 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
2635
2636 // END: Free all remaining items.
2637 for(size_t i = items.size(); i--; )
2638 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2639
2640 items.clear();
2641
Adam Sawickid2924172018-06-11 12:48:46 +02002642 ////////////////////////////////////////////////////////////////////////////////
2643 // Test for allocation too large for pool
2644
2645 {
2646 VmaAllocationCreateInfo allocCreateInfo = {};
2647 allocCreateInfo.pool = pool;
2648
2649 VkMemoryRequirements memReq;
2650 memReq.memoryTypeBits = UINT32_MAX;
2651 memReq.alignment = 1;
2652 memReq.size = poolCreateInfo.blockSize + 4;
2653
2654 VmaAllocation alloc = nullptr;
2655 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2656 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
2657 }
2658
Adam Sawickib8333fb2018-03-13 16:15:53 +01002659 vmaDestroyPool(g_hAllocator, pool);
2660}
2661
Adam Sawickie44c6262018-06-15 14:30:39 +02002662static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
2663{
2664 const uint8_t* pBytes = (const uint8_t*)pMemory;
2665 for(size_t i = 0; i < size; ++i)
2666 {
2667 if(pBytes[i] != pattern)
2668 {
2669 return false;
2670 }
2671 }
2672 return true;
2673}
2674
2675static void TestAllocationsInitialization()
2676{
2677 VkResult res;
2678
2679 const size_t BUF_SIZE = 1024;
2680
2681 // Create pool.
2682
2683 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2684 bufInfo.size = BUF_SIZE;
2685 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2686
2687 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
2688 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2689
2690 VmaPoolCreateInfo poolCreateInfo = {};
2691 poolCreateInfo.blockSize = BUF_SIZE * 10;
2692 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
2693 poolCreateInfo.maxBlockCount = 1;
2694 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2695 assert(res == VK_SUCCESS);
2696
2697 VmaAllocationCreateInfo bufAllocCreateInfo = {};
2698 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
2699 assert(res == VK_SUCCESS);
2700
2701 // Create one persistently mapped buffer to keep memory of this block mapped,
2702 // so that pointer to mapped data will remain (more or less...) valid even
2703 // after destruction of other allocations.
2704
2705 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2706 VkBuffer firstBuf;
2707 VmaAllocation firstAlloc;
2708 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
2709 assert(res == VK_SUCCESS);
2710
2711 // Test buffers.
2712
2713 for(uint32_t i = 0; i < 2; ++i)
2714 {
2715 const bool persistentlyMapped = i == 0;
2716 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
2717 VkBuffer buf;
2718 VmaAllocation alloc;
2719 VmaAllocationInfo allocInfo;
2720 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
2721 assert(res == VK_SUCCESS);
2722
2723 void* pMappedData;
2724 if(!persistentlyMapped)
2725 {
2726 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
2727 assert(res == VK_SUCCESS);
2728 }
2729 else
2730 {
2731 pMappedData = allocInfo.pMappedData;
2732 }
2733
2734 // Validate initialized content
2735 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
2736 assert(valid);
2737
2738 if(!persistentlyMapped)
2739 {
2740 vmaUnmapMemory(g_hAllocator, alloc);
2741 }
2742
2743 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2744
2745 // Validate freed content
2746 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
2747 assert(valid);
2748 }
2749
2750 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
2751 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
2752}
2753
Adam Sawickib8333fb2018-03-13 16:15:53 +01002754static void TestPool_Benchmark(
2755 PoolTestResult& outResult,
2756 const PoolTestConfig& config)
2757{
2758 assert(config.ThreadCount > 0);
2759
2760 RandomNumberGenerator mainRand{config.RandSeed};
2761
2762 uint32_t allocationSizeProbabilitySum = std::accumulate(
2763 config.AllocationSizes.begin(),
2764 config.AllocationSizes.end(),
2765 0u,
2766 [](uint32_t sum, const AllocationSize& allocSize) {
2767 return sum + allocSize.Probability;
2768 });
2769
2770 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2771 bufferInfo.size = 256; // Whatever.
2772 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2773
2774 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2775 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2776 imageInfo.extent.width = 256; // Whatever.
2777 imageInfo.extent.height = 256; // Whatever.
2778 imageInfo.extent.depth = 1;
2779 imageInfo.mipLevels = 1;
2780 imageInfo.arrayLayers = 1;
2781 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2782 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
2783 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2784 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
2785 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2786
2787 uint32_t bufferMemoryTypeBits = UINT32_MAX;
2788 {
2789 VkBuffer dummyBuffer;
2790 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2791 assert(res == VK_SUCCESS);
2792
2793 VkMemoryRequirements memReq;
2794 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2795 bufferMemoryTypeBits = memReq.memoryTypeBits;
2796
2797 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2798 }
2799
2800 uint32_t imageMemoryTypeBits = UINT32_MAX;
2801 {
2802 VkImage dummyImage;
2803 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
2804 assert(res == VK_SUCCESS);
2805
2806 VkMemoryRequirements memReq;
2807 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
2808 imageMemoryTypeBits = memReq.memoryTypeBits;
2809
2810 vkDestroyImage(g_hDevice, dummyImage, nullptr);
2811 }
2812
2813 uint32_t memoryTypeBits = 0;
2814 if(config.UsesBuffers() && config.UsesImages())
2815 {
2816 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
2817 if(memoryTypeBits == 0)
2818 {
2819 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
2820 return;
2821 }
2822 }
2823 else if(config.UsesBuffers())
2824 memoryTypeBits = bufferMemoryTypeBits;
2825 else if(config.UsesImages())
2826 memoryTypeBits = imageMemoryTypeBits;
2827 else
2828 assert(0);
2829
2830 VmaPoolCreateInfo poolCreateInfo = {};
2831 poolCreateInfo.memoryTypeIndex = 0;
2832 poolCreateInfo.minBlockCount = 1;
2833 poolCreateInfo.maxBlockCount = 1;
2834 poolCreateInfo.blockSize = config.PoolSize;
2835 poolCreateInfo.frameInUseCount = 1;
2836
2837 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
2838 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2839 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2840
2841 VmaPool pool;
2842 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2843 assert(res == VK_SUCCESS);
2844
2845 // Start time measurement - after creating pool and initializing data structures.
2846 time_point timeBeg = std::chrono::high_resolution_clock::now();
2847
2848 ////////////////////////////////////////////////////////////////////////////////
2849 // ThreadProc
2850 auto ThreadProc = [&](
2851 PoolTestThreadResult* outThreadResult,
2852 uint32_t randSeed,
2853 HANDLE frameStartEvent,
2854 HANDLE frameEndEvent) -> void
2855 {
2856 RandomNumberGenerator threadRand{randSeed};
2857
2858 outThreadResult->AllocationTimeMin = duration::max();
2859 outThreadResult->AllocationTimeSum = duration::zero();
2860 outThreadResult->AllocationTimeMax = duration::min();
2861 outThreadResult->DeallocationTimeMin = duration::max();
2862 outThreadResult->DeallocationTimeSum = duration::zero();
2863 outThreadResult->DeallocationTimeMax = duration::min();
2864 outThreadResult->AllocationCount = 0;
2865 outThreadResult->DeallocationCount = 0;
2866 outThreadResult->LostAllocationCount = 0;
2867 outThreadResult->LostAllocationTotalSize = 0;
2868 outThreadResult->FailedAllocationCount = 0;
2869 outThreadResult->FailedAllocationTotalSize = 0;
2870
2871 struct Item
2872 {
2873 VkDeviceSize BufferSize;
2874 VkExtent2D ImageSize;
2875 VkBuffer Buf;
2876 VkImage Image;
2877 VmaAllocation Alloc;
2878
2879 VkDeviceSize CalcSizeBytes() const
2880 {
2881 return BufferSize +
2882 ImageSize.width * ImageSize.height * 4;
2883 }
2884 };
2885 std::vector<Item> unusedItems, usedItems;
2886
2887 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
2888
2889 // Create all items - all unused, not yet allocated.
2890 for(size_t i = 0; i < threadTotalItemCount; ++i)
2891 {
2892 Item item = {};
2893
2894 uint32_t allocSizeIndex = 0;
2895 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
2896 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
2897 r -= config.AllocationSizes[allocSizeIndex++].Probability;
2898
2899 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
2900 if(allocSize.BufferSizeMax > 0)
2901 {
2902 assert(allocSize.BufferSizeMin > 0);
2903 assert(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
2904 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
2905 item.BufferSize = allocSize.BufferSizeMin;
2906 else
2907 {
2908 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
2909 item.BufferSize = item.BufferSize / 16 * 16;
2910 }
2911 }
2912 else
2913 {
2914 assert(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
2915 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
2916 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
2917 else
2918 {
2919 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2920 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2921 }
2922 }
2923
2924 unusedItems.push_back(item);
2925 }
2926
2927 auto Allocate = [&](Item& item) -> VkResult
2928 {
2929 VmaAllocationCreateInfo allocCreateInfo = {};
2930 allocCreateInfo.pool = pool;
2931 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2932 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2933
2934 if(item.BufferSize)
2935 {
2936 bufferInfo.size = item.BufferSize;
2937 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2938 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
2939 }
2940 else
2941 {
2942 assert(item.ImageSize.width && item.ImageSize.height);
2943
2944 imageInfo.extent.width = item.ImageSize.width;
2945 imageInfo.extent.height = item.ImageSize.height;
2946 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2947 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
2948 }
2949 };
2950
2951 ////////////////////////////////////////////////////////////////////////////////
2952 // Frames
2953 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2954 {
2955 WaitForSingleObject(frameStartEvent, INFINITE);
2956
2957 // Always make some percent of used bufs unused, to choose different used ones.
2958 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
2959 for(size_t i = 0; i < bufsToMakeUnused; ++i)
2960 {
2961 size_t index = threadRand.Generate() % usedItems.size();
2962 unusedItems.push_back(usedItems[index]);
2963 usedItems.erase(usedItems.begin() + index);
2964 }
2965
2966 // Determine which bufs we want to use in this frame.
2967 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
2968 / config.ThreadCount;
2969 assert(usedBufCount < usedItems.size() + unusedItems.size());
2970 // Move some used to unused.
2971 while(usedBufCount < usedItems.size())
2972 {
2973 size_t index = threadRand.Generate() % usedItems.size();
2974 unusedItems.push_back(usedItems[index]);
2975 usedItems.erase(usedItems.begin() + index);
2976 }
2977 // Move some unused to used.
2978 while(usedBufCount > usedItems.size())
2979 {
2980 size_t index = threadRand.Generate() % unusedItems.size();
2981 usedItems.push_back(unusedItems[index]);
2982 unusedItems.erase(unusedItems.begin() + index);
2983 }
2984
2985 uint32_t touchExistingCount = 0;
2986 uint32_t touchLostCount = 0;
2987 uint32_t createSucceededCount = 0;
2988 uint32_t createFailedCount = 0;
2989
2990 // Touch all used bufs. If not created or lost, allocate.
2991 for(size_t i = 0; i < usedItems.size(); ++i)
2992 {
2993 Item& item = usedItems[i];
2994 // Not yet created.
2995 if(item.Alloc == VK_NULL_HANDLE)
2996 {
2997 res = Allocate(item);
2998 ++outThreadResult->AllocationCount;
2999 if(res != VK_SUCCESS)
3000 {
3001 item.Alloc = VK_NULL_HANDLE;
3002 item.Buf = VK_NULL_HANDLE;
3003 ++outThreadResult->FailedAllocationCount;
3004 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
3005 ++createFailedCount;
3006 }
3007 else
3008 ++createSucceededCount;
3009 }
3010 else
3011 {
3012 // Touch.
3013 VmaAllocationInfo allocInfo;
3014 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
3015 // Lost.
3016 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3017 {
3018 ++touchLostCount;
3019
3020 // Destroy.
3021 {
3022 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
3023 if(item.Buf)
3024 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
3025 else
3026 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
3027 ++outThreadResult->DeallocationCount;
3028 }
3029 item.Alloc = VK_NULL_HANDLE;
3030 item.Buf = VK_NULL_HANDLE;
3031
3032 ++outThreadResult->LostAllocationCount;
3033 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
3034
3035 // Recreate.
3036 res = Allocate(item);
3037 ++outThreadResult->AllocationCount;
3038 // Creation failed.
3039 if(res != VK_SUCCESS)
3040 {
3041 ++outThreadResult->FailedAllocationCount;
3042 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
3043 ++createFailedCount;
3044 }
3045 else
3046 ++createSucceededCount;
3047 }
3048 else
3049 ++touchExistingCount;
3050 }
3051 }
3052
3053 /*
3054 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
3055 randSeed, frameIndex,
3056 touchExistingCount, touchLostCount,
3057 createSucceededCount, createFailedCount);
3058 */
3059
3060 SetEvent(frameEndEvent);
3061 }
3062
3063 // Free all remaining items.
3064 for(size_t i = usedItems.size(); i--; )
3065 {
3066 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
3067 if(usedItems[i].Buf)
3068 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
3069 else
3070 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
3071 ++outThreadResult->DeallocationCount;
3072 }
3073 for(size_t i = unusedItems.size(); i--; )
3074 {
3075 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
3076 if(unusedItems[i].Buf)
3077 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
3078 else
3079 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
3080 ++outThreadResult->DeallocationCount;
3081 }
3082 };
3083
3084 // Launch threads.
3085 uint32_t threadRandSeed = mainRand.Generate();
3086 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
3087 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
3088 std::vector<std::thread> bkgThreads;
3089 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
3090 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3091 {
3092 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
3093 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
3094 bkgThreads.emplace_back(std::bind(
3095 ThreadProc,
3096 &threadResults[threadIndex],
3097 threadRandSeed + threadIndex,
3098 frameStartEvents[threadIndex],
3099 frameEndEvents[threadIndex]));
3100 }
3101
3102 // Execute frames.
3103 assert(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
3104 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
3105 {
3106 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
3107 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3108 SetEvent(frameStartEvents[threadIndex]);
3109 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
3110 }
3111
3112 // Wait for threads finished
3113 for(size_t i = 0; i < bkgThreads.size(); ++i)
3114 {
3115 bkgThreads[i].join();
3116 CloseHandle(frameEndEvents[i]);
3117 CloseHandle(frameStartEvents[i]);
3118 }
3119 bkgThreads.clear();
3120
3121 // Finish time measurement - before destroying pool.
3122 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
3123
3124 vmaDestroyPool(g_hAllocator, pool);
3125
3126 outResult.AllocationTimeMin = duration::max();
3127 outResult.AllocationTimeAvg = duration::zero();
3128 outResult.AllocationTimeMax = duration::min();
3129 outResult.DeallocationTimeMin = duration::max();
3130 outResult.DeallocationTimeAvg = duration::zero();
3131 outResult.DeallocationTimeMax = duration::min();
3132 outResult.LostAllocationCount = 0;
3133 outResult.LostAllocationTotalSize = 0;
3134 outResult.FailedAllocationCount = 0;
3135 outResult.FailedAllocationTotalSize = 0;
3136 size_t allocationCount = 0;
3137 size_t deallocationCount = 0;
3138 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3139 {
3140 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
3141 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
3142 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
3143 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
3144 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
3145 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
3146 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
3147 allocationCount += threadResult.AllocationCount;
3148 deallocationCount += threadResult.DeallocationCount;
3149 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
3150 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
3151 outResult.LostAllocationCount += threadResult.LostAllocationCount;
3152 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
3153 }
3154 if(allocationCount)
3155 outResult.AllocationTimeAvg /= allocationCount;
3156 if(deallocationCount)
3157 outResult.DeallocationTimeAvg /= deallocationCount;
3158}
3159
3160static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
3161{
3162 if(ptr1 < ptr2)
3163 return ptr1 + size1 > ptr2;
3164 else if(ptr2 < ptr1)
3165 return ptr2 + size2 > ptr1;
3166 else
3167 return true;
3168}
3169
3170static void TestMapping()
3171{
3172 wprintf(L"Testing mapping...\n");
3173
3174 VkResult res;
3175 uint32_t memTypeIndex = UINT32_MAX;
3176
3177 enum TEST
3178 {
3179 TEST_NORMAL,
3180 TEST_POOL,
3181 TEST_DEDICATED,
3182 TEST_COUNT
3183 };
3184 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3185 {
3186 VmaPool pool = nullptr;
3187 if(testIndex == TEST_POOL)
3188 {
3189 assert(memTypeIndex != UINT32_MAX);
3190 VmaPoolCreateInfo poolInfo = {};
3191 poolInfo.memoryTypeIndex = memTypeIndex;
3192 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
3193 assert(res == VK_SUCCESS);
3194 }
3195
3196 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3197 bufInfo.size = 0x10000;
3198 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3199
3200 VmaAllocationCreateInfo allocCreateInfo = {};
3201 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3202 allocCreateInfo.pool = pool;
3203 if(testIndex == TEST_DEDICATED)
3204 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3205
3206 VmaAllocationInfo allocInfo;
3207
3208 // Mapped manually
3209
3210 // Create 2 buffers.
3211 BufferInfo bufferInfos[3];
3212 for(size_t i = 0; i < 2; ++i)
3213 {
3214 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3215 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
3216 assert(res == VK_SUCCESS);
3217 assert(allocInfo.pMappedData == nullptr);
3218 memTypeIndex = allocInfo.memoryType;
3219 }
3220
3221 // Map buffer 0.
3222 char* data00 = nullptr;
3223 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
3224 assert(res == VK_SUCCESS && data00 != nullptr);
3225 data00[0xFFFF] = data00[0];
3226
3227 // Map buffer 0 second time.
3228 char* data01 = nullptr;
3229 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
3230 assert(res == VK_SUCCESS && data01 == data00);
3231
3232 // Map buffer 1.
3233 char* data1 = nullptr;
3234 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
3235 assert(res == VK_SUCCESS && data1 != nullptr);
3236 assert(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
3237 data1[0xFFFF] = data1[0];
3238
3239 // Unmap buffer 0 two times.
3240 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3241 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3242 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
3243 assert(allocInfo.pMappedData == nullptr);
3244
3245 // Unmap buffer 1.
3246 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
3247 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
3248 assert(allocInfo.pMappedData == nullptr);
3249
3250 // Create 3rd buffer - persistently mapped.
3251 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3252 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3253 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
3254 assert(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
3255
3256 // Map buffer 2.
3257 char* data2 = nullptr;
3258 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
3259 assert(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
3260 data2[0xFFFF] = data2[0];
3261
3262 // Unmap buffer 2.
3263 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
3264 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
3265 assert(allocInfo.pMappedData == data2);
3266
3267 // Destroy all buffers.
3268 for(size_t i = 3; i--; )
3269 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
3270
3271 vmaDestroyPool(g_hAllocator, pool);
3272 }
3273}
3274
3275static void TestMappingMultithreaded()
3276{
3277 wprintf(L"Testing mapping multithreaded...\n");
3278
3279 static const uint32_t threadCount = 16;
3280 static const uint32_t bufferCount = 1024;
3281 static const uint32_t threadBufferCount = bufferCount / threadCount;
3282
3283 VkResult res;
3284 volatile uint32_t memTypeIndex = UINT32_MAX;
3285
3286 enum TEST
3287 {
3288 TEST_NORMAL,
3289 TEST_POOL,
3290 TEST_DEDICATED,
3291 TEST_COUNT
3292 };
3293 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3294 {
3295 VmaPool pool = nullptr;
3296 if(testIndex == TEST_POOL)
3297 {
3298 assert(memTypeIndex != UINT32_MAX);
3299 VmaPoolCreateInfo poolInfo = {};
3300 poolInfo.memoryTypeIndex = memTypeIndex;
3301 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
3302 assert(res == VK_SUCCESS);
3303 }
3304
3305 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3306 bufCreateInfo.size = 0x10000;
3307 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3308
3309 VmaAllocationCreateInfo allocCreateInfo = {};
3310 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3311 allocCreateInfo.pool = pool;
3312 if(testIndex == TEST_DEDICATED)
3313 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3314
3315 std::thread threads[threadCount];
3316 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3317 {
3318 threads[threadIndex] = std::thread([=, &memTypeIndex](){
3319 // ======== THREAD FUNCTION ========
3320
3321 RandomNumberGenerator rand{threadIndex};
3322
3323 enum class MODE
3324 {
3325 // Don't map this buffer at all.
3326 DONT_MAP,
3327 // Map and quickly unmap.
3328 MAP_FOR_MOMENT,
3329 // Map and unmap before destruction.
3330 MAP_FOR_LONGER,
3331 // Map two times. Quickly unmap, second unmap before destruction.
3332 MAP_TWO_TIMES,
3333 // Create this buffer as persistently mapped.
3334 PERSISTENTLY_MAPPED,
3335 COUNT
3336 };
3337 std::vector<BufferInfo> bufInfos{threadBufferCount};
3338 std::vector<MODE> bufModes{threadBufferCount};
3339
3340 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
3341 {
3342 BufferInfo& bufInfo = bufInfos[bufferIndex];
3343 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
3344 bufModes[bufferIndex] = mode;
3345
3346 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
3347 if(mode == MODE::PERSISTENTLY_MAPPED)
3348 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3349
3350 VmaAllocationInfo allocInfo;
3351 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
3352 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
3353 assert(res == VK_SUCCESS);
3354
3355 if(memTypeIndex == UINT32_MAX)
3356 memTypeIndex = allocInfo.memoryType;
3357
3358 char* data = nullptr;
3359
3360 if(mode == MODE::PERSISTENTLY_MAPPED)
3361 {
3362 data = (char*)allocInfo.pMappedData;
3363 assert(data != nullptr);
3364 }
3365 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
3366 mode == MODE::MAP_TWO_TIMES)
3367 {
3368 assert(data == nullptr);
3369 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
3370 assert(res == VK_SUCCESS && data != nullptr);
3371
3372 if(mode == MODE::MAP_TWO_TIMES)
3373 {
3374 char* data2 = nullptr;
3375 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
3376 assert(res == VK_SUCCESS && data2 == data);
3377 }
3378 }
3379 else if(mode == MODE::DONT_MAP)
3380 {
3381 assert(allocInfo.pMappedData == nullptr);
3382 }
3383 else
3384 assert(0);
3385
3386 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3387 if(data)
3388 data[0xFFFF] = data[0];
3389
3390 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
3391 {
3392 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
3393
3394 VmaAllocationInfo allocInfo;
3395 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
3396 if(mode == MODE::MAP_FOR_MOMENT)
3397 assert(allocInfo.pMappedData == nullptr);
3398 else
3399 assert(allocInfo.pMappedData == data);
3400 }
3401
3402 switch(rand.Generate() % 3)
3403 {
3404 case 0: Sleep(0); break; // Yield.
3405 case 1: Sleep(10); break; // 10 ms
3406 // default: No sleep.
3407 }
3408
3409 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3410 if(data)
3411 data[0xFFFF] = data[0];
3412 }
3413
3414 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
3415 {
3416 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
3417 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
3418 {
3419 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
3420
3421 VmaAllocationInfo allocInfo;
3422 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
3423 assert(allocInfo.pMappedData == nullptr);
3424 }
3425
3426 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
3427 }
3428 });
3429 }
3430
3431 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3432 threads[threadIndex].join();
3433
3434 vmaDestroyPool(g_hAllocator, pool);
3435 }
3436}
3437
3438static void WriteMainTestResultHeader(FILE* file)
3439{
3440 fprintf(file,
Adam Sawicki740b08f2018-08-27 13:42:07 +02003441 "Code,Time,"
3442 "Threads,Buffers and images,Sizes,Operations,Allocation strategy,Free order,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003443 "Total Time (us),"
3444 "Allocation Time Min (us),"
3445 "Allocation Time Avg (us),"
3446 "Allocation Time Max (us),"
3447 "Deallocation Time Min (us),"
3448 "Deallocation Time Avg (us),"
3449 "Deallocation Time Max (us),"
3450 "Total Memory Allocated (B),"
3451 "Free Range Size Avg (B),"
3452 "Free Range Size Max (B)\n");
3453}
3454
3455static void WriteMainTestResult(
3456 FILE* file,
3457 const char* codeDescription,
3458 const char* testDescription,
3459 const Config& config, const Result& result)
3460{
3461 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3462 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3463 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3464 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3465 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3466 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3467 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3468
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003469 std::string currTime;
3470 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003471
3472 fprintf(file,
3473 "%s,%s,%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003474 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
3475 codeDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003476 currTime.c_str(),
Adam Sawicki740b08f2018-08-27 13:42:07 +02003477 testDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01003478 totalTimeSeconds * 1e6f,
3479 allocationTimeMinSeconds * 1e6f,
3480 allocationTimeAvgSeconds * 1e6f,
3481 allocationTimeMaxSeconds * 1e6f,
3482 deallocationTimeMinSeconds * 1e6f,
3483 deallocationTimeAvgSeconds * 1e6f,
3484 deallocationTimeMaxSeconds * 1e6f,
3485 result.TotalMemoryAllocated,
3486 result.FreeRangeSizeAvg,
3487 result.FreeRangeSizeMax);
3488}
3489
3490static void WritePoolTestResultHeader(FILE* file)
3491{
3492 fprintf(file,
3493 "Code,Test,Time,"
3494 "Config,"
3495 "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 "Lost Allocation Count,"
3503 "Lost Allocation Total Size (B),"
3504 "Failed Allocation Count,"
3505 "Failed Allocation Total Size (B)\n");
3506}
3507
3508static void WritePoolTestResult(
3509 FILE* file,
3510 const char* codeDescription,
3511 const char* testDescription,
3512 const PoolTestConfig& config,
3513 const PoolTestResult& result)
3514{
3515 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3516 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3517 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3518 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3519 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3520 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3521 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3522
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003523 std::string currTime;
3524 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003525
3526 fprintf(file,
3527 "%s,%s,%s,"
3528 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
3529 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
3530 // General
3531 codeDescription,
3532 testDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003533 currTime.c_str(),
Adam Sawickib8333fb2018-03-13 16:15:53 +01003534 // Config
3535 config.ThreadCount,
3536 (unsigned long long)config.PoolSize,
3537 config.FrameCount,
3538 config.TotalItemCount,
3539 config.UsedItemCountMin,
3540 config.UsedItemCountMax,
3541 config.ItemsToMakeUnusedPercent,
3542 // Results
3543 totalTimeSeconds * 1e6f,
3544 allocationTimeMinSeconds * 1e6f,
3545 allocationTimeAvgSeconds * 1e6f,
3546 allocationTimeMaxSeconds * 1e6f,
3547 deallocationTimeMinSeconds * 1e6f,
3548 deallocationTimeAvgSeconds * 1e6f,
3549 deallocationTimeMaxSeconds * 1e6f,
3550 result.LostAllocationCount,
3551 result.LostAllocationTotalSize,
3552 result.FailedAllocationCount,
3553 result.FailedAllocationTotalSize);
3554}
3555
3556static void PerformCustomMainTest(FILE* file)
3557{
3558 Config config{};
3559 config.RandSeed = 65735476;
3560 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
3561 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3562 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3563 config.FreeOrder = FREE_ORDER::FORWARD;
3564 config.ThreadCount = 16;
3565 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02003566 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003567
3568 // Buffers
3569 //config.AllocationSizes.push_back({4, 16, 1024});
3570 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3571
3572 // Images
3573 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3574 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3575
3576 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3577 config.AdditionalOperationCount = 1024;
3578
3579 Result result{};
3580 VkResult res = MainTest(result, config);
3581 assert(res == VK_SUCCESS);
3582 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
3583}
3584
3585static void PerformCustomPoolTest(FILE* file)
3586{
3587 PoolTestConfig config;
3588 config.PoolSize = 100 * 1024 * 1024;
3589 config.RandSeed = 2345764;
3590 config.ThreadCount = 1;
3591 config.FrameCount = 200;
3592 config.ItemsToMakeUnusedPercent = 2;
3593
3594 AllocationSize allocSize = {};
3595 allocSize.BufferSizeMin = 1024;
3596 allocSize.BufferSizeMax = 1024 * 1024;
3597 allocSize.Probability = 1;
3598 config.AllocationSizes.push_back(allocSize);
3599
3600 allocSize.BufferSizeMin = 0;
3601 allocSize.BufferSizeMax = 0;
3602 allocSize.ImageSizeMin = 128;
3603 allocSize.ImageSizeMax = 1024;
3604 allocSize.Probability = 1;
3605 config.AllocationSizes.push_back(allocSize);
3606
3607 config.PoolSize = config.CalcAvgResourceSize() * 200;
3608 config.UsedItemCountMax = 160;
3609 config.TotalItemCount = config.UsedItemCountMax * 10;
3610 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3611
3612 g_MemoryAliasingWarningEnabled = false;
3613 PoolTestResult result = {};
3614 TestPool_Benchmark(result, config);
3615 g_MemoryAliasingWarningEnabled = true;
3616
3617 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
3618}
3619
Adam Sawickib8333fb2018-03-13 16:15:53 +01003620static void PerformMainTests(FILE* file)
3621{
3622 uint32_t repeatCount = 1;
3623 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3624
3625 Config config{};
3626 config.RandSeed = 65735476;
3627 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3628 config.FreeOrder = FREE_ORDER::FORWARD;
3629
3630 size_t threadCountCount = 1;
3631 switch(ConfigType)
3632 {
3633 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3634 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3635 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
3636 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
3637 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
3638 default: assert(0);
3639 }
Adam Sawicki0667e332018-08-24 17:26:44 +02003640
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003641 const size_t strategyCount = GetAllocationStrategyCount();
Adam Sawicki0667e332018-08-24 17:26:44 +02003642
Adam Sawickib8333fb2018-03-13 16:15:53 +01003643 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3644 {
3645 std::string desc1;
3646
3647 switch(threadCountIndex)
3648 {
3649 case 0:
3650 desc1 += "1_thread";
3651 config.ThreadCount = 1;
3652 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3653 break;
3654 case 1:
3655 desc1 += "16_threads+0%_common";
3656 config.ThreadCount = 16;
3657 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3658 break;
3659 case 2:
3660 desc1 += "16_threads+50%_common";
3661 config.ThreadCount = 16;
3662 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3663 break;
3664 case 3:
3665 desc1 += "16_threads+100%_common";
3666 config.ThreadCount = 16;
3667 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3668 break;
3669 case 4:
3670 desc1 += "2_threads+0%_common";
3671 config.ThreadCount = 2;
3672 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3673 break;
3674 case 5:
3675 desc1 += "2_threads+50%_common";
3676 config.ThreadCount = 2;
3677 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3678 break;
3679 case 6:
3680 desc1 += "2_threads+100%_common";
3681 config.ThreadCount = 2;
3682 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3683 break;
3684 default:
3685 assert(0);
3686 }
3687
3688 // 0 = buffers, 1 = images, 2 = buffers and images
3689 size_t buffersVsImagesCount = 2;
3690 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3691 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3692 {
3693 std::string desc2 = desc1;
3694 switch(buffersVsImagesIndex)
3695 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003696 case 0: desc2 += ",Buffers"; break;
3697 case 1: desc2 += ",Images"; break;
3698 case 2: desc2 += ",Buffers+Images"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003699 default: assert(0);
3700 }
3701
3702 // 0 = small, 1 = large, 2 = small and large
3703 size_t smallVsLargeCount = 2;
3704 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3705 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3706 {
3707 std::string desc3 = desc2;
3708 switch(smallVsLargeIndex)
3709 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003710 case 0: desc3 += ",Small"; break;
3711 case 1: desc3 += ",Large"; break;
3712 case 2: desc3 += ",Small+Large"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003713 default: assert(0);
3714 }
3715
3716 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3717 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3718 else
3719 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
3720
3721 // 0 = varying sizes min...max, 1 = set of constant sizes
3722 size_t constantSizesCount = 1;
3723 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3724 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3725 {
3726 std::string desc4 = desc3;
3727 switch(constantSizesIndex)
3728 {
3729 case 0: desc4 += " Varying_sizes"; break;
3730 case 1: desc4 += " Constant_sizes"; break;
3731 default: assert(0);
3732 }
3733
3734 config.AllocationSizes.clear();
3735 // Buffers present
3736 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3737 {
3738 // Small
3739 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3740 {
3741 // Varying size
3742 if(constantSizesIndex == 0)
3743 config.AllocationSizes.push_back({4, 16, 1024});
3744 // Constant sizes
3745 else
3746 {
3747 config.AllocationSizes.push_back({1, 16, 16});
3748 config.AllocationSizes.push_back({1, 64, 64});
3749 config.AllocationSizes.push_back({1, 256, 256});
3750 config.AllocationSizes.push_back({1, 1024, 1024});
3751 }
3752 }
3753 // Large
3754 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3755 {
3756 // Varying size
3757 if(constantSizesIndex == 0)
3758 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3759 // Constant sizes
3760 else
3761 {
3762 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3763 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3764 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3765 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3766 }
3767 }
3768 }
3769 // Images present
3770 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3771 {
3772 // Small
3773 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3774 {
3775 // Varying size
3776 if(constantSizesIndex == 0)
3777 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3778 // Constant sizes
3779 else
3780 {
3781 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3782 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3783 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3784 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3785 }
3786 }
3787 // Large
3788 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3789 {
3790 // Varying size
3791 if(constantSizesIndex == 0)
3792 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3793 // Constant sizes
3794 else
3795 {
3796 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3797 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3798 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3799 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3800 }
3801 }
3802 }
3803
3804 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
3805 size_t beginBytesToAllocateCount = 1;
3806 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
3807 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
3808 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
3809 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
3810 {
3811 std::string desc5 = desc4;
3812
3813 switch(beginBytesToAllocateIndex)
3814 {
3815 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003816 desc5 += ",Allocate_100%";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003817 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
3818 config.AdditionalOperationCount = 0;
3819 break;
3820 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003821 desc5 += ",Allocate_50%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003822 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
3823 config.AdditionalOperationCount = 1024;
3824 break;
3825 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003826 desc5 += ",Allocate_5%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003827 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3828 config.AdditionalOperationCount = 1024;
3829 break;
3830 case 3:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003831 desc5 += ",Allocate_95%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003832 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
3833 config.AdditionalOperationCount = 1024;
3834 break;
3835 default:
3836 assert(0);
3837 }
3838
Adam Sawicki0667e332018-08-24 17:26:44 +02003839 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01003840 {
Adam Sawicki0667e332018-08-24 17:26:44 +02003841 std::string desc6 = desc5;
3842 switch(strategyIndex)
3843 {
3844 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003845 desc6 += ",BestFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003846 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
3847 break;
3848 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003849 desc6 += ",WorstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003850 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
3851 break;
3852 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003853 desc6 += ",FirstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003854 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
3855 break;
3856 default:
3857 assert(0);
3858 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01003859
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003860 desc6 += ',';
3861 desc6 += FREE_ORDER_NAMES[(uint32_t)config.FreeOrder];
Adam Sawicki740b08f2018-08-27 13:42:07 +02003862
3863 const char* testDescription = desc6.c_str();
Adam Sawicki0667e332018-08-24 17:26:44 +02003864
3865 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3866 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003867 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawicki0667e332018-08-24 17:26:44 +02003868
3869 Result result{};
3870 VkResult res = MainTest(result, config);
3871 assert(res == VK_SUCCESS);
Adam Sawicki740b08f2018-08-27 13:42:07 +02003872 if(file)
3873 {
3874 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3875 }
Adam Sawicki0667e332018-08-24 17:26:44 +02003876 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01003877 }
3878 }
3879 }
3880 }
3881 }
3882 }
3883}
3884
3885static void PerformPoolTests(FILE* file)
3886{
3887 const size_t AVG_RESOURCES_PER_POOL = 300;
3888
3889 uint32_t repeatCount = 1;
3890 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3891
3892 PoolTestConfig config{};
3893 config.RandSeed = 2346343;
3894 config.FrameCount = 200;
3895 config.ItemsToMakeUnusedPercent = 2;
3896
3897 size_t threadCountCount = 1;
3898 switch(ConfigType)
3899 {
3900 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3901 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3902 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
3903 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
3904 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
3905 default: assert(0);
3906 }
3907 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3908 {
3909 std::string desc1;
3910
3911 switch(threadCountIndex)
3912 {
3913 case 0:
3914 desc1 += "1_thread";
3915 config.ThreadCount = 1;
3916 break;
3917 case 1:
3918 desc1 += "16_threads";
3919 config.ThreadCount = 16;
3920 break;
3921 case 2:
3922 desc1 += "2_threads";
3923 config.ThreadCount = 2;
3924 break;
3925 default:
3926 assert(0);
3927 }
3928
3929 // 0 = buffers, 1 = images, 2 = buffers and images
3930 size_t buffersVsImagesCount = 2;
3931 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3932 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3933 {
3934 std::string desc2 = desc1;
3935 switch(buffersVsImagesIndex)
3936 {
3937 case 0: desc2 += " Buffers"; break;
3938 case 1: desc2 += " Images"; break;
3939 case 2: desc2 += " Buffers+Images"; break;
3940 default: assert(0);
3941 }
3942
3943 // 0 = small, 1 = large, 2 = small and large
3944 size_t smallVsLargeCount = 2;
3945 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3946 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3947 {
3948 std::string desc3 = desc2;
3949 switch(smallVsLargeIndex)
3950 {
3951 case 0: desc3 += " Small"; break;
3952 case 1: desc3 += " Large"; break;
3953 case 2: desc3 += " Small+Large"; break;
3954 default: assert(0);
3955 }
3956
3957 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3958 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
3959 else
3960 config.PoolSize = 4ull * 1024 * 1024;
3961
3962 // 0 = varying sizes min...max, 1 = set of constant sizes
3963 size_t constantSizesCount = 1;
3964 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3965 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3966 {
3967 std::string desc4 = desc3;
3968 switch(constantSizesIndex)
3969 {
3970 case 0: desc4 += " Varying_sizes"; break;
3971 case 1: desc4 += " Constant_sizes"; break;
3972 default: assert(0);
3973 }
3974
3975 config.AllocationSizes.clear();
3976 // Buffers present
3977 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3978 {
3979 // Small
3980 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3981 {
3982 // Varying size
3983 if(constantSizesIndex == 0)
3984 config.AllocationSizes.push_back({4, 16, 1024});
3985 // Constant sizes
3986 else
3987 {
3988 config.AllocationSizes.push_back({1, 16, 16});
3989 config.AllocationSizes.push_back({1, 64, 64});
3990 config.AllocationSizes.push_back({1, 256, 256});
3991 config.AllocationSizes.push_back({1, 1024, 1024});
3992 }
3993 }
3994 // Large
3995 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3996 {
3997 // Varying size
3998 if(constantSizesIndex == 0)
3999 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
4000 // Constant sizes
4001 else
4002 {
4003 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
4004 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
4005 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
4006 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
4007 }
4008 }
4009 }
4010 // Images present
4011 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
4012 {
4013 // Small
4014 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
4015 {
4016 // Varying size
4017 if(constantSizesIndex == 0)
4018 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
4019 // Constant sizes
4020 else
4021 {
4022 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
4023 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
4024 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
4025 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
4026 }
4027 }
4028 // Large
4029 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
4030 {
4031 // Varying size
4032 if(constantSizesIndex == 0)
4033 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
4034 // Constant sizes
4035 else
4036 {
4037 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
4038 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
4039 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
4040 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
4041 }
4042 }
4043 }
4044
4045 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
4046 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
4047
4048 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
4049 size_t subscriptionModeCount;
4050 switch(ConfigType)
4051 {
4052 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
4053 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
4054 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
4055 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
4056 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
4057 default: assert(0);
4058 }
4059 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
4060 {
4061 std::string desc5 = desc4;
4062
4063 switch(subscriptionModeIndex)
4064 {
4065 case 0:
4066 desc5 += " Subscription_66%";
4067 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
4068 break;
4069 case 1:
4070 desc5 += " Subscription_133%";
4071 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
4072 break;
4073 case 2:
4074 desc5 += " Subscription_100%";
4075 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
4076 break;
4077 case 3:
4078 desc5 += " Subscription_33%";
4079 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
4080 break;
4081 case 4:
4082 desc5 += " Subscription_166%";
4083 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
4084 break;
4085 default:
4086 assert(0);
4087 }
4088
4089 config.TotalItemCount = config.UsedItemCountMax * 5;
4090 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
4091
4092 const char* testDescription = desc5.c_str();
4093
4094 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
4095 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02004096 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004097
4098 PoolTestResult result{};
4099 g_MemoryAliasingWarningEnabled = false;
4100 TestPool_Benchmark(result, config);
4101 g_MemoryAliasingWarningEnabled = true;
4102 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
4103 }
4104 }
4105 }
4106 }
4107 }
4108 }
4109}
4110
Adam Sawickia83793a2018-09-03 13:40:42 +02004111static void BasicTestBuddyAllocator()
4112{
4113 wprintf(L"Basic test buddy allocator\n");
4114
4115 RandomNumberGenerator rand{76543};
4116
4117 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4118 sampleBufCreateInfo.size = 1024; // Whatever.
4119 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4120
4121 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
4122 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4123
4124 VmaPoolCreateInfo poolCreateInfo = {};
4125 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
4126 assert(res == VK_SUCCESS);
4127
Adam Sawickid6e6d6b2018-09-21 14:07:02 +02004128 // Deliberately adding 1023 to test usable size smaller than memory block size.
4129 poolCreateInfo.blockSize = 1024 * 1024 + 1023;
Adam Sawickia83793a2018-09-03 13:40:42 +02004130 poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
Adam Sawicki80927152018-09-07 17:27:23 +02004131 //poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
Adam Sawickia83793a2018-09-03 13:40:42 +02004132
4133 VmaPool pool = nullptr;
4134 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
4135 assert(res == VK_SUCCESS);
4136
4137 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
4138
4139 VmaAllocationCreateInfo allocCreateInfo = {};
4140 allocCreateInfo.pool = pool;
4141
4142 std::vector<BufferInfo> bufInfo;
4143 BufferInfo newBufInfo;
4144 VmaAllocationInfo allocInfo;
4145
4146 bufCreateInfo.size = 1024 * 256;
4147 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4148 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
4149 assert(res == VK_SUCCESS);
4150 bufInfo.push_back(newBufInfo);
4151
4152 bufCreateInfo.size = 1024 * 512;
4153 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4154 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
4155 assert(res == VK_SUCCESS);
4156 bufInfo.push_back(newBufInfo);
4157
4158 bufCreateInfo.size = 1024 * 128;
4159 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4160 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
4161 assert(res == VK_SUCCESS);
4162 bufInfo.push_back(newBufInfo);
Adam Sawickia01d4582018-09-21 14:22:35 +02004163
4164 // Test very small allocation, smaller than minimum node size.
4165 bufCreateInfo.size = 1;
4166 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4167 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
4168 assert(res == VK_SUCCESS);
4169 bufInfo.push_back(newBufInfo);
Adam Sawickia83793a2018-09-03 13:40:42 +02004170
Adam Sawicki9933c5c2018-09-21 14:57:24 +02004171 // Test some small allocation with alignment requirement.
4172 {
4173 VkMemoryRequirements memReq;
4174 memReq.alignment = 256;
4175 memReq.memoryTypeBits = UINT32_MAX;
4176 memReq.size = 32;
4177
4178 newBufInfo.Buffer = VK_NULL_HANDLE;
4179 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo,
4180 &newBufInfo.Allocation, &allocInfo);
4181 assert(res == VK_SUCCESS);
4182 assert(allocInfo.offset % memReq.alignment == 0);
4183 bufInfo.push_back(newBufInfo);
4184 }
4185
4186 //SaveAllocatorStatsToFile(L"TEST.json");
4187
Adam Sawicki21017c62018-09-07 15:26:59 +02004188 VmaPoolStats stats = {};
4189 vmaGetPoolStats(g_hAllocator, pool, &stats);
4190 int DBG = 0; // Set breakpoint here to inspect `stats`.
4191
Adam Sawicki80927152018-09-07 17:27:23 +02004192 // Allocate enough new buffers to surely fall into second block.
4193 for(uint32_t i = 0; i < 32; ++i)
4194 {
4195 bufCreateInfo.size = 1024 * (rand.Generate() % 32 + 1);
4196 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
4197 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
4198 assert(res == VK_SUCCESS);
4199 bufInfo.push_back(newBufInfo);
4200 }
4201
4202 SaveAllocatorStatsToFile(L"BuddyTest01.json");
4203
Adam Sawickia83793a2018-09-03 13:40:42 +02004204 // Destroy the buffers in random order.
4205 while(!bufInfo.empty())
4206 {
4207 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
4208 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
4209 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
4210 bufInfo.erase(bufInfo.begin() + indexToDestroy);
4211 }
4212
4213 vmaDestroyPool(g_hAllocator, pool);
4214}
4215
Adam Sawickib8333fb2018-03-13 16:15:53 +01004216void Test()
4217{
4218 wprintf(L"TESTING:\n");
4219
Adam Sawickic6432d12018-09-21 16:44:16 +02004220 if(false)
Adam Sawicki70a683e2018-08-24 15:36:32 +02004221 {
4222 // # Temporarily insert custom tests here
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02004223 // ########################################
4224 // ########################################
Adam Sawicki80927152018-09-07 17:27:23 +02004225
Adam Sawickia83793a2018-09-03 13:40:42 +02004226 BasicTestBuddyAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02004227 return;
4228 }
4229
Adam Sawickib8333fb2018-03-13 16:15:53 +01004230 // # Simple tests
4231
4232 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02004233#if VMA_DEBUG_MARGIN
4234 TestDebugMargin();
4235#else
4236 TestPool_SameSize();
4237 TestHeapSizeLimit();
4238#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02004239#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
4240 TestAllocationsInitialization();
4241#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01004242 TestMapping();
4243 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02004244 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02004245 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02004246 TestLinearAllocatorMultiBlock();
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004247
Adam Sawicki4338f662018-09-07 14:12:37 +02004248 BasicTestBuddyAllocator();
4249
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004250 {
4251 FILE* file;
Adam Sawickic6432d12018-09-21 16:44:16 +02004252 fopen_s(&file, "Algorithms.csv", "w");
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004253 assert(file != NULL);
Adam Sawicki80927152018-09-07 17:27:23 +02004254 BenchmarkAlgorithms(file);
Adam Sawicki33d2ce72018-08-27 13:59:13 +02004255 fclose(file);
4256 }
4257
Adam Sawickib8333fb2018-03-13 16:15:53 +01004258 TestDefragmentationSimple();
4259 TestDefragmentationFull();
4260
4261 // # Detailed tests
4262 FILE* file;
4263 fopen_s(&file, "Results.csv", "w");
4264 assert(file != NULL);
4265
4266 WriteMainTestResultHeader(file);
4267 PerformMainTests(file);
4268 //PerformCustomMainTest(file);
4269
4270 WritePoolTestResultHeader(file);
4271 PerformPoolTests(file);
4272 //PerformCustomPoolTest(file);
4273
4274 fclose(file);
4275
4276 wprintf(L"Done.\n");
4277}
4278
Adam Sawickif1a793c2018-03-13 15:42:22 +01004279#endif // #ifdef _WIN32