blob: d04045d5ff909a15dda9a14d7864a6816945ec73 [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,
3321 "Code,Test,Time,"
3322 "Config,"
3323 "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 Sawicki0667e332018-08-24 17:26:44 +02003356 "BeginBytesToAllocate=%I64u MaxBytesToAllocate=%I64u AdditionalOperationCount=%u ThreadCount=%u FreeOrder=%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01003357 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
3358 codeDescription,
3359 testDescription,
3360 timeStr,
Adam Sawicki0667e332018-08-24 17:26:44 +02003361 config.BeginBytesToAllocate, config.MaxBytesToAllocate, config.AdditionalOperationCount, config.ThreadCount, FREE_ORDER_NAMES[(uint32_t)config.FreeOrder],
Adam Sawickib8333fb2018-03-13 16:15:53 +01003362 totalTimeSeconds * 1e6f,
3363 allocationTimeMinSeconds * 1e6f,
3364 allocationTimeAvgSeconds * 1e6f,
3365 allocationTimeMaxSeconds * 1e6f,
3366 deallocationTimeMinSeconds * 1e6f,
3367 deallocationTimeAvgSeconds * 1e6f,
3368 deallocationTimeMaxSeconds * 1e6f,
3369 result.TotalMemoryAllocated,
3370 result.FreeRangeSizeAvg,
3371 result.FreeRangeSizeMax);
3372}
3373
3374static void WritePoolTestResultHeader(FILE* file)
3375{
3376 fprintf(file,
3377 "Code,Test,Time,"
3378 "Config,"
3379 "Total Time (us),"
3380 "Allocation Time Min (us),"
3381 "Allocation Time Avg (us),"
3382 "Allocation Time Max (us),"
3383 "Deallocation Time Min (us),"
3384 "Deallocation Time Avg (us),"
3385 "Deallocation Time Max (us),"
3386 "Lost Allocation Count,"
3387 "Lost Allocation Total Size (B),"
3388 "Failed Allocation Count,"
3389 "Failed Allocation Total Size (B)\n");
3390}
3391
3392static void WritePoolTestResult(
3393 FILE* file,
3394 const char* codeDescription,
3395 const char* testDescription,
3396 const PoolTestConfig& config,
3397 const PoolTestResult& result)
3398{
3399 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3400 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3401 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3402 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3403 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3404 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3405 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3406
3407 time_t rawTime; time(&rawTime);
3408 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
3409 char timeStr[128];
3410 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
3411
3412 fprintf(file,
3413 "%s,%s,%s,"
3414 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
3415 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
3416 // General
3417 codeDescription,
3418 testDescription,
3419 timeStr,
3420 // Config
3421 config.ThreadCount,
3422 (unsigned long long)config.PoolSize,
3423 config.FrameCount,
3424 config.TotalItemCount,
3425 config.UsedItemCountMin,
3426 config.UsedItemCountMax,
3427 config.ItemsToMakeUnusedPercent,
3428 // Results
3429 totalTimeSeconds * 1e6f,
3430 allocationTimeMinSeconds * 1e6f,
3431 allocationTimeAvgSeconds * 1e6f,
3432 allocationTimeMaxSeconds * 1e6f,
3433 deallocationTimeMinSeconds * 1e6f,
3434 deallocationTimeAvgSeconds * 1e6f,
3435 deallocationTimeMaxSeconds * 1e6f,
3436 result.LostAllocationCount,
3437 result.LostAllocationTotalSize,
3438 result.FailedAllocationCount,
3439 result.FailedAllocationTotalSize);
3440}
3441
3442static void PerformCustomMainTest(FILE* file)
3443{
3444 Config config{};
3445 config.RandSeed = 65735476;
3446 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
3447 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3448 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3449 config.FreeOrder = FREE_ORDER::FORWARD;
3450 config.ThreadCount = 16;
3451 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02003452 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01003453
3454 // Buffers
3455 //config.AllocationSizes.push_back({4, 16, 1024});
3456 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3457
3458 // Images
3459 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3460 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3461
3462 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3463 config.AdditionalOperationCount = 1024;
3464
3465 Result result{};
3466 VkResult res = MainTest(result, config);
3467 assert(res == VK_SUCCESS);
3468 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
3469}
3470
3471static void PerformCustomPoolTest(FILE* file)
3472{
3473 PoolTestConfig config;
3474 config.PoolSize = 100 * 1024 * 1024;
3475 config.RandSeed = 2345764;
3476 config.ThreadCount = 1;
3477 config.FrameCount = 200;
3478 config.ItemsToMakeUnusedPercent = 2;
3479
3480 AllocationSize allocSize = {};
3481 allocSize.BufferSizeMin = 1024;
3482 allocSize.BufferSizeMax = 1024 * 1024;
3483 allocSize.Probability = 1;
3484 config.AllocationSizes.push_back(allocSize);
3485
3486 allocSize.BufferSizeMin = 0;
3487 allocSize.BufferSizeMax = 0;
3488 allocSize.ImageSizeMin = 128;
3489 allocSize.ImageSizeMax = 1024;
3490 allocSize.Probability = 1;
3491 config.AllocationSizes.push_back(allocSize);
3492
3493 config.PoolSize = config.CalcAvgResourceSize() * 200;
3494 config.UsedItemCountMax = 160;
3495 config.TotalItemCount = config.UsedItemCountMax * 10;
3496 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3497
3498 g_MemoryAliasingWarningEnabled = false;
3499 PoolTestResult result = {};
3500 TestPool_Benchmark(result, config);
3501 g_MemoryAliasingWarningEnabled = true;
3502
3503 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
3504}
3505
Adam Sawickib8333fb2018-03-13 16:15:53 +01003506static const char* CODE_DESCRIPTION = "Foo";
3507
3508static void PerformMainTests(FILE* file)
3509{
3510 uint32_t repeatCount = 1;
3511 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3512
3513 Config config{};
3514 config.RandSeed = 65735476;
3515 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3516 config.FreeOrder = FREE_ORDER::FORWARD;
3517
3518 size_t threadCountCount = 1;
3519 switch(ConfigType)
3520 {
3521 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3522 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3523 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
3524 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
3525 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
3526 default: assert(0);
3527 }
Adam Sawicki0667e332018-08-24 17:26:44 +02003528
3529 size_t strategyCount = 0;
3530 switch(ConfigType)
3531 {
3532 case CONFIG_TYPE_MINIMUM: strategyCount = 1; break;
3533 case CONFIG_TYPE_SMALL: strategyCount = 1; break;
3534 case CONFIG_TYPE_AVERAGE: strategyCount = 2; break;
3535 case CONFIG_TYPE_LARGE: strategyCount = 2; break;
3536 case CONFIG_TYPE_MAXIMUM: strategyCount = 3; break;
3537 default: assert(0);
3538 }
3539
Adam Sawickib8333fb2018-03-13 16:15:53 +01003540 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3541 {
3542 std::string desc1;
3543
3544 switch(threadCountIndex)
3545 {
3546 case 0:
3547 desc1 += "1_thread";
3548 config.ThreadCount = 1;
3549 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3550 break;
3551 case 1:
3552 desc1 += "16_threads+0%_common";
3553 config.ThreadCount = 16;
3554 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3555 break;
3556 case 2:
3557 desc1 += "16_threads+50%_common";
3558 config.ThreadCount = 16;
3559 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3560 break;
3561 case 3:
3562 desc1 += "16_threads+100%_common";
3563 config.ThreadCount = 16;
3564 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3565 break;
3566 case 4:
3567 desc1 += "2_threads+0%_common";
3568 config.ThreadCount = 2;
3569 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3570 break;
3571 case 5:
3572 desc1 += "2_threads+50%_common";
3573 config.ThreadCount = 2;
3574 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3575 break;
3576 case 6:
3577 desc1 += "2_threads+100%_common";
3578 config.ThreadCount = 2;
3579 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3580 break;
3581 default:
3582 assert(0);
3583 }
3584
3585 // 0 = buffers, 1 = images, 2 = buffers and images
3586 size_t buffersVsImagesCount = 2;
3587 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3588 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3589 {
3590 std::string desc2 = desc1;
3591 switch(buffersVsImagesIndex)
3592 {
3593 case 0: desc2 += " Buffers"; break;
3594 case 1: desc2 += " Images"; break;
3595 case 2: desc2 += " Buffers+Images"; break;
3596 default: assert(0);
3597 }
3598
3599 // 0 = small, 1 = large, 2 = small and large
3600 size_t smallVsLargeCount = 2;
3601 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3602 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3603 {
3604 std::string desc3 = desc2;
3605 switch(smallVsLargeIndex)
3606 {
3607 case 0: desc3 += " Small"; break;
3608 case 1: desc3 += " Large"; break;
3609 case 2: desc3 += " Small+Large"; break;
3610 default: assert(0);
3611 }
3612
3613 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3614 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3615 else
3616 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
3617
3618 // 0 = varying sizes min...max, 1 = set of constant sizes
3619 size_t constantSizesCount = 1;
3620 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3621 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3622 {
3623 std::string desc4 = desc3;
3624 switch(constantSizesIndex)
3625 {
3626 case 0: desc4 += " Varying_sizes"; break;
3627 case 1: desc4 += " Constant_sizes"; break;
3628 default: assert(0);
3629 }
3630
3631 config.AllocationSizes.clear();
3632 // Buffers present
3633 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3634 {
3635 // Small
3636 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3637 {
3638 // Varying size
3639 if(constantSizesIndex == 0)
3640 config.AllocationSizes.push_back({4, 16, 1024});
3641 // Constant sizes
3642 else
3643 {
3644 config.AllocationSizes.push_back({1, 16, 16});
3645 config.AllocationSizes.push_back({1, 64, 64});
3646 config.AllocationSizes.push_back({1, 256, 256});
3647 config.AllocationSizes.push_back({1, 1024, 1024});
3648 }
3649 }
3650 // Large
3651 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3652 {
3653 // Varying size
3654 if(constantSizesIndex == 0)
3655 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3656 // Constant sizes
3657 else
3658 {
3659 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3660 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3661 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3662 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3663 }
3664 }
3665 }
3666 // Images present
3667 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3668 {
3669 // Small
3670 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3671 {
3672 // Varying size
3673 if(constantSizesIndex == 0)
3674 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3675 // Constant sizes
3676 else
3677 {
3678 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3679 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3680 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3681 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3682 }
3683 }
3684 // Large
3685 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3686 {
3687 // Varying size
3688 if(constantSizesIndex == 0)
3689 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3690 // Constant sizes
3691 else
3692 {
3693 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3694 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3695 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3696 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3697 }
3698 }
3699 }
3700
3701 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
3702 size_t beginBytesToAllocateCount = 1;
3703 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
3704 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
3705 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
3706 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
3707 {
3708 std::string desc5 = desc4;
3709
3710 switch(beginBytesToAllocateIndex)
3711 {
3712 case 0:
3713 desc5 += " Allocate_100%";
3714 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
3715 config.AdditionalOperationCount = 0;
3716 break;
3717 case 1:
3718 desc5 += " Allocate_50%+Operations";
3719 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
3720 config.AdditionalOperationCount = 1024;
3721 break;
3722 case 2:
3723 desc5 += " Allocate_5%+Operations";
3724 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3725 config.AdditionalOperationCount = 1024;
3726 break;
3727 case 3:
3728 desc5 += " Allocate_95%+Operations";
3729 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
3730 config.AdditionalOperationCount = 1024;
3731 break;
3732 default:
3733 assert(0);
3734 }
3735
Adam Sawicki0667e332018-08-24 17:26:44 +02003736 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01003737 {
Adam Sawicki0667e332018-08-24 17:26:44 +02003738 std::string desc6 = desc5;
3739 switch(strategyIndex)
3740 {
3741 case 0:
3742 desc6 += " BestFit";
3743 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
3744 break;
3745 case 1:
3746 desc6 += " WorstFit";
3747 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
3748 break;
3749 case 2:
3750 desc6 += " FirstFit";
3751 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
3752 break;
3753 default:
3754 assert(0);
3755 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01003756
Adam Sawicki0667e332018-08-24 17:26:44 +02003757 const char* testDescription = desc6.c_str();
3758
3759 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3760 {
3761 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3762
3763 Result result{};
3764 VkResult res = MainTest(result, config);
3765 assert(res == VK_SUCCESS);
3766 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3767 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01003768 }
3769 }
3770 }
3771 }
3772 }
3773 }
3774}
3775
3776static void PerformPoolTests(FILE* file)
3777{
3778 const size_t AVG_RESOURCES_PER_POOL = 300;
3779
3780 uint32_t repeatCount = 1;
3781 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3782
3783 PoolTestConfig config{};
3784 config.RandSeed = 2346343;
3785 config.FrameCount = 200;
3786 config.ItemsToMakeUnusedPercent = 2;
3787
3788 size_t threadCountCount = 1;
3789 switch(ConfigType)
3790 {
3791 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3792 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3793 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
3794 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
3795 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
3796 default: assert(0);
3797 }
3798 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3799 {
3800 std::string desc1;
3801
3802 switch(threadCountIndex)
3803 {
3804 case 0:
3805 desc1 += "1_thread";
3806 config.ThreadCount = 1;
3807 break;
3808 case 1:
3809 desc1 += "16_threads";
3810 config.ThreadCount = 16;
3811 break;
3812 case 2:
3813 desc1 += "2_threads";
3814 config.ThreadCount = 2;
3815 break;
3816 default:
3817 assert(0);
3818 }
3819
3820 // 0 = buffers, 1 = images, 2 = buffers and images
3821 size_t buffersVsImagesCount = 2;
3822 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3823 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3824 {
3825 std::string desc2 = desc1;
3826 switch(buffersVsImagesIndex)
3827 {
3828 case 0: desc2 += " Buffers"; break;
3829 case 1: desc2 += " Images"; break;
3830 case 2: desc2 += " Buffers+Images"; break;
3831 default: assert(0);
3832 }
3833
3834 // 0 = small, 1 = large, 2 = small and large
3835 size_t smallVsLargeCount = 2;
3836 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3837 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3838 {
3839 std::string desc3 = desc2;
3840 switch(smallVsLargeIndex)
3841 {
3842 case 0: desc3 += " Small"; break;
3843 case 1: desc3 += " Large"; break;
3844 case 2: desc3 += " Small+Large"; break;
3845 default: assert(0);
3846 }
3847
3848 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3849 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
3850 else
3851 config.PoolSize = 4ull * 1024 * 1024;
3852
3853 // 0 = varying sizes min...max, 1 = set of constant sizes
3854 size_t constantSizesCount = 1;
3855 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3856 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3857 {
3858 std::string desc4 = desc3;
3859 switch(constantSizesIndex)
3860 {
3861 case 0: desc4 += " Varying_sizes"; break;
3862 case 1: desc4 += " Constant_sizes"; break;
3863 default: assert(0);
3864 }
3865
3866 config.AllocationSizes.clear();
3867 // Buffers present
3868 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3869 {
3870 // Small
3871 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3872 {
3873 // Varying size
3874 if(constantSizesIndex == 0)
3875 config.AllocationSizes.push_back({4, 16, 1024});
3876 // Constant sizes
3877 else
3878 {
3879 config.AllocationSizes.push_back({1, 16, 16});
3880 config.AllocationSizes.push_back({1, 64, 64});
3881 config.AllocationSizes.push_back({1, 256, 256});
3882 config.AllocationSizes.push_back({1, 1024, 1024});
3883 }
3884 }
3885 // Large
3886 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3887 {
3888 // Varying size
3889 if(constantSizesIndex == 0)
3890 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3891 // Constant sizes
3892 else
3893 {
3894 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3895 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3896 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3897 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3898 }
3899 }
3900 }
3901 // Images present
3902 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3903 {
3904 // Small
3905 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3906 {
3907 // Varying size
3908 if(constantSizesIndex == 0)
3909 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3910 // Constant sizes
3911 else
3912 {
3913 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3914 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3915 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3916 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3917 }
3918 }
3919 // Large
3920 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3921 {
3922 // Varying size
3923 if(constantSizesIndex == 0)
3924 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3925 // Constant sizes
3926 else
3927 {
3928 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3929 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3930 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3931 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3932 }
3933 }
3934 }
3935
3936 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
3937 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
3938
3939 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
3940 size_t subscriptionModeCount;
3941 switch(ConfigType)
3942 {
3943 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
3944 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
3945 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
3946 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
3947 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
3948 default: assert(0);
3949 }
3950 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
3951 {
3952 std::string desc5 = desc4;
3953
3954 switch(subscriptionModeIndex)
3955 {
3956 case 0:
3957 desc5 += " Subscription_66%";
3958 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
3959 break;
3960 case 1:
3961 desc5 += " Subscription_133%";
3962 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
3963 break;
3964 case 2:
3965 desc5 += " Subscription_100%";
3966 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
3967 break;
3968 case 3:
3969 desc5 += " Subscription_33%";
3970 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
3971 break;
3972 case 4:
3973 desc5 += " Subscription_166%";
3974 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
3975 break;
3976 default:
3977 assert(0);
3978 }
3979
3980 config.TotalItemCount = config.UsedItemCountMax * 5;
3981 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3982
3983 const char* testDescription = desc5.c_str();
3984
3985 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3986 {
3987 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3988
3989 PoolTestResult result{};
3990 g_MemoryAliasingWarningEnabled = false;
3991 TestPool_Benchmark(result, config);
3992 g_MemoryAliasingWarningEnabled = true;
3993 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3994 }
3995 }
3996 }
3997 }
3998 }
3999 }
4000}
4001
4002void Test()
4003{
4004 wprintf(L"TESTING:\n");
4005
Adam Sawicki70a683e2018-08-24 15:36:32 +02004006 if(false)
4007 {
4008 // # Temporarily insert custom tests here
4009TestLinearAllocator();
4010ManuallyTestLinearAllocator();
4011TestLinearAllocatorMultiBlock();
4012BenchmarkLinearAllocator();
4013 return;
4014 }
4015
Adam Sawickib8333fb2018-03-13 16:15:53 +01004016 // # Simple tests
4017
Adam Sawicki0667e332018-08-24 17:26:44 +02004018#if 0
Adam Sawickib8333fb2018-03-13 16:15:53 +01004019 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02004020#if VMA_DEBUG_MARGIN
4021 TestDebugMargin();
4022#else
4023 TestPool_SameSize();
4024 TestHeapSizeLimit();
4025#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02004026#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
4027 TestAllocationsInitialization();
4028#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01004029 TestMapping();
4030 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02004031 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02004032 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02004033 TestLinearAllocatorMultiBlock();
Adam Sawicki0a607132018-08-24 11:18:41 +02004034 BenchmarkLinearAllocator();
Adam Sawickib8333fb2018-03-13 16:15:53 +01004035 TestDefragmentationSimple();
4036 TestDefragmentationFull();
Adam Sawicki0667e332018-08-24 17:26:44 +02004037#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01004038
4039 // # Detailed tests
4040 FILE* file;
4041 fopen_s(&file, "Results.csv", "w");
4042 assert(file != NULL);
4043
4044 WriteMainTestResultHeader(file);
4045 PerformMainTests(file);
4046 //PerformCustomMainTest(file);
4047
Adam Sawicki0667e332018-08-24 17:26:44 +02004048#if 0
Adam Sawickib8333fb2018-03-13 16:15:53 +01004049 WritePoolTestResultHeader(file);
4050 PerformPoolTests(file);
Adam Sawicki0667e332018-08-24 17:26:44 +02004051#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01004052 //PerformCustomPoolTest(file);
4053
4054 fclose(file);
4055
4056 wprintf(L"Done.\n");
4057}
4058
Adam Sawickif1a793c2018-03-13 15:42:22 +01004059#endif // #ifdef _WIN32