blob: 010d1c287ac5ed4d60728192cb2985f5058d161d [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 Sawicki0a607132018-08-24 11:18:41 +020010enum CONFIG_TYPE {
11 CONFIG_TYPE_MINIMUM,
12 CONFIG_TYPE_SMALL,
13 CONFIG_TYPE_AVERAGE,
14 CONFIG_TYPE_LARGE,
15 CONFIG_TYPE_MAXIMUM,
16 CONFIG_TYPE_COUNT
17};
18
19static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
20//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
21
Adam Sawickib8333fb2018-03-13 16:15:53 +010022enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
23
Adam Sawicki0667e332018-08-24 17:26:44 +020024static const char* FREE_ORDER_NAMES[] = {
25 "FORWARD",
26 "BACKWARD",
27 "RANDOM",
Adam Sawicki0a607132018-08-24 11:18:41 +020028};
29
Adam Sawickib8333fb2018-03-13 16:15:53 +010030struct AllocationSize
31{
32 uint32_t Probability;
33 VkDeviceSize BufferSizeMin, BufferSizeMax;
34 uint32_t ImageSizeMin, ImageSizeMax;
35};
36
37struct Config
38{
39 uint32_t RandSeed;
40 VkDeviceSize BeginBytesToAllocate;
41 uint32_t AdditionalOperationCount;
42 VkDeviceSize MaxBytesToAllocate;
43 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
44 std::vector<AllocationSize> AllocationSizes;
45 uint32_t ThreadCount;
46 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
47 FREE_ORDER FreeOrder;
Adam Sawicki0667e332018-08-24 17:26:44 +020048 VmaAllocationCreateFlags AllocationStrategy; // For VMA_ALLOCATION_CREATE_STRATEGY_*
Adam Sawickib8333fb2018-03-13 16:15:53 +010049};
50
51struct Result
52{
53 duration TotalTime;
54 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
55 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
56 VkDeviceSize TotalMemoryAllocated;
57 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
58};
59
60void TestDefragmentationSimple();
61void TestDefragmentationFull();
62
63struct PoolTestConfig
64{
65 uint32_t RandSeed;
66 uint32_t ThreadCount;
67 VkDeviceSize PoolSize;
68 uint32_t FrameCount;
69 uint32_t TotalItemCount;
70 // Range for number of items used in each frame.
71 uint32_t UsedItemCountMin, UsedItemCountMax;
72 // Percent of items to make unused, and possibly make some others used in each frame.
73 uint32_t ItemsToMakeUnusedPercent;
74 std::vector<AllocationSize> AllocationSizes;
75
76 VkDeviceSize CalcAvgResourceSize() const
77 {
78 uint32_t probabilitySum = 0;
79 VkDeviceSize sizeSum = 0;
80 for(size_t i = 0; i < AllocationSizes.size(); ++i)
81 {
82 const AllocationSize& allocSize = AllocationSizes[i];
83 if(allocSize.BufferSizeMax > 0)
84 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
85 else
86 {
87 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
88 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
89 }
90 probabilitySum += allocSize.Probability;
91 }
92 return sizeSum / probabilitySum;
93 }
94
95 bool UsesBuffers() const
96 {
97 for(size_t i = 0; i < AllocationSizes.size(); ++i)
98 if(AllocationSizes[i].BufferSizeMax > 0)
99 return true;
100 return false;
101 }
102
103 bool UsesImages() const
104 {
105 for(size_t i = 0; i < AllocationSizes.size(); ++i)
106 if(AllocationSizes[i].ImageSizeMax > 0)
107 return true;
108 return false;
109 }
110};
111
112struct PoolTestResult
113{
114 duration TotalTime;
115 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
116 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
117 size_t LostAllocationCount, LostAllocationTotalSize;
118 size_t FailedAllocationCount, FailedAllocationTotalSize;
119};
120
121static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
122
Adam Sawicki8cfe05f2018-08-22 16:48:17 +0200123static uint32_t g_FrameIndex = 0;
124
Adam Sawickib8333fb2018-03-13 16:15:53 +0100125struct BufferInfo
126{
127 VkBuffer Buffer = VK_NULL_HANDLE;
128 VmaAllocation Allocation = VK_NULL_HANDLE;
129};
130
131static void InitResult(Result& outResult)
132{
133 outResult.TotalTime = duration::zero();
134 outResult.AllocationTimeMin = duration::max();
135 outResult.AllocationTimeAvg = duration::zero();
136 outResult.AllocationTimeMax = duration::min();
137 outResult.DeallocationTimeMin = duration::max();
138 outResult.DeallocationTimeAvg = duration::zero();
139 outResult.DeallocationTimeMax = duration::min();
140 outResult.TotalMemoryAllocated = 0;
141 outResult.FreeRangeSizeAvg = 0;
142 outResult.FreeRangeSizeMax = 0;
143}
144
145class TimeRegisterObj
146{
147public:
148 TimeRegisterObj(duration& min, duration& sum, duration& max) :
149 m_Min(min),
150 m_Sum(sum),
151 m_Max(max),
152 m_TimeBeg(std::chrono::high_resolution_clock::now())
153 {
154 }
155
156 ~TimeRegisterObj()
157 {
158 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
159 m_Sum += d;
160 if(d < m_Min) m_Min = d;
161 if(d > m_Max) m_Max = d;
162 }
163
164private:
165 duration& m_Min;
166 duration& m_Sum;
167 duration& m_Max;
168 time_point m_TimeBeg;
169};
170
171struct PoolTestThreadResult
172{
173 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
174 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
175 size_t AllocationCount, DeallocationCount;
176 size_t LostAllocationCount, LostAllocationTotalSize;
177 size_t FailedAllocationCount, FailedAllocationTotalSize;
178};
179
180class AllocationTimeRegisterObj : public TimeRegisterObj
181{
182public:
183 AllocationTimeRegisterObj(Result& result) :
184 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
185 {
186 }
187};
188
189class DeallocationTimeRegisterObj : public TimeRegisterObj
190{
191public:
192 DeallocationTimeRegisterObj(Result& result) :
193 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
194 {
195 }
196};
197
198class PoolAllocationTimeRegisterObj : public TimeRegisterObj
199{
200public:
201 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
202 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
203 {
204 }
205};
206
207class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
208{
209public:
210 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
211 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
212 {
213 }
214};
215
216VkResult MainTest(Result& outResult, const Config& config)
217{
218 assert(config.ThreadCount > 0);
219
220 InitResult(outResult);
221
222 RandomNumberGenerator mainRand{config.RandSeed};
223
224 time_point timeBeg = std::chrono::high_resolution_clock::now();
225
226 std::atomic<size_t> allocationCount = 0;
227 VkResult res = VK_SUCCESS;
228
229 uint32_t memUsageProbabilitySum =
230 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
231 config.MemUsageProbability[2] + config.MemUsageProbability[3];
232 assert(memUsageProbabilitySum > 0);
233
234 uint32_t allocationSizeProbabilitySum = std::accumulate(
235 config.AllocationSizes.begin(),
236 config.AllocationSizes.end(),
237 0u,
238 [](uint32_t sum, const AllocationSize& allocSize) {
239 return sum + allocSize.Probability;
240 });
241
242 struct Allocation
243 {
244 VkBuffer Buffer;
245 VkImage Image;
246 VmaAllocation Alloc;
247 };
248
249 std::vector<Allocation> commonAllocations;
250 std::mutex commonAllocationsMutex;
251
252 auto Allocate = [&](
253 VkDeviceSize bufferSize,
254 const VkExtent2D imageExtent,
255 RandomNumberGenerator& localRand,
256 VkDeviceSize& totalAllocatedBytes,
257 std::vector<Allocation>& allocations) -> VkResult
258 {
259 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
260
261 uint32_t memUsageIndex = 0;
262 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
263 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
264 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
265
266 VmaAllocationCreateInfo memReq = {};
267 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
Adam Sawicki0667e332018-08-24 17:26:44 +0200268 memReq.flags |= config.AllocationStrategy;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100269
270 Allocation allocation = {};
271 VmaAllocationInfo allocationInfo;
272
273 // Buffer
274 if(bufferSize > 0)
275 {
276 assert(imageExtent.width == 0);
277 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
278 bufferInfo.size = bufferSize;
279 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
280
281 {
282 AllocationTimeRegisterObj timeRegisterObj{outResult};
283 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
284 }
285 }
286 // Image
287 else
288 {
289 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
290 imageInfo.imageType = VK_IMAGE_TYPE_2D;
291 imageInfo.extent.width = imageExtent.width;
292 imageInfo.extent.height = imageExtent.height;
293 imageInfo.extent.depth = 1;
294 imageInfo.mipLevels = 1;
295 imageInfo.arrayLayers = 1;
296 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
297 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
298 VK_IMAGE_TILING_OPTIMAL :
299 VK_IMAGE_TILING_LINEAR;
300 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
301 switch(memReq.usage)
302 {
303 case VMA_MEMORY_USAGE_GPU_ONLY:
304 switch(localRand.Generate() % 3)
305 {
306 case 0:
307 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
308 break;
309 case 1:
310 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
311 break;
312 case 2:
313 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
314 break;
315 }
316 break;
317 case VMA_MEMORY_USAGE_CPU_ONLY:
318 case VMA_MEMORY_USAGE_CPU_TO_GPU:
319 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
320 break;
321 case VMA_MEMORY_USAGE_GPU_TO_CPU:
322 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
323 break;
324 }
325 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
326 imageInfo.flags = 0;
327
328 {
329 AllocationTimeRegisterObj timeRegisterObj{outResult};
330 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
331 }
332 }
333
334 if(res == VK_SUCCESS)
335 {
336 ++allocationCount;
337 totalAllocatedBytes += allocationInfo.size;
338 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
339 if(useCommonAllocations)
340 {
341 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
342 commonAllocations.push_back(allocation);
343 }
344 else
345 allocations.push_back(allocation);
346 }
347 else
348 {
349 assert(0);
350 }
351 return res;
352 };
353
354 auto GetNextAllocationSize = [&](
355 VkDeviceSize& outBufSize,
356 VkExtent2D& outImageSize,
357 RandomNumberGenerator& localRand)
358 {
359 outBufSize = 0;
360 outImageSize = {0, 0};
361
362 uint32_t allocSizeIndex = 0;
363 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
364 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
365 r -= config.AllocationSizes[allocSizeIndex++].Probability;
366
367 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
368 if(allocSize.BufferSizeMax > 0)
369 {
370 assert(allocSize.ImageSizeMax == 0);
371 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
372 outBufSize = allocSize.BufferSizeMin;
373 else
374 {
375 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
376 outBufSize = outBufSize / 16 * 16;
377 }
378 }
379 else
380 {
381 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
382 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
383 else
384 {
385 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
386 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
387 }
388 }
389 };
390
391 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
392 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
393
394 auto ThreadProc = [&](uint32_t randSeed) -> void
395 {
396 RandomNumberGenerator threadRand(randSeed);
397 VkDeviceSize threadTotalAllocatedBytes = 0;
398 std::vector<Allocation> threadAllocations;
399 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
400 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
401 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
402
403 // BEGIN ALLOCATIONS
404 for(;;)
405 {
406 VkDeviceSize bufferSize = 0;
407 VkExtent2D imageExtent = {};
408 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
409 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
410 threadBeginBytesToAllocate)
411 {
412 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
413 break;
414 }
415 else
416 break;
417 }
418
419 // ADDITIONAL ALLOCATIONS AND FREES
420 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
421 {
422 VkDeviceSize bufferSize = 0;
423 VkExtent2D imageExtent = {};
424 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
425
426 // true = allocate, false = free
427 bool allocate = threadRand.Generate() % 2 != 0;
428
429 if(allocate)
430 {
431 if(threadTotalAllocatedBytes +
432 bufferSize +
433 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
434 threadMaxBytesToAllocate)
435 {
436 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
437 break;
438 }
439 }
440 else
441 {
442 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
443 if(useCommonAllocations)
444 {
445 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
446 if(!commonAllocations.empty())
447 {
448 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
449 VmaAllocationInfo allocationInfo;
450 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
451 if(threadTotalAllocatedBytes >= allocationInfo.size)
452 {
453 DeallocationTimeRegisterObj timeRegisterObj{outResult};
454 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
455 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
456 else
457 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
458 threadTotalAllocatedBytes -= allocationInfo.size;
459 commonAllocations.erase(commonAllocations.begin() + indexToFree);
460 }
461 }
462 }
463 else
464 {
465 if(!threadAllocations.empty())
466 {
467 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
468 VmaAllocationInfo allocationInfo;
469 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
470 if(threadTotalAllocatedBytes >= allocationInfo.size)
471 {
472 DeallocationTimeRegisterObj timeRegisterObj{outResult};
473 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
474 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
475 else
476 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
477 threadTotalAllocatedBytes -= allocationInfo.size;
478 threadAllocations.erase(threadAllocations.begin() + indexToFree);
479 }
480 }
481 }
482 }
483 }
484
485 ++numThreadsReachedMaxAllocations;
486
487 WaitForSingleObject(threadsFinishEvent, INFINITE);
488
489 // DEALLOCATION
490 while(!threadAllocations.empty())
491 {
492 size_t indexToFree = 0;
493 switch(config.FreeOrder)
494 {
495 case FREE_ORDER::FORWARD:
496 indexToFree = 0;
497 break;
498 case FREE_ORDER::BACKWARD:
499 indexToFree = threadAllocations.size() - 1;
500 break;
501 case FREE_ORDER::RANDOM:
502 indexToFree = mainRand.Generate() % threadAllocations.size();
503 break;
504 }
505
506 {
507 DeallocationTimeRegisterObj timeRegisterObj{outResult};
508 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
509 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
510 else
511 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
512 }
513 threadAllocations.erase(threadAllocations.begin() + indexToFree);
514 }
515 };
516
517 uint32_t threadRandSeed = mainRand.Generate();
518 std::vector<std::thread> bkgThreads;
519 for(size_t i = 0; i < config.ThreadCount; ++i)
520 {
521 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
522 }
523
524 // Wait for threads reached max allocations
525 while(numThreadsReachedMaxAllocations < config.ThreadCount)
526 Sleep(0);
527
528 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
529 VmaStats vmaStats = {};
530 vmaCalculateStats(g_hAllocator, &vmaStats);
531 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
532 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
533 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
534
535 // Signal threads to deallocate
536 SetEvent(threadsFinishEvent);
537
538 // Wait for threads finished
539 for(size_t i = 0; i < bkgThreads.size(); ++i)
540 bkgThreads[i].join();
541 bkgThreads.clear();
542
543 CloseHandle(threadsFinishEvent);
544
545 // Deallocate remaining common resources
546 while(!commonAllocations.empty())
547 {
548 size_t indexToFree = 0;
549 switch(config.FreeOrder)
550 {
551 case FREE_ORDER::FORWARD:
552 indexToFree = 0;
553 break;
554 case FREE_ORDER::BACKWARD:
555 indexToFree = commonAllocations.size() - 1;
556 break;
557 case FREE_ORDER::RANDOM:
558 indexToFree = mainRand.Generate() % commonAllocations.size();
559 break;
560 }
561
562 {
563 DeallocationTimeRegisterObj timeRegisterObj{outResult};
564 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
565 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
566 else
567 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
568 }
569 commonAllocations.erase(commonAllocations.begin() + indexToFree);
570 }
571
572 if(allocationCount)
573 {
574 outResult.AllocationTimeAvg /= allocationCount;
575 outResult.DeallocationTimeAvg /= allocationCount;
576 }
577
578 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
579
580 return res;
581}
582
Adam Sawickie44c6262018-06-15 14:30:39 +0200583static void SaveAllocatorStatsToFile(const wchar_t* filePath)
Adam Sawickib8333fb2018-03-13 16:15:53 +0100584{
585 char* stats;
Adam Sawickie44c6262018-06-15 14:30:39 +0200586 vmaBuildStatsString(g_hAllocator, &stats, VK_TRUE);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100587 SaveFile(filePath, stats, strlen(stats));
Adam Sawickie44c6262018-06-15 14:30:39 +0200588 vmaFreeStatsString(g_hAllocator, stats);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100589}
590
591struct AllocInfo
592{
593 VmaAllocation m_Allocation;
594 VkBuffer m_Buffer;
595 VkImage m_Image;
596 uint32_t m_StartValue;
597 union
598 {
599 VkBufferCreateInfo m_BufferInfo;
600 VkImageCreateInfo m_ImageInfo;
601 };
602};
603
604static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
605{
606 outMemReq = {};
607 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
608 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
609}
610
611static void CreateBuffer(
612 VmaPool pool,
613 const VkBufferCreateInfo& bufCreateInfo,
614 bool persistentlyMapped,
615 AllocInfo& outAllocInfo)
616{
617 outAllocInfo = {};
618 outAllocInfo.m_BufferInfo = bufCreateInfo;
619
620 VmaAllocationCreateInfo allocCreateInfo = {};
621 allocCreateInfo.pool = pool;
622 if(persistentlyMapped)
623 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
624
625 VmaAllocationInfo vmaAllocInfo = {};
626 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
627
628 // Setup StartValue and fill.
629 {
630 outAllocInfo.m_StartValue = (uint32_t)rand();
631 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
632 assert((data != nullptr) == persistentlyMapped);
633 if(!persistentlyMapped)
634 {
635 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
636 }
637
638 uint32_t value = outAllocInfo.m_StartValue;
639 assert(bufCreateInfo.size % 4 == 0);
640 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
641 data[i] = value++;
642
643 if(!persistentlyMapped)
644 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
645 }
646}
647
648static void CreateAllocation(AllocInfo& outAllocation, VmaAllocator allocator)
649{
650 outAllocation.m_Allocation = nullptr;
651 outAllocation.m_Buffer = nullptr;
652 outAllocation.m_Image = nullptr;
653 outAllocation.m_StartValue = (uint32_t)rand();
654
655 VmaAllocationCreateInfo vmaMemReq;
656 GetMemReq(vmaMemReq);
657
658 VmaAllocationInfo allocInfo;
659
660 const bool isBuffer = true;//(rand() & 0x1) != 0;
661 const bool isLarge = (rand() % 16) == 0;
662 if(isBuffer)
663 {
664 const uint32_t bufferSize = isLarge ?
665 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
666 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
667
668 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
669 bufferInfo.size = bufferSize;
670 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
671
672 VkResult res = vmaCreateBuffer(allocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
673 outAllocation.m_BufferInfo = bufferInfo;
674 assert(res == VK_SUCCESS);
675 }
676 else
677 {
678 const uint32_t imageSizeX = isLarge ?
679 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
680 rand() % 1024 + 1; // 1 ... 1024
681 const uint32_t imageSizeY = isLarge ?
682 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
683 rand() % 1024 + 1; // 1 ... 1024
684
685 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
686 imageInfo.imageType = VK_IMAGE_TYPE_2D;
687 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
688 imageInfo.extent.width = imageSizeX;
689 imageInfo.extent.height = imageSizeY;
690 imageInfo.extent.depth = 1;
691 imageInfo.mipLevels = 1;
692 imageInfo.arrayLayers = 1;
693 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
694 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
695 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
696 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
697
698 VkResult res = vmaCreateImage(allocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
699 outAllocation.m_ImageInfo = imageInfo;
700 assert(res == VK_SUCCESS);
701 }
702
703 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
704 if(allocInfo.pMappedData == nullptr)
705 {
706 VkResult res = vmaMapMemory(allocator, outAllocation.m_Allocation, (void**)&data);
707 assert(res == VK_SUCCESS);
708 }
709
710 uint32_t value = outAllocation.m_StartValue;
711 assert(allocInfo.size % 4 == 0);
712 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
713 data[i] = value++;
714
715 if(allocInfo.pMappedData == nullptr)
716 vmaUnmapMemory(allocator, outAllocation.m_Allocation);
717}
718
719static void DestroyAllocation(const AllocInfo& allocation)
720{
721 if(allocation.m_Buffer)
722 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
723 else
724 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
725}
726
727static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
728{
729 for(size_t i = allocations.size(); i--; )
730 DestroyAllocation(allocations[i]);
731 allocations.clear();
732}
733
734static void ValidateAllocationData(const AllocInfo& allocation)
735{
736 VmaAllocationInfo allocInfo;
737 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
738
739 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
740 if(allocInfo.pMappedData == nullptr)
741 {
742 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
743 assert(res == VK_SUCCESS);
744 }
745
746 uint32_t value = allocation.m_StartValue;
747 bool ok = true;
748 size_t i;
749 assert(allocInfo.size % 4 == 0);
750 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
751 {
752 if(data[i] != value++)
753 {
754 ok = false;
755 break;
756 }
757 }
758 assert(ok);
759
760 if(allocInfo.pMappedData == nullptr)
761 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
762}
763
764static void RecreateAllocationResource(AllocInfo& allocation)
765{
766 VmaAllocationInfo allocInfo;
767 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
768
769 if(allocation.m_Buffer)
770 {
771 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, nullptr);
772
773 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, nullptr, &allocation.m_Buffer);
774 assert(res == VK_SUCCESS);
775
776 // Just to silence validation layer warnings.
777 VkMemoryRequirements vkMemReq;
778 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
779 assert(vkMemReq.size == allocation.m_BufferInfo.size);
780
781 res = vkBindBufferMemory(g_hDevice, allocation.m_Buffer, allocInfo.deviceMemory, allocInfo.offset);
782 assert(res == VK_SUCCESS);
783 }
784 else
785 {
786 vkDestroyImage(g_hDevice, allocation.m_Image, nullptr);
787
788 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, nullptr, &allocation.m_Image);
789 assert(res == VK_SUCCESS);
790
791 // Just to silence validation layer warnings.
792 VkMemoryRequirements vkMemReq;
793 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
794
795 res = vkBindImageMemory(g_hDevice, allocation.m_Image, allocInfo.deviceMemory, allocInfo.offset);
796 assert(res == VK_SUCCESS);
797 }
798}
799
800static void Defragment(AllocInfo* allocs, size_t allocCount,
801 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
802 VmaDefragmentationStats* defragmentationStats = nullptr)
803{
804 std::vector<VmaAllocation> vmaAllocs(allocCount);
805 for(size_t i = 0; i < allocCount; ++i)
806 vmaAllocs[i] = allocs[i].m_Allocation;
807
808 std::vector<VkBool32> allocChanged(allocCount);
809
810 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
811 defragmentationInfo, defragmentationStats) );
812
813 for(size_t i = 0; i < allocCount; ++i)
814 {
815 if(allocChanged[i])
816 {
817 RecreateAllocationResource(allocs[i]);
818 }
819 }
820}
821
822static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
823{
824 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
825 ValidateAllocationData(allocInfo);
826 });
827}
828
829void TestDefragmentationSimple()
830{
831 wprintf(L"Test defragmentation simple\n");
832
833 RandomNumberGenerator rand(667);
834
835 const VkDeviceSize BUF_SIZE = 0x10000;
836 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
837
838 const VkDeviceSize MIN_BUF_SIZE = 32;
839 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
840 auto RandomBufSize = [&]() -> VkDeviceSize {
841 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
842 };
843
844 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
845 bufCreateInfo.size = BUF_SIZE;
846 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
847
848 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
849 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
850
851 uint32_t memTypeIndex = UINT32_MAX;
852 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
853
854 VmaPoolCreateInfo poolCreateInfo = {};
855 poolCreateInfo.blockSize = BLOCK_SIZE;
856 poolCreateInfo.memoryTypeIndex = memTypeIndex;
857
858 VmaPool pool;
859 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
860
861 std::vector<AllocInfo> allocations;
862
863 // persistentlyMappedOption = 0 - not persistently mapped.
864 // persistentlyMappedOption = 1 - persistently mapped.
865 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
866 {
867 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
868 const bool persistentlyMapped = persistentlyMappedOption != 0;
869
870 // # Test 1
871 // Buffers of fixed size.
872 // Fill 2 blocks. Remove odd buffers. Defragment everything.
873 // Expected result: at least 1 block freed.
874 {
875 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
876 {
877 AllocInfo allocInfo;
878 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
879 allocations.push_back(allocInfo);
880 }
881
882 for(size_t i = 1; i < allocations.size(); ++i)
883 {
884 DestroyAllocation(allocations[i]);
885 allocations.erase(allocations.begin() + i);
886 }
887
888 VmaDefragmentationStats defragStats;
889 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
890 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
891 assert(defragStats.deviceMemoryBlocksFreed >= 1);
892
893 ValidateAllocationsData(allocations.data(), allocations.size());
894
895 DestroyAllAllocations(allocations);
896 }
897
898 // # Test 2
899 // Buffers of fixed size.
900 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
901 // Expected result: Each of 4 interations makes some progress.
902 {
903 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
904 {
905 AllocInfo allocInfo;
906 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
907 allocations.push_back(allocInfo);
908 }
909
910 for(size_t i = 1; i < allocations.size(); ++i)
911 {
912 DestroyAllocation(allocations[i]);
913 allocations.erase(allocations.begin() + i);
914 }
915
916 VmaDefragmentationInfo defragInfo = {};
917 defragInfo.maxAllocationsToMove = 1;
918 defragInfo.maxBytesToMove = BUF_SIZE;
919
920 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
921 {
922 VmaDefragmentationStats defragStats;
923 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
924 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
925 }
926
927 ValidateAllocationsData(allocations.data(), allocations.size());
928
929 DestroyAllAllocations(allocations);
930 }
931
932 // # Test 3
933 // Buffers of variable size.
934 // Create a number of buffers. Remove some percent of them.
935 // Defragment while having some percent of them unmovable.
936 // Expected result: Just simple validation.
937 {
938 for(size_t i = 0; i < 100; ++i)
939 {
940 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
941 localBufCreateInfo.size = RandomBufSize();
942
943 AllocInfo allocInfo;
944 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
945 allocations.push_back(allocInfo);
946 }
947
948 const uint32_t percentToDelete = 60;
949 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
950 for(size_t i = 0; i < numberToDelete; ++i)
951 {
952 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
953 DestroyAllocation(allocations[indexToDelete]);
954 allocations.erase(allocations.begin() + indexToDelete);
955 }
956
957 // Non-movable allocations will be at the beginning of allocations array.
958 const uint32_t percentNonMovable = 20;
959 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
960 for(size_t i = 0; i < numberNonMovable; ++i)
961 {
962 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
963 if(indexNonMovable != i)
964 std::swap(allocations[i], allocations[indexNonMovable]);
965 }
966
967 VmaDefragmentationStats defragStats;
968 Defragment(
969 allocations.data() + numberNonMovable,
970 allocations.size() - numberNonMovable,
971 nullptr, &defragStats);
972
973 ValidateAllocationsData(allocations.data(), allocations.size());
974
975 DestroyAllAllocations(allocations);
976 }
977 }
978
979 vmaDestroyPool(g_hAllocator, pool);
980}
981
982void TestDefragmentationFull()
983{
984 std::vector<AllocInfo> allocations;
985
986 // Create initial allocations.
987 for(size_t i = 0; i < 400; ++i)
988 {
989 AllocInfo allocation;
990 CreateAllocation(allocation, g_hAllocator);
991 allocations.push_back(allocation);
992 }
993
994 // Delete random allocations
995 const size_t allocationsToDeletePercent = 80;
996 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
997 for(size_t i = 0; i < allocationsToDelete; ++i)
998 {
999 size_t index = (size_t)rand() % allocations.size();
1000 DestroyAllocation(allocations[index]);
1001 allocations.erase(allocations.begin() + index);
1002 }
1003
1004 for(size_t i = 0; i < allocations.size(); ++i)
1005 ValidateAllocationData(allocations[i]);
1006
Adam Sawicki0667e332018-08-24 17:26:44 +02001007 //SaveAllocatorStatsToFile(L"Before.csv");
Adam Sawickib8333fb2018-03-13 16:15:53 +01001008
1009 {
1010 std::vector<VmaAllocation> vmaAllocations(allocations.size());
1011 for(size_t i = 0; i < allocations.size(); ++i)
1012 vmaAllocations[i] = allocations[i].m_Allocation;
1013
1014 const size_t nonMovablePercent = 0;
1015 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
1016 for(size_t i = 0; i < nonMovableCount; ++i)
1017 {
1018 size_t index = (size_t)rand() % vmaAllocations.size();
1019 vmaAllocations.erase(vmaAllocations.begin() + index);
1020 }
1021
1022 const uint32_t defragCount = 1;
1023 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1024 {
1025 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1026
1027 VmaDefragmentationInfo defragmentationInfo;
1028 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1029 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1030
1031 wprintf(L"Defragmentation #%u\n", defragIndex);
1032
1033 time_point begTime = std::chrono::high_resolution_clock::now();
1034
1035 VmaDefragmentationStats stats;
1036 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
1037 assert(res >= 0);
1038
1039 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1040
1041 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1042 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1043 wprintf(L"Time: %.2f s\n", defragmentDuration);
1044
1045 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1046 {
1047 if(allocationsChanged[i])
1048 {
1049 RecreateAllocationResource(allocations[i]);
1050 }
1051 }
1052
1053 for(size_t i = 0; i < allocations.size(); ++i)
1054 ValidateAllocationData(allocations[i]);
1055
Adam Sawicki0667e332018-08-24 17:26:44 +02001056 //wchar_t fileName[MAX_PATH];
1057 //swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
1058 //SaveAllocatorStatsToFile(fileName);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001059 }
1060 }
1061
1062 // Destroy all remaining allocations.
1063 DestroyAllAllocations(allocations);
1064}
1065
1066static void TestUserData()
1067{
1068 VkResult res;
1069
1070 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1071 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1072 bufCreateInfo.size = 0x10000;
1073
1074 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
1075 {
1076 // Opaque pointer
1077 {
1078
1079 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
1080 void* pointerToSomething = &res;
1081
1082 VmaAllocationCreateInfo allocCreateInfo = {};
1083 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1084 allocCreateInfo.pUserData = numberAsPointer;
1085 if(testIndex == 1)
1086 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1087
1088 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1089 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1090 assert(res == VK_SUCCESS);
1091 assert(allocInfo.pUserData = numberAsPointer);
1092
1093 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1094 assert(allocInfo.pUserData == numberAsPointer);
1095
1096 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
1097 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1098 assert(allocInfo.pUserData == pointerToSomething);
1099
1100 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1101 }
1102
1103 // String
1104 {
1105 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
1106 const char* name2 = "2";
1107 const size_t name1Len = strlen(name1);
1108
1109 char* name1Buf = new char[name1Len + 1];
1110 strcpy_s(name1Buf, name1Len + 1, name1);
1111
1112 VmaAllocationCreateInfo allocCreateInfo = {};
1113 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1114 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1115 allocCreateInfo.pUserData = name1Buf;
1116 if(testIndex == 1)
1117 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1118
1119 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1120 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1121 assert(res == VK_SUCCESS);
1122 assert(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
1123 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1124
1125 delete[] name1Buf;
1126
1127 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1128 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1129
1130 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
1131 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1132 assert(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
1133
1134 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
1135 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1136 assert(allocInfo.pUserData == nullptr);
1137
1138 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1139 }
1140 }
1141}
1142
1143static void TestMemoryRequirements()
1144{
1145 VkResult res;
1146 VkBuffer buf;
1147 VmaAllocation alloc;
1148 VmaAllocationInfo allocInfo;
1149
1150 const VkPhysicalDeviceMemoryProperties* memProps;
1151 vmaGetMemoryProperties(g_hAllocator, &memProps);
1152
1153 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1154 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1155 bufInfo.size = 128;
1156
1157 VmaAllocationCreateInfo allocCreateInfo = {};
1158
1159 // No requirements.
1160 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1161 assert(res == VK_SUCCESS);
1162 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1163
1164 // Usage.
1165 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1166 allocCreateInfo.requiredFlags = 0;
1167 allocCreateInfo.preferredFlags = 0;
1168 allocCreateInfo.memoryTypeBits = UINT32_MAX;
1169
1170 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1171 assert(res == VK_SUCCESS);
1172 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1173 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1174
1175 // Required flags, preferred flags.
1176 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
1177 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1178 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1179 allocCreateInfo.memoryTypeBits = 0;
1180
1181 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1182 assert(res == VK_SUCCESS);
1183 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1184 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
1185 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1186
1187 // memoryTypeBits.
1188 const uint32_t memType = allocInfo.memoryType;
1189 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1190 allocCreateInfo.requiredFlags = 0;
1191 allocCreateInfo.preferredFlags = 0;
1192 allocCreateInfo.memoryTypeBits = 1u << memType;
1193
1194 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1195 assert(res == VK_SUCCESS);
1196 assert(allocInfo.memoryType == memType);
1197 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1198
1199}
1200
1201static void TestBasics()
1202{
1203 VkResult res;
1204
1205 TestMemoryRequirements();
1206
1207 // Lost allocation
1208 {
1209 VmaAllocation alloc = VK_NULL_HANDLE;
1210 vmaCreateLostAllocation(g_hAllocator, &alloc);
1211 assert(alloc != VK_NULL_HANDLE);
1212
1213 VmaAllocationInfo allocInfo;
1214 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1215 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1216 assert(allocInfo.size == 0);
1217
1218 vmaFreeMemory(g_hAllocator, alloc);
1219 }
1220
1221 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
1222 {
1223 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1224 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1225 bufCreateInfo.size = 128;
1226
1227 VmaAllocationCreateInfo allocCreateInfo = {};
1228 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1229 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1230
1231 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1232 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1233 assert(res == VK_SUCCESS);
1234
1235 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1236
1237 // Same with OWN_MEMORY.
1238 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1239
1240 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1241 assert(res == VK_SUCCESS);
1242
1243 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1244 }
1245
1246 TestUserData();
1247}
1248
1249void TestHeapSizeLimit()
1250{
1251 const VkDeviceSize HEAP_SIZE_LIMIT = 1ull * 1024 * 1024 * 1024; // 1 GB
1252 const VkDeviceSize BLOCK_SIZE = 128ull * 1024 * 1024; // 128 MB
1253
1254 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
1255 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
1256 {
1257 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
1258 }
1259
1260 VmaAllocatorCreateInfo allocatorCreateInfo = {};
1261 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
1262 allocatorCreateInfo.device = g_hDevice;
1263 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
1264
1265 VmaAllocator hAllocator;
1266 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
1267 assert(res == VK_SUCCESS);
1268
1269 struct Item
1270 {
1271 VkBuffer hBuf;
1272 VmaAllocation hAlloc;
1273 };
1274 std::vector<Item> items;
1275
1276 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1277 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1278
1279 // 1. Allocate two blocks of Own Memory, half the size of BLOCK_SIZE.
1280 VmaAllocationInfo ownAllocInfo;
1281 {
1282 VmaAllocationCreateInfo allocCreateInfo = {};
1283 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1284 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1285
1286 bufCreateInfo.size = BLOCK_SIZE / 2;
1287
1288 for(size_t i = 0; i < 2; ++i)
1289 {
1290 Item item;
1291 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &ownAllocInfo);
1292 assert(res == VK_SUCCESS);
1293 items.push_back(item);
1294 }
1295 }
1296
1297 // Create pool to make sure allocations must be out of this memory type.
1298 VmaPoolCreateInfo poolCreateInfo = {};
1299 poolCreateInfo.memoryTypeIndex = ownAllocInfo.memoryType;
1300 poolCreateInfo.blockSize = BLOCK_SIZE;
1301
1302 VmaPool hPool;
1303 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
1304 assert(res == VK_SUCCESS);
1305
1306 // 2. Allocate normal buffers from all the remaining memory.
1307 {
1308 VmaAllocationCreateInfo allocCreateInfo = {};
1309 allocCreateInfo.pool = hPool;
1310
1311 bufCreateInfo.size = BLOCK_SIZE / 2;
1312
1313 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
1314 for(size_t i = 0; i < bufCount; ++i)
1315 {
1316 Item item;
1317 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
1318 assert(res == VK_SUCCESS);
1319 items.push_back(item);
1320 }
1321 }
1322
1323 // 3. Allocation of one more (even small) buffer should fail.
1324 {
1325 VmaAllocationCreateInfo allocCreateInfo = {};
1326 allocCreateInfo.pool = hPool;
1327
1328 bufCreateInfo.size = 128;
1329
1330 VkBuffer hBuf;
1331 VmaAllocation hAlloc;
1332 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
1333 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1334 }
1335
1336 // Destroy everything.
1337 for(size_t i = items.size(); i--; )
1338 {
1339 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
1340 }
1341
1342 vmaDestroyPool(hAllocator, hPool);
1343
1344 vmaDestroyAllocator(hAllocator);
1345}
1346
Adam Sawicki212a4a62018-06-14 15:44:45 +02001347#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02001348static void TestDebugMargin()
1349{
1350 if(VMA_DEBUG_MARGIN == 0)
1351 {
1352 return;
1353 }
1354
1355 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02001356 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02001357
1358 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02001359 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02001360
1361 // Create few buffers of different size.
1362 const size_t BUF_COUNT = 10;
1363 BufferInfo buffers[BUF_COUNT];
1364 VmaAllocationInfo allocInfo[BUF_COUNT];
1365 for(size_t i = 0; i < 10; ++i)
1366 {
1367 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02001368 // Last one will be mapped.
1369 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02001370
1371 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
1372 assert(res == VK_SUCCESS);
1373 // Margin is preserved also at the beginning of a block.
1374 assert(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02001375
1376 if(i == BUF_COUNT - 1)
1377 {
1378 // Fill with data.
1379 assert(allocInfo[i].pMappedData != nullptr);
1380 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
1381 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
1382 }
Adam Sawicki73b16652018-06-11 16:39:25 +02001383 }
1384
1385 // Check if their offsets preserve margin between them.
1386 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
1387 {
1388 if(lhs.deviceMemory != rhs.deviceMemory)
1389 {
1390 return lhs.deviceMemory < rhs.deviceMemory;
1391 }
1392 return lhs.offset < rhs.offset;
1393 });
1394 for(size_t i = 1; i < BUF_COUNT; ++i)
1395 {
1396 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
1397 {
1398 assert(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
1399 }
1400 }
1401
Adam Sawicki212a4a62018-06-14 15:44:45 +02001402 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
1403 assert(res == VK_SUCCESS);
1404
Adam Sawicki73b16652018-06-11 16:39:25 +02001405 // Destroy all buffers.
1406 for(size_t i = BUF_COUNT; i--; )
1407 {
1408 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
1409 }
1410}
Adam Sawicki212a4a62018-06-14 15:44:45 +02001411#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02001412
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001413static void TestLinearAllocator()
1414{
1415 wprintf(L"Test linear allocator\n");
1416
1417 RandomNumberGenerator rand{645332};
1418
1419 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1420 sampleBufCreateInfo.size = 1024; // Whatever.
1421 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1422
1423 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1424 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1425
1426 VmaPoolCreateInfo poolCreateInfo = {};
1427 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1428 assert(res == VK_SUCCESS);
1429
Adam Sawickiee082772018-06-20 17:45:49 +02001430 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001431 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1432 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
1433
1434 VmaPool pool = nullptr;
1435 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1436 assert(res == VK_SUCCESS);
1437
1438 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1439
1440 VmaAllocationCreateInfo allocCreateInfo = {};
1441 allocCreateInfo.pool = pool;
1442
1443 constexpr size_t maxBufCount = 100;
1444 std::vector<BufferInfo> bufInfo;
1445
1446 constexpr VkDeviceSize bufSizeMin = 16;
1447 constexpr VkDeviceSize bufSizeMax = 1024;
1448 VmaAllocationInfo allocInfo;
1449 VkDeviceSize prevOffset = 0;
1450
1451 // Test one-time free.
1452 for(size_t i = 0; i < 2; ++i)
1453 {
1454 // Allocate number of buffers of varying size that surely fit into this block.
1455 VkDeviceSize bufSumSize = 0;
1456 for(size_t i = 0; i < maxBufCount; ++i)
1457 {
1458 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1459 BufferInfo newBufInfo;
1460 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1461 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1462 assert(res == VK_SUCCESS);
1463 assert(i == 0 || allocInfo.offset > prevOffset);
1464 bufInfo.push_back(newBufInfo);
1465 prevOffset = allocInfo.offset;
1466 bufSumSize += bufCreateInfo.size;
1467 }
1468
1469 // Validate pool stats.
1470 VmaPoolStats stats;
1471 vmaGetPoolStats(g_hAllocator, pool, &stats);
1472 assert(stats.size == poolCreateInfo.blockSize);
1473 assert(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
1474 assert(stats.allocationCount == bufInfo.size());
1475
1476 // Destroy the buffers in random order.
1477 while(!bufInfo.empty())
1478 {
1479 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1480 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1481 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1482 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1483 }
1484 }
1485
1486 // Test stack.
1487 {
1488 // Allocate number of buffers of varying size that surely fit into this block.
1489 for(size_t i = 0; i < maxBufCount; ++i)
1490 {
1491 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1492 BufferInfo newBufInfo;
1493 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1494 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1495 assert(res == VK_SUCCESS);
1496 assert(i == 0 || allocInfo.offset > prevOffset);
1497 bufInfo.push_back(newBufInfo);
1498 prevOffset = allocInfo.offset;
1499 }
1500
1501 // Destroy few buffers from top of the stack.
1502 for(size_t i = 0; i < maxBufCount / 5; ++i)
1503 {
1504 const BufferInfo& currBufInfo = bufInfo.back();
1505 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1506 bufInfo.pop_back();
1507 }
1508
1509 // Create some more
1510 for(size_t i = 0; i < maxBufCount / 5; ++i)
1511 {
1512 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1513 BufferInfo newBufInfo;
1514 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1515 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1516 assert(res == VK_SUCCESS);
1517 assert(i == 0 || allocInfo.offset > prevOffset);
1518 bufInfo.push_back(newBufInfo);
1519 prevOffset = allocInfo.offset;
1520 }
1521
1522 // Destroy the buffers in reverse order.
1523 while(!bufInfo.empty())
1524 {
1525 const BufferInfo& currBufInfo = bufInfo.back();
1526 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1527 bufInfo.pop_back();
1528 }
1529 }
1530
Adam Sawickiee082772018-06-20 17:45:49 +02001531 // Test ring buffer.
1532 {
1533 // Allocate number of buffers that surely fit into this block.
1534 bufCreateInfo.size = bufSizeMax;
1535 for(size_t i = 0; i < maxBufCount; ++i)
1536 {
1537 BufferInfo newBufInfo;
1538 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1539 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1540 assert(res == VK_SUCCESS);
1541 assert(i == 0 || allocInfo.offset > prevOffset);
1542 bufInfo.push_back(newBufInfo);
1543 prevOffset = allocInfo.offset;
1544 }
1545
1546 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
1547 const size_t buffersPerIter = maxBufCount / 10 - 1;
1548 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
1549 for(size_t iter = 0; iter < iterCount; ++iter)
1550 {
1551 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1552 {
1553 const BufferInfo& currBufInfo = bufInfo.front();
1554 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1555 bufInfo.erase(bufInfo.begin());
1556 }
1557 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1558 {
1559 BufferInfo newBufInfo;
1560 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1561 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1562 assert(res == VK_SUCCESS);
1563 bufInfo.push_back(newBufInfo);
1564 }
1565 }
1566
1567 // Allocate buffers until we reach out-of-memory.
1568 uint32_t debugIndex = 0;
1569 while(res == VK_SUCCESS)
1570 {
1571 BufferInfo newBufInfo;
1572 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1573 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1574 if(res == VK_SUCCESS)
1575 {
1576 bufInfo.push_back(newBufInfo);
1577 }
1578 else
1579 {
1580 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1581 }
1582 ++debugIndex;
1583 }
1584
1585 // Destroy the buffers in random order.
1586 while(!bufInfo.empty())
1587 {
1588 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1589 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1590 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1591 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1592 }
1593 }
1594
Adam Sawicki680b2252018-08-22 14:47:32 +02001595 // Test double stack.
1596 {
1597 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
1598 VkDeviceSize prevOffsetLower = 0;
1599 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
1600 for(size_t i = 0; i < maxBufCount; ++i)
1601 {
1602 const bool upperAddress = (i % 2) != 0;
1603 if(upperAddress)
1604 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1605 else
1606 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1607 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1608 BufferInfo newBufInfo;
1609 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1610 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1611 assert(res == VK_SUCCESS);
1612 if(upperAddress)
1613 {
1614 assert(allocInfo.offset < prevOffsetUpper);
1615 prevOffsetUpper = allocInfo.offset;
1616 }
1617 else
1618 {
1619 assert(allocInfo.offset >= prevOffsetLower);
1620 prevOffsetLower = allocInfo.offset;
1621 }
1622 assert(prevOffsetLower < prevOffsetUpper);
1623 bufInfo.push_back(newBufInfo);
1624 }
1625
1626 // Destroy few buffers from top of the stack.
1627 for(size_t i = 0; i < maxBufCount / 5; ++i)
1628 {
1629 const BufferInfo& currBufInfo = bufInfo.back();
1630 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1631 bufInfo.pop_back();
1632 }
1633
1634 // Create some more
1635 for(size_t i = 0; i < maxBufCount / 5; ++i)
1636 {
1637 const bool upperAddress = (i % 2) != 0;
1638 if(upperAddress)
1639 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1640 else
1641 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1642 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1643 BufferInfo newBufInfo;
1644 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1645 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1646 assert(res == VK_SUCCESS);
1647 bufInfo.push_back(newBufInfo);
1648 }
1649
1650 // Destroy the buffers in reverse order.
1651 while(!bufInfo.empty())
1652 {
1653 const BufferInfo& currBufInfo = bufInfo.back();
1654 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1655 bufInfo.pop_back();
1656 }
1657
1658 // Create buffers on both sides until we reach out of memory.
1659 prevOffsetLower = 0;
1660 prevOffsetUpper = poolCreateInfo.blockSize;
1661 res = VK_SUCCESS;
1662 for(size_t i = 0; res == VK_SUCCESS; ++i)
1663 {
1664 const bool upperAddress = (i % 2) != 0;
1665 if(upperAddress)
1666 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1667 else
1668 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1669 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1670 BufferInfo newBufInfo;
1671 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1672 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1673 if(res == VK_SUCCESS)
1674 {
1675 if(upperAddress)
1676 {
1677 assert(allocInfo.offset < prevOffsetUpper);
1678 prevOffsetUpper = allocInfo.offset;
1679 }
1680 else
1681 {
1682 assert(allocInfo.offset >= prevOffsetLower);
1683 prevOffsetLower = allocInfo.offset;
1684 }
1685 assert(prevOffsetLower < prevOffsetUpper);
1686 bufInfo.push_back(newBufInfo);
1687 }
1688 }
1689
1690 // Destroy the buffers in random order.
1691 while(!bufInfo.empty())
1692 {
1693 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1694 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1695 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1696 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1697 }
1698
1699 // Create buffers on upper side only, constant size, until we reach out of memory.
1700 prevOffsetUpper = poolCreateInfo.blockSize;
1701 res = VK_SUCCESS;
1702 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1703 bufCreateInfo.size = bufSizeMax;
1704 for(size_t i = 0; res == VK_SUCCESS; ++i)
1705 {
1706 BufferInfo newBufInfo;
1707 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1708 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1709 if(res == VK_SUCCESS)
1710 {
1711 assert(allocInfo.offset < prevOffsetUpper);
1712 prevOffsetUpper = allocInfo.offset;
1713 bufInfo.push_back(newBufInfo);
1714 }
1715 }
1716
1717 // Destroy the buffers in reverse order.
1718 while(!bufInfo.empty())
1719 {
1720 const BufferInfo& currBufInfo = bufInfo.back();
1721 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1722 bufInfo.pop_back();
1723 }
1724 }
1725
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001726 // Test ring buffer with lost allocations.
1727 {
1728 // Allocate number of buffers until pool is full.
1729 // Notice CAN_BECOME_LOST flag and call to vmaSetCurrentFrameIndex.
1730 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT;
1731 res = VK_SUCCESS;
1732 for(size_t i = 0; res == VK_SUCCESS; ++i)
1733 {
1734 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1735
1736 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1737
1738 BufferInfo newBufInfo;
1739 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1740 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1741 if(res == VK_SUCCESS)
1742 bufInfo.push_back(newBufInfo);
1743 }
1744
1745 // Free first half of it.
1746 {
1747 const size_t buffersToDelete = bufInfo.size() / 2;
1748 for(size_t i = 0; i < buffersToDelete; ++i)
1749 {
1750 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1751 }
1752 bufInfo.erase(bufInfo.begin(), bufInfo.begin() + buffersToDelete);
1753 }
1754
1755 // Allocate number of buffers until pool is full again.
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001756 // This way we make sure ring buffers wraps around, front in in the middle.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001757 res = VK_SUCCESS;
1758 for(size_t i = 0; res == VK_SUCCESS; ++i)
1759 {
1760 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1761
1762 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1763
1764 BufferInfo newBufInfo;
1765 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1766 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1767 if(res == VK_SUCCESS)
1768 bufInfo.push_back(newBufInfo);
1769 }
1770
1771 VkDeviceSize firstNewOffset;
1772 {
1773 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1774
1775 // Allocate a large buffer with CAN_MAKE_OTHER_LOST.
1776 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1777 bufCreateInfo.size = bufSizeMax;
1778
1779 BufferInfo newBufInfo;
1780 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1781 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1782 assert(res == VK_SUCCESS);
1783 bufInfo.push_back(newBufInfo);
1784 firstNewOffset = allocInfo.offset;
1785
1786 // Make sure at least one buffer from the beginning became lost.
1787 vmaGetAllocationInfo(g_hAllocator, bufInfo[0].Allocation, &allocInfo);
1788 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1789 }
1790
1791 // Allocate more buffers that CAN_MAKE_OTHER_LOST until we wrap-around with this.
1792 size_t newCount = 1;
1793 for(;;)
1794 {
1795 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1796
1797 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1798
1799 BufferInfo newBufInfo;
1800 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1801 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1802 assert(res == VK_SUCCESS);
1803 bufInfo.push_back(newBufInfo);
1804 ++newCount;
1805 if(allocInfo.offset < firstNewOffset)
1806 break;
1807 }
1808
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001809 // Delete buffers that are lost.
1810 for(size_t i = bufInfo.size(); i--; )
1811 {
1812 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
1813 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1814 {
1815 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1816 bufInfo.erase(bufInfo.begin() + i);
1817 }
1818 }
1819
1820 // Test vmaMakePoolAllocationsLost
1821 {
1822 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1823
1824 size_t lostAllocCount = SIZE_MAX;
1825 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostAllocCount);
1826 assert(lostAllocCount > 0);
1827
1828 size_t realLostAllocCount = 0;
1829 for(size_t i = 0; i < bufInfo.size(); ++i)
1830 {
1831 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
1832 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1833 ++realLostAllocCount;
1834 }
1835 assert(realLostAllocCount == lostAllocCount);
1836 }
1837
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001838 // Destroy all the buffers in forward order.
1839 for(size_t i = 0; i < bufInfo.size(); ++i)
1840 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1841 bufInfo.clear();
1842 }
1843
Adam Sawicki70a683e2018-08-24 15:36:32 +02001844 vmaDestroyPool(g_hAllocator, pool);
1845}
Adam Sawickif799c4f2018-08-23 10:40:30 +02001846
Adam Sawicki70a683e2018-08-24 15:36:32 +02001847static void TestLinearAllocatorMultiBlock()
1848{
1849 wprintf(L"Test linear allocator multi block\n");
1850
1851 RandomNumberGenerator rand{345673};
1852
1853 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1854 sampleBufCreateInfo.size = 1024 * 1024;
1855 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1856
1857 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1858 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1859
1860 VmaPoolCreateInfo poolCreateInfo = {};
1861 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1862 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1863 assert(res == VK_SUCCESS);
1864
1865 VmaPool pool = nullptr;
1866 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1867 assert(res == VK_SUCCESS);
1868
1869 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1870
1871 VmaAllocationCreateInfo allocCreateInfo = {};
1872 allocCreateInfo.pool = pool;
1873
1874 std::vector<BufferInfo> bufInfo;
1875 VmaAllocationInfo allocInfo;
1876
1877 // Test one-time free.
1878 {
1879 // Allocate buffers until we move to a second block.
1880 VkDeviceMemory lastMem = VK_NULL_HANDLE;
1881 for(uint32_t i = 0; ; ++i)
1882 {
1883 BufferInfo newBufInfo;
1884 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1885 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1886 assert(res == VK_SUCCESS);
1887 bufInfo.push_back(newBufInfo);
1888 if(lastMem && allocInfo.deviceMemory != lastMem)
1889 {
1890 break;
1891 }
1892 lastMem = allocInfo.deviceMemory;
1893 }
1894
1895 assert(bufInfo.size() > 2);
1896
1897 // Make sure that pool has now two blocks.
1898 VmaPoolStats poolStats = {};
1899 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1900 assert(poolStats.blockCount == 2);
1901
1902 // Destroy all the buffers in random order.
1903 while(!bufInfo.empty())
1904 {
1905 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1906 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1907 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1908 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1909 }
1910
1911 // Make sure that pool has now at most one block.
1912 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1913 assert(poolStats.blockCount <= 1);
1914 }
1915
1916 // Test stack.
1917 {
1918 // Allocate buffers until we move to a second block.
1919 VkDeviceMemory lastMem = VK_NULL_HANDLE;
1920 for(uint32_t i = 0; ; ++i)
1921 {
1922 BufferInfo newBufInfo;
1923 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1924 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1925 assert(res == VK_SUCCESS);
1926 bufInfo.push_back(newBufInfo);
1927 if(lastMem && allocInfo.deviceMemory != lastMem)
1928 {
1929 break;
1930 }
1931 lastMem = allocInfo.deviceMemory;
1932 }
1933
1934 assert(bufInfo.size() > 2);
1935
1936 // Add few more buffers.
1937 for(uint32_t i = 0; i < 5; ++i)
1938 {
1939 BufferInfo newBufInfo;
1940 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1941 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1942 assert(res == VK_SUCCESS);
1943 bufInfo.push_back(newBufInfo);
1944 }
1945
1946 // Make sure that pool has now two blocks.
1947 VmaPoolStats poolStats = {};
1948 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1949 assert(poolStats.blockCount == 2);
1950
1951 // Delete half of buffers, LIFO.
1952 for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
1953 {
1954 const BufferInfo& currBufInfo = bufInfo.back();
1955 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1956 bufInfo.pop_back();
1957 }
1958
1959 // Add one more buffer.
1960 BufferInfo newBufInfo;
1961 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1962 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1963 assert(res == VK_SUCCESS);
1964 bufInfo.push_back(newBufInfo);
1965
1966 // Make sure that pool has now one block.
1967 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1968 assert(poolStats.blockCount == 1);
1969
1970 // Delete all the remaining buffers, LIFO.
1971 while(!bufInfo.empty())
1972 {
1973 const BufferInfo& currBufInfo = bufInfo.back();
1974 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1975 bufInfo.pop_back();
1976 }
Adam Sawickif799c4f2018-08-23 10:40:30 +02001977 }
1978
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001979 vmaDestroyPool(g_hAllocator, pool);
1980}
1981
Adam Sawickifd11d752018-08-22 15:02:10 +02001982static void ManuallyTestLinearAllocator()
1983{
1984 VmaStats origStats;
1985 vmaCalculateStats(g_hAllocator, &origStats);
1986
1987 wprintf(L"Manually test linear allocator\n");
1988
1989 RandomNumberGenerator rand{645332};
1990
1991 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1992 sampleBufCreateInfo.size = 1024; // Whatever.
1993 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1994
1995 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1996 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1997
1998 VmaPoolCreateInfo poolCreateInfo = {};
1999 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2000 assert(res == VK_SUCCESS);
2001
2002 poolCreateInfo.blockSize = 10 * 1024;
2003 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2004 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2005
2006 VmaPool pool = nullptr;
2007 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2008 assert(res == VK_SUCCESS);
2009
2010 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
2011
2012 VmaAllocationCreateInfo allocCreateInfo = {};
2013 allocCreateInfo.pool = pool;
2014
2015 std::vector<BufferInfo> bufInfo;
2016 VmaAllocationInfo allocInfo;
2017 BufferInfo newBufInfo;
2018
2019 // Test double stack.
2020 {
2021 /*
2022 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
2023 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
2024
2025 Totally:
2026 1 block allocated
2027 10240 Vulkan bytes
2028 6 new allocations
2029 2256 bytes in allocations
2030 */
2031
2032 bufCreateInfo.size = 32;
2033 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2034 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2035 assert(res == VK_SUCCESS);
2036 bufInfo.push_back(newBufInfo);
2037
2038 bufCreateInfo.size = 1024;
2039 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2040 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2041 assert(res == VK_SUCCESS);
2042 bufInfo.push_back(newBufInfo);
2043
2044 bufCreateInfo.size = 32;
2045 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2046 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2047 assert(res == VK_SUCCESS);
2048 bufInfo.push_back(newBufInfo);
2049
2050 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
2051
2052 bufCreateInfo.size = 128;
2053 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2054 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2055 assert(res == VK_SUCCESS);
2056 bufInfo.push_back(newBufInfo);
2057
2058 bufCreateInfo.size = 1024;
2059 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2060 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2061 assert(res == VK_SUCCESS);
2062 bufInfo.push_back(newBufInfo);
2063
2064 bufCreateInfo.size = 16;
2065 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2066 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2067 assert(res == VK_SUCCESS);
2068 bufInfo.push_back(newBufInfo);
2069
2070 VmaStats currStats;
2071 vmaCalculateStats(g_hAllocator, &currStats);
2072 VmaPoolStats poolStats;
2073 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2074
2075 char* statsStr = nullptr;
2076 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
2077
2078 // PUT BREAKPOINT HERE TO CHECK.
2079 // Inspect: currStats versus origStats, poolStats, statsStr.
2080 int I = 0;
2081
2082 vmaFreeStatsString(g_hAllocator, statsStr);
2083
2084 // Destroy the buffers in reverse order.
2085 while(!bufInfo.empty())
2086 {
2087 const BufferInfo& currBufInfo = bufInfo.back();
2088 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2089 bufInfo.pop_back();
2090 }
2091 }
2092
2093 vmaDestroyPool(g_hAllocator, pool);
2094}
2095
Adam Sawicki0a607132018-08-24 11:18:41 +02002096static void BenchmarkLinearAllocatorCase(bool linear, bool empty, FREE_ORDER freeOrder)
2097{
2098 RandomNumberGenerator rand{16223};
2099
2100 const VkDeviceSize bufSizeMin = 32;
2101 const VkDeviceSize bufSizeMax = 1024;
2102 const size_t maxBufCapacity = 10000;
2103 const uint32_t iterationCount = 10;
2104
2105 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2106 sampleBufCreateInfo.size = bufSizeMax;
2107 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2108
2109 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2110 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2111
2112 VmaPoolCreateInfo poolCreateInfo = {};
2113 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2114 assert(res == VK_SUCCESS);
2115
2116 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
2117 if(linear)
2118 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2119 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2120
2121 VmaPool pool = nullptr;
2122 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2123 assert(res == VK_SUCCESS);
2124
2125 // Buffer created just to get memory requirements. Never bound to any memory.
2126 VkBuffer dummyBuffer = VK_NULL_HANDLE;
2127 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, nullptr, &dummyBuffer);
2128 assert(res == VK_SUCCESS && dummyBuffer);
2129
2130 VkMemoryRequirements memReq = {};
2131 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2132
2133 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2134
2135 VmaAllocationCreateInfo allocCreateInfo = {};
2136 allocCreateInfo.pool = pool;
2137
2138 VmaAllocation alloc;
2139 std::vector<VmaAllocation> baseAllocations;
2140
2141 if(!empty)
2142 {
2143 // Make allocations up to half of pool size.
2144 VkDeviceSize totalSize = 0;
2145 while(totalSize < poolCreateInfo.blockSize / 2)
2146 {
2147 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2148 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2149 assert(res == VK_SUCCESS);
2150 baseAllocations.push_back(alloc);
2151 totalSize += memReq.size;
2152 }
2153
2154 // Delete half of them, choose randomly.
2155 size_t allocsToDelete = baseAllocations.size() / 2;
2156 for(size_t i = 0; i < allocsToDelete; ++i)
2157 {
2158 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
2159 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
2160 baseAllocations.erase(baseAllocations.begin() + index);
2161 }
2162 }
2163
2164 // BENCHMARK
2165 const size_t allocCount = maxBufCapacity / 2;
2166 std::vector<VmaAllocation> testAllocations;
2167 testAllocations.reserve(allocCount);
2168 duration allocTotalDuration = duration::zero();
2169 duration freeTotalDuration = duration::zero();
2170 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
2171 {
2172 // Allocations
2173 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
2174 for(size_t i = 0; i < allocCount; ++i)
2175 {
2176 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2177 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2178 assert(res == VK_SUCCESS);
2179 testAllocations.push_back(alloc);
2180 }
2181 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
2182
2183 // Deallocations
2184 switch(freeOrder)
2185 {
2186 case FREE_ORDER::FORWARD:
2187 // Leave testAllocations unchanged.
2188 break;
2189 case FREE_ORDER::BACKWARD:
2190 std::reverse(testAllocations.begin(), testAllocations.end());
2191 break;
2192 case FREE_ORDER::RANDOM:
2193 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
2194 break;
2195 default: assert(0);
2196 }
2197
2198 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
2199 for(size_t i = 0; i < allocCount; ++i)
2200 vmaFreeMemory(g_hAllocator, testAllocations[i]);
2201 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
2202
2203 testAllocations.clear();
2204 }
2205
2206 // Delete baseAllocations
2207 while(!baseAllocations.empty())
2208 {
2209 vmaFreeMemory(g_hAllocator, baseAllocations.back());
2210 baseAllocations.pop_back();
2211 }
2212
2213 vmaDestroyPool(g_hAllocator, pool);
2214
Adam Sawicki0667e332018-08-24 17:26:44 +02002215 printf(" LinearAlgorithm=%u %s FreeOrder=%s: allocations %g s, free %g s\n",
Adam Sawicki0a607132018-08-24 11:18:41 +02002216 linear ? 1 : 0,
Adam Sawicki0667e332018-08-24 17:26:44 +02002217 empty ? "Empty" : "Not empty",
Adam Sawicki0a607132018-08-24 11:18:41 +02002218 FREE_ORDER_NAMES[(size_t)freeOrder],
2219 ToFloatSeconds(allocTotalDuration),
2220 ToFloatSeconds(freeTotalDuration));
2221}
2222
2223static void BenchmarkLinearAllocator()
2224{
2225 wprintf(L"Benchmark linear allocator\n");
2226
2227 uint32_t freeOrderCount = 1;
2228 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
2229 freeOrderCount = 3;
2230 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
2231 freeOrderCount = 2;
2232
2233 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
2234
2235 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
2236 {
2237 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
2238 switch(freeOrderIndex)
2239 {
2240 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
2241 case 1: freeOrder = FREE_ORDER::FORWARD; break;
2242 case 2: freeOrder = FREE_ORDER::RANDOM; break;
2243 default: assert(0);
2244 }
2245
2246 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
2247 {
2248 for(uint32_t linearIndex = 0; linearIndex < 2; ++linearIndex)
2249 {
2250 BenchmarkLinearAllocatorCase(
2251 linearIndex ? 1 : 0, // linear
2252 emptyIndex ? 0 : 1, // empty
2253 freeOrder); // freeOrder
2254 }
2255 }
2256 }
2257}
2258
Adam Sawickib8333fb2018-03-13 16:15:53 +01002259static void TestPool_SameSize()
2260{
2261 const VkDeviceSize BUF_SIZE = 1024 * 1024;
2262 const size_t BUF_COUNT = 100;
2263 VkResult res;
2264
2265 RandomNumberGenerator rand{123};
2266
2267 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2268 bufferInfo.size = BUF_SIZE;
2269 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2270
2271 uint32_t memoryTypeBits = UINT32_MAX;
2272 {
2273 VkBuffer dummyBuffer;
2274 res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2275 assert(res == VK_SUCCESS);
2276
2277 VkMemoryRequirements memReq;
2278 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2279 memoryTypeBits = memReq.memoryTypeBits;
2280
2281 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2282 }
2283
2284 VmaAllocationCreateInfo poolAllocInfo = {};
2285 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2286 uint32_t memTypeIndex;
2287 res = vmaFindMemoryTypeIndex(
2288 g_hAllocator,
2289 memoryTypeBits,
2290 &poolAllocInfo,
2291 &memTypeIndex);
2292
2293 VmaPoolCreateInfo poolCreateInfo = {};
2294 poolCreateInfo.memoryTypeIndex = memTypeIndex;
2295 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
2296 poolCreateInfo.minBlockCount = 1;
2297 poolCreateInfo.maxBlockCount = 4;
2298 poolCreateInfo.frameInUseCount = 0;
2299
2300 VmaPool pool;
2301 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2302 assert(res == VK_SUCCESS);
2303
2304 vmaSetCurrentFrameIndex(g_hAllocator, 1);
2305
2306 VmaAllocationCreateInfo allocInfo = {};
2307 allocInfo.pool = pool;
2308 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2309 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2310
2311 struct BufItem
2312 {
2313 VkBuffer Buf;
2314 VmaAllocation Alloc;
2315 };
2316 std::vector<BufItem> items;
2317
2318 // Fill entire pool.
2319 for(size_t i = 0; i < BUF_COUNT; ++i)
2320 {
2321 BufItem item;
2322 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2323 assert(res == VK_SUCCESS);
2324 items.push_back(item);
2325 }
2326
2327 // Make sure that another allocation would fail.
2328 {
2329 BufItem item;
2330 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2331 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
2332 }
2333
2334 // Validate that no buffer is lost. Also check that they are not mapped.
2335 for(size_t i = 0; i < items.size(); ++i)
2336 {
2337 VmaAllocationInfo allocInfo;
2338 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2339 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2340 assert(allocInfo.pMappedData == nullptr);
2341 }
2342
2343 // Free some percent of random items.
2344 {
2345 const size_t PERCENT_TO_FREE = 10;
2346 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
2347 for(size_t i = 0; i < itemsToFree; ++i)
2348 {
2349 size_t index = (size_t)rand.Generate() % items.size();
2350 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2351 items.erase(items.begin() + index);
2352 }
2353 }
2354
2355 // Randomly allocate and free items.
2356 {
2357 const size_t OPERATION_COUNT = BUF_COUNT;
2358 for(size_t i = 0; i < OPERATION_COUNT; ++i)
2359 {
2360 bool allocate = rand.Generate() % 2 != 0;
2361 if(allocate)
2362 {
2363 if(items.size() < BUF_COUNT)
2364 {
2365 BufItem item;
2366 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2367 assert(res == VK_SUCCESS);
2368 items.push_back(item);
2369 }
2370 }
2371 else // Free
2372 {
2373 if(!items.empty())
2374 {
2375 size_t index = (size_t)rand.Generate() % items.size();
2376 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2377 items.erase(items.begin() + index);
2378 }
2379 }
2380 }
2381 }
2382
2383 // Allocate up to maximum.
2384 while(items.size() < BUF_COUNT)
2385 {
2386 BufItem item;
2387 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2388 assert(res == VK_SUCCESS);
2389 items.push_back(item);
2390 }
2391
2392 // Validate that no buffer is lost.
2393 for(size_t i = 0; i < items.size(); ++i)
2394 {
2395 VmaAllocationInfo allocInfo;
2396 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2397 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2398 }
2399
2400 // Next frame.
2401 vmaSetCurrentFrameIndex(g_hAllocator, 2);
2402
2403 // Allocate another BUF_COUNT buffers.
2404 for(size_t i = 0; i < BUF_COUNT; ++i)
2405 {
2406 BufItem item;
2407 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2408 assert(res == VK_SUCCESS);
2409 items.push_back(item);
2410 }
2411
2412 // Make sure the first BUF_COUNT is lost. Delete them.
2413 for(size_t i = 0; i < BUF_COUNT; ++i)
2414 {
2415 VmaAllocationInfo allocInfo;
2416 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2417 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
2418 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2419 }
2420 items.erase(items.begin(), items.begin() + BUF_COUNT);
2421
2422 // Validate that no buffer is lost.
2423 for(size_t i = 0; i < items.size(); ++i)
2424 {
2425 VmaAllocationInfo allocInfo;
2426 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2427 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2428 }
2429
2430 // Free one item.
2431 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
2432 items.pop_back();
2433
2434 // Validate statistics.
2435 {
2436 VmaPoolStats poolStats = {};
2437 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2438 assert(poolStats.allocationCount == items.size());
2439 assert(poolStats.size = BUF_COUNT * BUF_SIZE);
2440 assert(poolStats.unusedRangeCount == 1);
2441 assert(poolStats.unusedRangeSizeMax == BUF_SIZE);
2442 assert(poolStats.unusedSize == BUF_SIZE);
2443 }
2444
2445 // Free all remaining items.
2446 for(size_t i = items.size(); i--; )
2447 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2448 items.clear();
2449
2450 // Allocate maximum items again.
2451 for(size_t i = 0; i < BUF_COUNT; ++i)
2452 {
2453 BufItem item;
2454 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2455 assert(res == VK_SUCCESS);
2456 items.push_back(item);
2457 }
2458
2459 // Delete every other item.
2460 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
2461 {
2462 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2463 items.erase(items.begin() + i);
2464 }
2465
2466 // Defragment!
2467 {
2468 std::vector<VmaAllocation> allocationsToDefragment(items.size());
2469 for(size_t i = 0; i < items.size(); ++i)
2470 allocationsToDefragment[i] = items[i].Alloc;
2471
2472 VmaDefragmentationStats defragmentationStats;
2473 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
2474 assert(res == VK_SUCCESS);
2475 assert(defragmentationStats.deviceMemoryBlocksFreed == 2);
2476 }
2477
2478 // Free all remaining items.
2479 for(size_t i = items.size(); i--; )
2480 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2481 items.clear();
2482
2483 ////////////////////////////////////////////////////////////////////////////////
2484 // Test for vmaMakePoolAllocationsLost
2485
2486 // Allocate 4 buffers on frame 10.
2487 vmaSetCurrentFrameIndex(g_hAllocator, 10);
2488 for(size_t i = 0; i < 4; ++i)
2489 {
2490 BufItem item;
2491 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2492 assert(res == VK_SUCCESS);
2493 items.push_back(item);
2494 }
2495
2496 // Touch first 2 of them on frame 11.
2497 vmaSetCurrentFrameIndex(g_hAllocator, 11);
2498 for(size_t i = 0; i < 2; ++i)
2499 {
2500 VmaAllocationInfo allocInfo;
2501 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2502 }
2503
2504 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
2505 size_t lostCount = 0xDEADC0DE;
2506 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
2507 assert(lostCount == 2);
2508
2509 // Make another call. Now 0 should be lost.
2510 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
2511 assert(lostCount == 0);
2512
2513 // Make another call, with null count. Should not crash.
2514 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
2515
2516 // END: Free all remaining items.
2517 for(size_t i = items.size(); i--; )
2518 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2519
2520 items.clear();
2521
Adam Sawickid2924172018-06-11 12:48:46 +02002522 ////////////////////////////////////////////////////////////////////////////////
2523 // Test for allocation too large for pool
2524
2525 {
2526 VmaAllocationCreateInfo allocCreateInfo = {};
2527 allocCreateInfo.pool = pool;
2528
2529 VkMemoryRequirements memReq;
2530 memReq.memoryTypeBits = UINT32_MAX;
2531 memReq.alignment = 1;
2532 memReq.size = poolCreateInfo.blockSize + 4;
2533
2534 VmaAllocation alloc = nullptr;
2535 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2536 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
2537 }
2538
Adam Sawickib8333fb2018-03-13 16:15:53 +01002539 vmaDestroyPool(g_hAllocator, pool);
2540}
2541
Adam Sawickie44c6262018-06-15 14:30:39 +02002542static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
2543{
2544 const uint8_t* pBytes = (const uint8_t*)pMemory;
2545 for(size_t i = 0; i < size; ++i)
2546 {
2547 if(pBytes[i] != pattern)
2548 {
2549 return false;
2550 }
2551 }
2552 return true;
2553}
2554
2555static void TestAllocationsInitialization()
2556{
2557 VkResult res;
2558
2559 const size_t BUF_SIZE = 1024;
2560
2561 // Create pool.
2562
2563 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2564 bufInfo.size = BUF_SIZE;
2565 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2566
2567 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
2568 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2569
2570 VmaPoolCreateInfo poolCreateInfo = {};
2571 poolCreateInfo.blockSize = BUF_SIZE * 10;
2572 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
2573 poolCreateInfo.maxBlockCount = 1;
2574 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2575 assert(res == VK_SUCCESS);
2576
2577 VmaAllocationCreateInfo bufAllocCreateInfo = {};
2578 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
2579 assert(res == VK_SUCCESS);
2580
2581 // Create one persistently mapped buffer to keep memory of this block mapped,
2582 // so that pointer to mapped data will remain (more or less...) valid even
2583 // after destruction of other allocations.
2584
2585 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2586 VkBuffer firstBuf;
2587 VmaAllocation firstAlloc;
2588 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
2589 assert(res == VK_SUCCESS);
2590
2591 // Test buffers.
2592
2593 for(uint32_t i = 0; i < 2; ++i)
2594 {
2595 const bool persistentlyMapped = i == 0;
2596 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
2597 VkBuffer buf;
2598 VmaAllocation alloc;
2599 VmaAllocationInfo allocInfo;
2600 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
2601 assert(res == VK_SUCCESS);
2602
2603 void* pMappedData;
2604 if(!persistentlyMapped)
2605 {
2606 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
2607 assert(res == VK_SUCCESS);
2608 }
2609 else
2610 {
2611 pMappedData = allocInfo.pMappedData;
2612 }
2613
2614 // Validate initialized content
2615 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
2616 assert(valid);
2617
2618 if(!persistentlyMapped)
2619 {
2620 vmaUnmapMemory(g_hAllocator, alloc);
2621 }
2622
2623 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2624
2625 // Validate freed content
2626 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
2627 assert(valid);
2628 }
2629
2630 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
2631 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
2632}
2633
Adam Sawickib8333fb2018-03-13 16:15:53 +01002634static void TestPool_Benchmark(
2635 PoolTestResult& outResult,
2636 const PoolTestConfig& config)
2637{
2638 assert(config.ThreadCount > 0);
2639
2640 RandomNumberGenerator mainRand{config.RandSeed};
2641
2642 uint32_t allocationSizeProbabilitySum = std::accumulate(
2643 config.AllocationSizes.begin(),
2644 config.AllocationSizes.end(),
2645 0u,
2646 [](uint32_t sum, const AllocationSize& allocSize) {
2647 return sum + allocSize.Probability;
2648 });
2649
2650 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2651 bufferInfo.size = 256; // Whatever.
2652 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2653
2654 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2655 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2656 imageInfo.extent.width = 256; // Whatever.
2657 imageInfo.extent.height = 256; // Whatever.
2658 imageInfo.extent.depth = 1;
2659 imageInfo.mipLevels = 1;
2660 imageInfo.arrayLayers = 1;
2661 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2662 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
2663 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2664 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
2665 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2666
2667 uint32_t bufferMemoryTypeBits = UINT32_MAX;
2668 {
2669 VkBuffer dummyBuffer;
2670 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2671 assert(res == VK_SUCCESS);
2672
2673 VkMemoryRequirements memReq;
2674 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2675 bufferMemoryTypeBits = memReq.memoryTypeBits;
2676
2677 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2678 }
2679
2680 uint32_t imageMemoryTypeBits = UINT32_MAX;
2681 {
2682 VkImage dummyImage;
2683 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
2684 assert(res == VK_SUCCESS);
2685
2686 VkMemoryRequirements memReq;
2687 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
2688 imageMemoryTypeBits = memReq.memoryTypeBits;
2689
2690 vkDestroyImage(g_hDevice, dummyImage, nullptr);
2691 }
2692
2693 uint32_t memoryTypeBits = 0;
2694 if(config.UsesBuffers() && config.UsesImages())
2695 {
2696 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
2697 if(memoryTypeBits == 0)
2698 {
2699 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
2700 return;
2701 }
2702 }
2703 else if(config.UsesBuffers())
2704 memoryTypeBits = bufferMemoryTypeBits;
2705 else if(config.UsesImages())
2706 memoryTypeBits = imageMemoryTypeBits;
2707 else
2708 assert(0);
2709
2710 VmaPoolCreateInfo poolCreateInfo = {};
2711 poolCreateInfo.memoryTypeIndex = 0;
2712 poolCreateInfo.minBlockCount = 1;
2713 poolCreateInfo.maxBlockCount = 1;
2714 poolCreateInfo.blockSize = config.PoolSize;
2715 poolCreateInfo.frameInUseCount = 1;
2716
2717 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
2718 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2719 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2720
2721 VmaPool pool;
2722 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2723 assert(res == VK_SUCCESS);
2724
2725 // Start time measurement - after creating pool and initializing data structures.
2726 time_point timeBeg = std::chrono::high_resolution_clock::now();
2727
2728 ////////////////////////////////////////////////////////////////////////////////
2729 // ThreadProc
2730 auto ThreadProc = [&](
2731 PoolTestThreadResult* outThreadResult,
2732 uint32_t randSeed,
2733 HANDLE frameStartEvent,
2734 HANDLE frameEndEvent) -> void
2735 {
2736 RandomNumberGenerator threadRand{randSeed};
2737
2738 outThreadResult->AllocationTimeMin = duration::max();
2739 outThreadResult->AllocationTimeSum = duration::zero();
2740 outThreadResult->AllocationTimeMax = duration::min();
2741 outThreadResult->DeallocationTimeMin = duration::max();
2742 outThreadResult->DeallocationTimeSum = duration::zero();
2743 outThreadResult->DeallocationTimeMax = duration::min();
2744 outThreadResult->AllocationCount = 0;
2745 outThreadResult->DeallocationCount = 0;
2746 outThreadResult->LostAllocationCount = 0;
2747 outThreadResult->LostAllocationTotalSize = 0;
2748 outThreadResult->FailedAllocationCount = 0;
2749 outThreadResult->FailedAllocationTotalSize = 0;
2750
2751 struct Item
2752 {
2753 VkDeviceSize BufferSize;
2754 VkExtent2D ImageSize;
2755 VkBuffer Buf;
2756 VkImage Image;
2757 VmaAllocation Alloc;
2758
2759 VkDeviceSize CalcSizeBytes() const
2760 {
2761 return BufferSize +
2762 ImageSize.width * ImageSize.height * 4;
2763 }
2764 };
2765 std::vector<Item> unusedItems, usedItems;
2766
2767 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
2768
2769 // Create all items - all unused, not yet allocated.
2770 for(size_t i = 0; i < threadTotalItemCount; ++i)
2771 {
2772 Item item = {};
2773
2774 uint32_t allocSizeIndex = 0;
2775 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
2776 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
2777 r -= config.AllocationSizes[allocSizeIndex++].Probability;
2778
2779 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
2780 if(allocSize.BufferSizeMax > 0)
2781 {
2782 assert(allocSize.BufferSizeMin > 0);
2783 assert(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
2784 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
2785 item.BufferSize = allocSize.BufferSizeMin;
2786 else
2787 {
2788 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
2789 item.BufferSize = item.BufferSize / 16 * 16;
2790 }
2791 }
2792 else
2793 {
2794 assert(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
2795 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
2796 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
2797 else
2798 {
2799 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2800 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2801 }
2802 }
2803
2804 unusedItems.push_back(item);
2805 }
2806
2807 auto Allocate = [&](Item& item) -> VkResult
2808 {
2809 VmaAllocationCreateInfo allocCreateInfo = {};
2810 allocCreateInfo.pool = pool;
2811 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2812 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2813
2814 if(item.BufferSize)
2815 {
2816 bufferInfo.size = item.BufferSize;
2817 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2818 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
2819 }
2820 else
2821 {
2822 assert(item.ImageSize.width && item.ImageSize.height);
2823
2824 imageInfo.extent.width = item.ImageSize.width;
2825 imageInfo.extent.height = item.ImageSize.height;
2826 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2827 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
2828 }
2829 };
2830
2831 ////////////////////////////////////////////////////////////////////////////////
2832 // Frames
2833 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2834 {
2835 WaitForSingleObject(frameStartEvent, INFINITE);
2836
2837 // Always make some percent of used bufs unused, to choose different used ones.
2838 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
2839 for(size_t i = 0; i < bufsToMakeUnused; ++i)
2840 {
2841 size_t index = threadRand.Generate() % usedItems.size();
2842 unusedItems.push_back(usedItems[index]);
2843 usedItems.erase(usedItems.begin() + index);
2844 }
2845
2846 // Determine which bufs we want to use in this frame.
2847 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
2848 / config.ThreadCount;
2849 assert(usedBufCount < usedItems.size() + unusedItems.size());
2850 // Move some used to unused.
2851 while(usedBufCount < usedItems.size())
2852 {
2853 size_t index = threadRand.Generate() % usedItems.size();
2854 unusedItems.push_back(usedItems[index]);
2855 usedItems.erase(usedItems.begin() + index);
2856 }
2857 // Move some unused to used.
2858 while(usedBufCount > usedItems.size())
2859 {
2860 size_t index = threadRand.Generate() % unusedItems.size();
2861 usedItems.push_back(unusedItems[index]);
2862 unusedItems.erase(unusedItems.begin() + index);
2863 }
2864
2865 uint32_t touchExistingCount = 0;
2866 uint32_t touchLostCount = 0;
2867 uint32_t createSucceededCount = 0;
2868 uint32_t createFailedCount = 0;
2869
2870 // Touch all used bufs. If not created or lost, allocate.
2871 for(size_t i = 0; i < usedItems.size(); ++i)
2872 {
2873 Item& item = usedItems[i];
2874 // Not yet created.
2875 if(item.Alloc == VK_NULL_HANDLE)
2876 {
2877 res = Allocate(item);
2878 ++outThreadResult->AllocationCount;
2879 if(res != VK_SUCCESS)
2880 {
2881 item.Alloc = VK_NULL_HANDLE;
2882 item.Buf = VK_NULL_HANDLE;
2883 ++outThreadResult->FailedAllocationCount;
2884 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
2885 ++createFailedCount;
2886 }
2887 else
2888 ++createSucceededCount;
2889 }
2890 else
2891 {
2892 // Touch.
2893 VmaAllocationInfo allocInfo;
2894 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
2895 // Lost.
2896 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
2897 {
2898 ++touchLostCount;
2899
2900 // Destroy.
2901 {
2902 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2903 if(item.Buf)
2904 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
2905 else
2906 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
2907 ++outThreadResult->DeallocationCount;
2908 }
2909 item.Alloc = VK_NULL_HANDLE;
2910 item.Buf = VK_NULL_HANDLE;
2911
2912 ++outThreadResult->LostAllocationCount;
2913 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
2914
2915 // Recreate.
2916 res = Allocate(item);
2917 ++outThreadResult->AllocationCount;
2918 // Creation failed.
2919 if(res != VK_SUCCESS)
2920 {
2921 ++outThreadResult->FailedAllocationCount;
2922 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
2923 ++createFailedCount;
2924 }
2925 else
2926 ++createSucceededCount;
2927 }
2928 else
2929 ++touchExistingCount;
2930 }
2931 }
2932
2933 /*
2934 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
2935 randSeed, frameIndex,
2936 touchExistingCount, touchLostCount,
2937 createSucceededCount, createFailedCount);
2938 */
2939
2940 SetEvent(frameEndEvent);
2941 }
2942
2943 // Free all remaining items.
2944 for(size_t i = usedItems.size(); i--; )
2945 {
2946 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2947 if(usedItems[i].Buf)
2948 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
2949 else
2950 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
2951 ++outThreadResult->DeallocationCount;
2952 }
2953 for(size_t i = unusedItems.size(); i--; )
2954 {
2955 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
2956 if(unusedItems[i].Buf)
2957 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
2958 else
2959 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
2960 ++outThreadResult->DeallocationCount;
2961 }
2962 };
2963
2964 // Launch threads.
2965 uint32_t threadRandSeed = mainRand.Generate();
2966 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
2967 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
2968 std::vector<std::thread> bkgThreads;
2969 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
2970 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2971 {
2972 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2973 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2974 bkgThreads.emplace_back(std::bind(
2975 ThreadProc,
2976 &threadResults[threadIndex],
2977 threadRandSeed + threadIndex,
2978 frameStartEvents[threadIndex],
2979 frameEndEvents[threadIndex]));
2980 }
2981
2982 // Execute frames.
2983 assert(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
2984 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2985 {
2986 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
2987 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2988 SetEvent(frameStartEvents[threadIndex]);
2989 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
2990 }
2991
2992 // Wait for threads finished
2993 for(size_t i = 0; i < bkgThreads.size(); ++i)
2994 {
2995 bkgThreads[i].join();
2996 CloseHandle(frameEndEvents[i]);
2997 CloseHandle(frameStartEvents[i]);
2998 }
2999 bkgThreads.clear();
3000
3001 // Finish time measurement - before destroying pool.
3002 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
3003
3004 vmaDestroyPool(g_hAllocator, pool);
3005
3006 outResult.AllocationTimeMin = duration::max();
3007 outResult.AllocationTimeAvg = duration::zero();
3008 outResult.AllocationTimeMax = duration::min();
3009 outResult.DeallocationTimeMin = duration::max();
3010 outResult.DeallocationTimeAvg = duration::zero();
3011 outResult.DeallocationTimeMax = duration::min();
3012 outResult.LostAllocationCount = 0;
3013 outResult.LostAllocationTotalSize = 0;
3014 outResult.FailedAllocationCount = 0;
3015 outResult.FailedAllocationTotalSize = 0;
3016 size_t allocationCount = 0;
3017 size_t deallocationCount = 0;
3018 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3019 {
3020 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
3021 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
3022 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
3023 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
3024 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
3025 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
3026 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
3027 allocationCount += threadResult.AllocationCount;
3028 deallocationCount += threadResult.DeallocationCount;
3029 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
3030 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
3031 outResult.LostAllocationCount += threadResult.LostAllocationCount;
3032 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
3033 }
3034 if(allocationCount)
3035 outResult.AllocationTimeAvg /= allocationCount;
3036 if(deallocationCount)
3037 outResult.DeallocationTimeAvg /= deallocationCount;
3038}
3039
3040static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
3041{
3042 if(ptr1 < ptr2)
3043 return ptr1 + size1 > ptr2;
3044 else if(ptr2 < ptr1)
3045 return ptr2 + size2 > ptr1;
3046 else
3047 return true;
3048}
3049
3050static void TestMapping()
3051{
3052 wprintf(L"Testing mapping...\n");
3053
3054 VkResult res;
3055 uint32_t memTypeIndex = UINT32_MAX;
3056
3057 enum TEST
3058 {
3059 TEST_NORMAL,
3060 TEST_POOL,
3061 TEST_DEDICATED,
3062 TEST_COUNT
3063 };
3064 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3065 {
3066 VmaPool pool = nullptr;
3067 if(testIndex == TEST_POOL)
3068 {
3069 assert(memTypeIndex != UINT32_MAX);
3070 VmaPoolCreateInfo poolInfo = {};
3071 poolInfo.memoryTypeIndex = memTypeIndex;
3072 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
3073 assert(res == VK_SUCCESS);
3074 }
3075
3076 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3077 bufInfo.size = 0x10000;
3078 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3079
3080 VmaAllocationCreateInfo allocCreateInfo = {};
3081 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3082 allocCreateInfo.pool = pool;
3083 if(testIndex == TEST_DEDICATED)
3084 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3085
3086 VmaAllocationInfo allocInfo;
3087
3088 // Mapped manually
3089
3090 // Create 2 buffers.
3091 BufferInfo bufferInfos[3];
3092 for(size_t i = 0; i < 2; ++i)
3093 {
3094 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3095 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
3096 assert(res == VK_SUCCESS);
3097 assert(allocInfo.pMappedData == nullptr);
3098 memTypeIndex = allocInfo.memoryType;
3099 }
3100
3101 // Map buffer 0.
3102 char* data00 = nullptr;
3103 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
3104 assert(res == VK_SUCCESS && data00 != nullptr);
3105 data00[0xFFFF] = data00[0];
3106
3107 // Map buffer 0 second time.
3108 char* data01 = nullptr;
3109 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
3110 assert(res == VK_SUCCESS && data01 == data00);
3111
3112 // Map buffer 1.
3113 char* data1 = nullptr;
3114 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
3115 assert(res == VK_SUCCESS && data1 != nullptr);
3116 assert(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
3117 data1[0xFFFF] = data1[0];
3118
3119 // Unmap buffer 0 two times.
3120 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3121 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3122 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
3123 assert(allocInfo.pMappedData == nullptr);
3124
3125 // Unmap buffer 1.
3126 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
3127 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
3128 assert(allocInfo.pMappedData == nullptr);
3129
3130 // Create 3rd buffer - persistently mapped.
3131 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3132 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3133 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
3134 assert(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
3135
3136 // Map buffer 2.
3137 char* data2 = nullptr;
3138 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
3139 assert(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
3140 data2[0xFFFF] = data2[0];
3141
3142 // Unmap buffer 2.
3143 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
3144 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
3145 assert(allocInfo.pMappedData == data2);
3146
3147 // Destroy all buffers.
3148 for(size_t i = 3; i--; )
3149 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
3150
3151 vmaDestroyPool(g_hAllocator, pool);
3152 }
3153}
3154
3155static void TestMappingMultithreaded()
3156{
3157 wprintf(L"Testing mapping multithreaded...\n");
3158
3159 static const uint32_t threadCount = 16;
3160 static const uint32_t bufferCount = 1024;
3161 static const uint32_t threadBufferCount = bufferCount / threadCount;
3162
3163 VkResult res;
3164 volatile uint32_t memTypeIndex = UINT32_MAX;
3165
3166 enum TEST
3167 {
3168 TEST_NORMAL,
3169 TEST_POOL,
3170 TEST_DEDICATED,
3171 TEST_COUNT
3172 };
3173 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3174 {
3175 VmaPool pool = nullptr;
3176 if(testIndex == TEST_POOL)
3177 {
3178 assert(memTypeIndex != UINT32_MAX);
3179 VmaPoolCreateInfo poolInfo = {};
3180 poolInfo.memoryTypeIndex = memTypeIndex;
3181 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
3182 assert(res == VK_SUCCESS);
3183 }
3184
3185 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3186 bufCreateInfo.size = 0x10000;
3187 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3188
3189 VmaAllocationCreateInfo allocCreateInfo = {};
3190 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3191 allocCreateInfo.pool = pool;
3192 if(testIndex == TEST_DEDICATED)
3193 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3194
3195 std::thread threads[threadCount];
3196 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3197 {
3198 threads[threadIndex] = std::thread([=, &memTypeIndex](){
3199 // ======== THREAD FUNCTION ========
3200
3201 RandomNumberGenerator rand{threadIndex};
3202
3203 enum class MODE
3204 {
3205 // Don't map this buffer at all.
3206 DONT_MAP,
3207 // Map and quickly unmap.
3208 MAP_FOR_MOMENT,
3209 // Map and unmap before destruction.
3210 MAP_FOR_LONGER,
3211 // Map two times. Quickly unmap, second unmap before destruction.
3212 MAP_TWO_TIMES,
3213 // Create this buffer as persistently mapped.
3214 PERSISTENTLY_MAPPED,
3215 COUNT
3216 };
3217 std::vector<BufferInfo> bufInfos{threadBufferCount};
3218 std::vector<MODE> bufModes{threadBufferCount};
3219
3220 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
3221 {
3222 BufferInfo& bufInfo = bufInfos[bufferIndex];
3223 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
3224 bufModes[bufferIndex] = mode;
3225
3226 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
3227 if(mode == MODE::PERSISTENTLY_MAPPED)
3228 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3229
3230 VmaAllocationInfo allocInfo;
3231 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
3232 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
3233 assert(res == VK_SUCCESS);
3234
3235 if(memTypeIndex == UINT32_MAX)
3236 memTypeIndex = allocInfo.memoryType;
3237
3238 char* data = nullptr;
3239
3240 if(mode == MODE::PERSISTENTLY_MAPPED)
3241 {
3242 data = (char*)allocInfo.pMappedData;
3243 assert(data != nullptr);
3244 }
3245 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
3246 mode == MODE::MAP_TWO_TIMES)
3247 {
3248 assert(data == nullptr);
3249 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
3250 assert(res == VK_SUCCESS && data != nullptr);
3251
3252 if(mode == MODE::MAP_TWO_TIMES)
3253 {
3254 char* data2 = nullptr;
3255 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
3256 assert(res == VK_SUCCESS && data2 == data);
3257 }
3258 }
3259 else if(mode == MODE::DONT_MAP)
3260 {
3261 assert(allocInfo.pMappedData == nullptr);
3262 }
3263 else
3264 assert(0);
3265
3266 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3267 if(data)
3268 data[0xFFFF] = data[0];
3269
3270 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
3271 {
3272 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
3273
3274 VmaAllocationInfo allocInfo;
3275 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
3276 if(mode == MODE::MAP_FOR_MOMENT)
3277 assert(allocInfo.pMappedData == nullptr);
3278 else
3279 assert(allocInfo.pMappedData == data);
3280 }
3281
3282 switch(rand.Generate() % 3)
3283 {
3284 case 0: Sleep(0); break; // Yield.
3285 case 1: Sleep(10); break; // 10 ms
3286 // default: No sleep.
3287 }
3288
3289 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3290 if(data)
3291 data[0xFFFF] = data[0];
3292 }
3293
3294 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
3295 {
3296 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
3297 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
3298 {
3299 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
3300
3301 VmaAllocationInfo allocInfo;
3302 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
3303 assert(allocInfo.pMappedData == nullptr);
3304 }
3305
3306 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
3307 }
3308 });
3309 }
3310
3311 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3312 threads[threadIndex].join();
3313
3314 vmaDestroyPool(g_hAllocator, pool);
3315 }
3316}
3317
3318static void WriteMainTestResultHeader(FILE* file)
3319{
3320 fprintf(file,
Adam Sawicki740b08f2018-08-27 13:42:07 +02003321 "Code,Time,"
3322 "Threads,Buffers and images,Sizes,Operations,Allocation strategy,Free order,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003323 "Total Time (us),"
3324 "Allocation Time Min (us),"
3325 "Allocation Time Avg (us),"
3326 "Allocation Time Max (us),"
3327 "Deallocation Time Min (us),"
3328 "Deallocation Time Avg (us),"
3329 "Deallocation Time Max (us),"
3330 "Total Memory Allocated (B),"
3331 "Free Range Size Avg (B),"
3332 "Free Range Size Max (B)\n");
3333}
3334
3335static void WriteMainTestResult(
3336 FILE* file,
3337 const char* codeDescription,
3338 const char* testDescription,
3339 const Config& config, const Result& result)
3340{
3341 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3342 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3343 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3344 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3345 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3346 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3347 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3348
3349 time_t rawTime; time(&rawTime);
3350 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
3351 char timeStr[128];
3352 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
3353
3354 fprintf(file,
3355 "%s,%s,%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003356 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
3357 codeDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01003358 timeStr,
Adam Sawicki740b08f2018-08-27 13:42:07 +02003359 testDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01003360 totalTimeSeconds * 1e6f,
3361 allocationTimeMinSeconds * 1e6f,
3362 allocationTimeAvgSeconds * 1e6f,
3363 allocationTimeMaxSeconds * 1e6f,
3364 deallocationTimeMinSeconds * 1e6f,
3365 deallocationTimeAvgSeconds * 1e6f,
3366 deallocationTimeMaxSeconds * 1e6f,
3367 result.TotalMemoryAllocated,
3368 result.FreeRangeSizeAvg,
3369 result.FreeRangeSizeMax);
3370}
3371
3372static void WritePoolTestResultHeader(FILE* file)
3373{
3374 fprintf(file,
3375 "Code,Test,Time,"
3376 "Config,"
3377 "Total Time (us),"
3378 "Allocation Time Min (us),"
3379 "Allocation Time Avg (us),"
3380 "Allocation Time Max (us),"
3381 "Deallocation Time Min (us),"
3382 "Deallocation Time Avg (us),"
3383 "Deallocation Time Max (us),"
3384 "Lost Allocation Count,"
3385 "Lost Allocation Total Size (B),"
3386 "Failed Allocation Count,"
3387 "Failed Allocation Total Size (B)\n");
3388}
3389
3390static void WritePoolTestResult(
3391 FILE* file,
3392 const char* codeDescription,
3393 const char* testDescription,
3394 const PoolTestConfig& config,
3395 const PoolTestResult& result)
3396{
3397 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3398 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3399 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3400 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3401 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3402 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3403 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3404
3405 time_t rawTime; time(&rawTime);
3406 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
3407 char timeStr[128];
3408 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
3409
3410 fprintf(file,
3411 "%s,%s,%s,"
3412 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
3413 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
3414 // General
3415 codeDescription,
3416 testDescription,
3417 timeStr,
3418 // Config
3419 config.ThreadCount,
3420 (unsigned long long)config.PoolSize,
3421 config.FrameCount,
3422 config.TotalItemCount,
3423 config.UsedItemCountMin,
3424 config.UsedItemCountMax,
3425 config.ItemsToMakeUnusedPercent,
3426 // Results
3427 totalTimeSeconds * 1e6f,
3428 allocationTimeMinSeconds * 1e6f,
3429 allocationTimeAvgSeconds * 1e6f,
3430 allocationTimeMaxSeconds * 1e6f,
3431 deallocationTimeMinSeconds * 1e6f,
3432 deallocationTimeAvgSeconds * 1e6f,
3433 deallocationTimeMaxSeconds * 1e6f,
3434 result.LostAllocationCount,
3435 result.LostAllocationTotalSize,
3436 result.FailedAllocationCount,
3437 result.FailedAllocationTotalSize);
3438}
3439
3440static void PerformCustomMainTest(FILE* file)
3441{
3442 Config config{};
3443 config.RandSeed = 65735476;
3444 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
3445 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3446 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3447 config.FreeOrder = FREE_ORDER::FORWARD;
3448 config.ThreadCount = 16;
3449 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02003450 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003451
3452 // Buffers
3453 //config.AllocationSizes.push_back({4, 16, 1024});
3454 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3455
3456 // Images
3457 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3458 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3459
3460 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3461 config.AdditionalOperationCount = 1024;
3462
3463 Result result{};
3464 VkResult res = MainTest(result, config);
3465 assert(res == VK_SUCCESS);
3466 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
3467}
3468
3469static void PerformCustomPoolTest(FILE* file)
3470{
3471 PoolTestConfig config;
3472 config.PoolSize = 100 * 1024 * 1024;
3473 config.RandSeed = 2345764;
3474 config.ThreadCount = 1;
3475 config.FrameCount = 200;
3476 config.ItemsToMakeUnusedPercent = 2;
3477
3478 AllocationSize allocSize = {};
3479 allocSize.BufferSizeMin = 1024;
3480 allocSize.BufferSizeMax = 1024 * 1024;
3481 allocSize.Probability = 1;
3482 config.AllocationSizes.push_back(allocSize);
3483
3484 allocSize.BufferSizeMin = 0;
3485 allocSize.BufferSizeMax = 0;
3486 allocSize.ImageSizeMin = 128;
3487 allocSize.ImageSizeMax = 1024;
3488 allocSize.Probability = 1;
3489 config.AllocationSizes.push_back(allocSize);
3490
3491 config.PoolSize = config.CalcAvgResourceSize() * 200;
3492 config.UsedItemCountMax = 160;
3493 config.TotalItemCount = config.UsedItemCountMax * 10;
3494 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3495
3496 g_MemoryAliasingWarningEnabled = false;
3497 PoolTestResult result = {};
3498 TestPool_Benchmark(result, config);
3499 g_MemoryAliasingWarningEnabled = true;
3500
3501 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
3502}
3503
Adam Sawickib8333fb2018-03-13 16:15:53 +01003504static const char* CODE_DESCRIPTION = "Foo";
3505
3506static void PerformMainTests(FILE* file)
3507{
3508 uint32_t repeatCount = 1;
3509 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3510
3511 Config config{};
3512 config.RandSeed = 65735476;
3513 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3514 config.FreeOrder = FREE_ORDER::FORWARD;
3515
3516 size_t threadCountCount = 1;
3517 switch(ConfigType)
3518 {
3519 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3520 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3521 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
3522 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
3523 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
3524 default: assert(0);
3525 }
Adam Sawicki0667e332018-08-24 17:26:44 +02003526
3527 size_t strategyCount = 0;
3528 switch(ConfigType)
3529 {
3530 case CONFIG_TYPE_MINIMUM: strategyCount = 1; break;
3531 case CONFIG_TYPE_SMALL: strategyCount = 1; break;
3532 case CONFIG_TYPE_AVERAGE: strategyCount = 2; break;
3533 case CONFIG_TYPE_LARGE: strategyCount = 2; break;
3534 case CONFIG_TYPE_MAXIMUM: strategyCount = 3; break;
3535 default: assert(0);
3536 }
3537
Adam Sawickib8333fb2018-03-13 16:15:53 +01003538 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3539 {
3540 std::string desc1;
3541
3542 switch(threadCountIndex)
3543 {
3544 case 0:
3545 desc1 += "1_thread";
3546 config.ThreadCount = 1;
3547 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3548 break;
3549 case 1:
3550 desc1 += "16_threads+0%_common";
3551 config.ThreadCount = 16;
3552 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3553 break;
3554 case 2:
3555 desc1 += "16_threads+50%_common";
3556 config.ThreadCount = 16;
3557 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3558 break;
3559 case 3:
3560 desc1 += "16_threads+100%_common";
3561 config.ThreadCount = 16;
3562 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3563 break;
3564 case 4:
3565 desc1 += "2_threads+0%_common";
3566 config.ThreadCount = 2;
3567 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3568 break;
3569 case 5:
3570 desc1 += "2_threads+50%_common";
3571 config.ThreadCount = 2;
3572 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3573 break;
3574 case 6:
3575 desc1 += "2_threads+100%_common";
3576 config.ThreadCount = 2;
3577 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3578 break;
3579 default:
3580 assert(0);
3581 }
3582
3583 // 0 = buffers, 1 = images, 2 = buffers and images
3584 size_t buffersVsImagesCount = 2;
3585 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3586 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3587 {
3588 std::string desc2 = desc1;
3589 switch(buffersVsImagesIndex)
3590 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003591 case 0: desc2 += ",Buffers"; break;
3592 case 1: desc2 += ",Images"; break;
3593 case 2: desc2 += ",Buffers+Images"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003594 default: assert(0);
3595 }
3596
3597 // 0 = small, 1 = large, 2 = small and large
3598 size_t smallVsLargeCount = 2;
3599 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3600 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3601 {
3602 std::string desc3 = desc2;
3603 switch(smallVsLargeIndex)
3604 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003605 case 0: desc3 += ",Small"; break;
3606 case 1: desc3 += ",Large"; break;
3607 case 2: desc3 += ",Small+Large"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003608 default: assert(0);
3609 }
3610
3611 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3612 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3613 else
3614 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
3615
3616 // 0 = varying sizes min...max, 1 = set of constant sizes
3617 size_t constantSizesCount = 1;
3618 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3619 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3620 {
3621 std::string desc4 = desc3;
3622 switch(constantSizesIndex)
3623 {
3624 case 0: desc4 += " Varying_sizes"; break;
3625 case 1: desc4 += " Constant_sizes"; break;
3626 default: assert(0);
3627 }
3628
3629 config.AllocationSizes.clear();
3630 // Buffers present
3631 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3632 {
3633 // Small
3634 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3635 {
3636 // Varying size
3637 if(constantSizesIndex == 0)
3638 config.AllocationSizes.push_back({4, 16, 1024});
3639 // Constant sizes
3640 else
3641 {
3642 config.AllocationSizes.push_back({1, 16, 16});
3643 config.AllocationSizes.push_back({1, 64, 64});
3644 config.AllocationSizes.push_back({1, 256, 256});
3645 config.AllocationSizes.push_back({1, 1024, 1024});
3646 }
3647 }
3648 // Large
3649 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3650 {
3651 // Varying size
3652 if(constantSizesIndex == 0)
3653 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3654 // Constant sizes
3655 else
3656 {
3657 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3658 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3659 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3660 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3661 }
3662 }
3663 }
3664 // Images present
3665 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3666 {
3667 // Small
3668 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3669 {
3670 // Varying size
3671 if(constantSizesIndex == 0)
3672 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3673 // Constant sizes
3674 else
3675 {
3676 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3677 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3678 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3679 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3680 }
3681 }
3682 // Large
3683 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3684 {
3685 // Varying size
3686 if(constantSizesIndex == 0)
3687 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3688 // Constant sizes
3689 else
3690 {
3691 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3692 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3693 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3694 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3695 }
3696 }
3697 }
3698
3699 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
3700 size_t beginBytesToAllocateCount = 1;
3701 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
3702 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
3703 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
3704 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
3705 {
3706 std::string desc5 = desc4;
3707
3708 switch(beginBytesToAllocateIndex)
3709 {
3710 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003711 desc5 += ",Allocate_100%";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003712 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
3713 config.AdditionalOperationCount = 0;
3714 break;
3715 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003716 desc5 += ",Allocate_50%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003717 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
3718 config.AdditionalOperationCount = 1024;
3719 break;
3720 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003721 desc5 += ",Allocate_5%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003722 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3723 config.AdditionalOperationCount = 1024;
3724 break;
3725 case 3:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003726 desc5 += ",Allocate_95%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01003727 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
3728 config.AdditionalOperationCount = 1024;
3729 break;
3730 default:
3731 assert(0);
3732 }
3733
Adam Sawicki0667e332018-08-24 17:26:44 +02003734 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01003735 {
Adam Sawicki0667e332018-08-24 17:26:44 +02003736 std::string desc6 = desc5;
3737 switch(strategyIndex)
3738 {
3739 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003740 desc6 += ",BestFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003741 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
3742 break;
3743 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003744 desc6 += ",WorstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003745 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
3746 break;
3747 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02003748 desc6 += ",FirstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02003749 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
3750 break;
3751 default:
3752 assert(0);
3753 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01003754
Adam Sawicki740b08f2018-08-27 13:42:07 +02003755 switch(config.FreeOrder)
3756 {
3757 case FREE_ORDER::FORWARD: desc6 += ",Forward"; break;
3758 case FREE_ORDER::BACKWARD: desc6 += ",Backward"; break;
3759 case FREE_ORDER::RANDOM: desc6 += ",Random"; break;
3760 default: assert(0);
3761 }
3762
3763 const char* testDescription = desc6.c_str();
Adam Sawicki0667e332018-08-24 17:26:44 +02003764
3765 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3766 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003767 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawicki0667e332018-08-24 17:26:44 +02003768
3769 Result result{};
3770 VkResult res = MainTest(result, config);
3771 assert(res == VK_SUCCESS);
Adam Sawicki740b08f2018-08-27 13:42:07 +02003772 if(file)
3773 {
3774 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3775 }
Adam Sawicki0667e332018-08-24 17:26:44 +02003776 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01003777 }
3778 }
3779 }
3780 }
3781 }
3782 }
3783}
3784
3785static void PerformPoolTests(FILE* file)
3786{
3787 const size_t AVG_RESOURCES_PER_POOL = 300;
3788
3789 uint32_t repeatCount = 1;
3790 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3791
3792 PoolTestConfig config{};
3793 config.RandSeed = 2346343;
3794 config.FrameCount = 200;
3795 config.ItemsToMakeUnusedPercent = 2;
3796
3797 size_t threadCountCount = 1;
3798 switch(ConfigType)
3799 {
3800 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3801 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3802 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
3803 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
3804 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
3805 default: assert(0);
3806 }
3807 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3808 {
3809 std::string desc1;
3810
3811 switch(threadCountIndex)
3812 {
3813 case 0:
3814 desc1 += "1_thread";
3815 config.ThreadCount = 1;
3816 break;
3817 case 1:
3818 desc1 += "16_threads";
3819 config.ThreadCount = 16;
3820 break;
3821 case 2:
3822 desc1 += "2_threads";
3823 config.ThreadCount = 2;
3824 break;
3825 default:
3826 assert(0);
3827 }
3828
3829 // 0 = buffers, 1 = images, 2 = buffers and images
3830 size_t buffersVsImagesCount = 2;
3831 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3832 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3833 {
3834 std::string desc2 = desc1;
3835 switch(buffersVsImagesIndex)
3836 {
3837 case 0: desc2 += " Buffers"; break;
3838 case 1: desc2 += " Images"; break;
3839 case 2: desc2 += " Buffers+Images"; break;
3840 default: assert(0);
3841 }
3842
3843 // 0 = small, 1 = large, 2 = small and large
3844 size_t smallVsLargeCount = 2;
3845 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3846 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3847 {
3848 std::string desc3 = desc2;
3849 switch(smallVsLargeIndex)
3850 {
3851 case 0: desc3 += " Small"; break;
3852 case 1: desc3 += " Large"; break;
3853 case 2: desc3 += " Small+Large"; break;
3854 default: assert(0);
3855 }
3856
3857 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3858 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
3859 else
3860 config.PoolSize = 4ull * 1024 * 1024;
3861
3862 // 0 = varying sizes min...max, 1 = set of constant sizes
3863 size_t constantSizesCount = 1;
3864 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3865 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3866 {
3867 std::string desc4 = desc3;
3868 switch(constantSizesIndex)
3869 {
3870 case 0: desc4 += " Varying_sizes"; break;
3871 case 1: desc4 += " Constant_sizes"; break;
3872 default: assert(0);
3873 }
3874
3875 config.AllocationSizes.clear();
3876 // Buffers present
3877 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3878 {
3879 // Small
3880 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3881 {
3882 // Varying size
3883 if(constantSizesIndex == 0)
3884 config.AllocationSizes.push_back({4, 16, 1024});
3885 // Constant sizes
3886 else
3887 {
3888 config.AllocationSizes.push_back({1, 16, 16});
3889 config.AllocationSizes.push_back({1, 64, 64});
3890 config.AllocationSizes.push_back({1, 256, 256});
3891 config.AllocationSizes.push_back({1, 1024, 1024});
3892 }
3893 }
3894 // Large
3895 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3896 {
3897 // Varying size
3898 if(constantSizesIndex == 0)
3899 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3900 // Constant sizes
3901 else
3902 {
3903 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3904 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3905 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3906 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3907 }
3908 }
3909 }
3910 // Images present
3911 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3912 {
3913 // Small
3914 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3915 {
3916 // Varying size
3917 if(constantSizesIndex == 0)
3918 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3919 // Constant sizes
3920 else
3921 {
3922 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3923 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3924 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3925 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3926 }
3927 }
3928 // Large
3929 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3930 {
3931 // Varying size
3932 if(constantSizesIndex == 0)
3933 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3934 // Constant sizes
3935 else
3936 {
3937 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3938 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3939 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3940 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3941 }
3942 }
3943 }
3944
3945 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
3946 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
3947
3948 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
3949 size_t subscriptionModeCount;
3950 switch(ConfigType)
3951 {
3952 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
3953 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
3954 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
3955 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
3956 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
3957 default: assert(0);
3958 }
3959 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
3960 {
3961 std::string desc5 = desc4;
3962
3963 switch(subscriptionModeIndex)
3964 {
3965 case 0:
3966 desc5 += " Subscription_66%";
3967 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
3968 break;
3969 case 1:
3970 desc5 += " Subscription_133%";
3971 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
3972 break;
3973 case 2:
3974 desc5 += " Subscription_100%";
3975 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
3976 break;
3977 case 3:
3978 desc5 += " Subscription_33%";
3979 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
3980 break;
3981 case 4:
3982 desc5 += " Subscription_166%";
3983 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
3984 break;
3985 default:
3986 assert(0);
3987 }
3988
3989 config.TotalItemCount = config.UsedItemCountMax * 5;
3990 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3991
3992 const char* testDescription = desc5.c_str();
3993
3994 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3995 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02003996 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003997
3998 PoolTestResult result{};
3999 g_MemoryAliasingWarningEnabled = false;
4000 TestPool_Benchmark(result, config);
4001 g_MemoryAliasingWarningEnabled = true;
4002 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
4003 }
4004 }
4005 }
4006 }
4007 }
4008 }
4009}
4010
4011void Test()
4012{
4013 wprintf(L"TESTING:\n");
4014
Adam Sawicki70a683e2018-08-24 15:36:32 +02004015 if(false)
4016 {
4017 // # Temporarily insert custom tests here
Adam Sawicki70a683e2018-08-24 15:36:32 +02004018 return;
4019 }
4020
Adam Sawickib8333fb2018-03-13 16:15:53 +01004021 // # Simple tests
4022
4023 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02004024#if VMA_DEBUG_MARGIN
4025 TestDebugMargin();
4026#else
4027 TestPool_SameSize();
4028 TestHeapSizeLimit();
4029#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02004030#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
4031 TestAllocationsInitialization();
4032#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01004033 TestMapping();
4034 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02004035 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02004036 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02004037 TestLinearAllocatorMultiBlock();
Adam Sawicki0a607132018-08-24 11:18:41 +02004038 BenchmarkLinearAllocator();
Adam Sawickib8333fb2018-03-13 16:15:53 +01004039 TestDefragmentationSimple();
4040 TestDefragmentationFull();
4041
4042 // # Detailed tests
4043 FILE* file;
4044 fopen_s(&file, "Results.csv", "w");
4045 assert(file != NULL);
4046
4047 WriteMainTestResultHeader(file);
4048 PerformMainTests(file);
4049 //PerformCustomMainTest(file);
4050
4051 WritePoolTestResultHeader(file);
4052 PerformPoolTests(file);
4053 //PerformCustomPoolTest(file);
4054
4055 fclose(file);
4056
4057 wprintf(L"Done.\n");
4058}
4059
Adam Sawickif1a793c2018-03-13 15:42:22 +01004060#endif // #ifdef _WIN32