blob: 1881ff562b07d714988d2962d4c0be60181a6105 [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 Sawickib8333fb2018-03-13 16:15:53 +010010enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
11
12struct AllocationSize
13{
14 uint32_t Probability;
15 VkDeviceSize BufferSizeMin, BufferSizeMax;
16 uint32_t ImageSizeMin, ImageSizeMax;
17};
18
19struct Config
20{
21 uint32_t RandSeed;
22 VkDeviceSize BeginBytesToAllocate;
23 uint32_t AdditionalOperationCount;
24 VkDeviceSize MaxBytesToAllocate;
25 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
26 std::vector<AllocationSize> AllocationSizes;
27 uint32_t ThreadCount;
28 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
29 FREE_ORDER FreeOrder;
30};
31
32struct Result
33{
34 duration TotalTime;
35 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
36 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
37 VkDeviceSize TotalMemoryAllocated;
38 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
39};
40
41void TestDefragmentationSimple();
42void TestDefragmentationFull();
43
44struct PoolTestConfig
45{
46 uint32_t RandSeed;
47 uint32_t ThreadCount;
48 VkDeviceSize PoolSize;
49 uint32_t FrameCount;
50 uint32_t TotalItemCount;
51 // Range for number of items used in each frame.
52 uint32_t UsedItemCountMin, UsedItemCountMax;
53 // Percent of items to make unused, and possibly make some others used in each frame.
54 uint32_t ItemsToMakeUnusedPercent;
55 std::vector<AllocationSize> AllocationSizes;
56
57 VkDeviceSize CalcAvgResourceSize() const
58 {
59 uint32_t probabilitySum = 0;
60 VkDeviceSize sizeSum = 0;
61 for(size_t i = 0; i < AllocationSizes.size(); ++i)
62 {
63 const AllocationSize& allocSize = AllocationSizes[i];
64 if(allocSize.BufferSizeMax > 0)
65 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
66 else
67 {
68 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
69 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
70 }
71 probabilitySum += allocSize.Probability;
72 }
73 return sizeSum / probabilitySum;
74 }
75
76 bool UsesBuffers() const
77 {
78 for(size_t i = 0; i < AllocationSizes.size(); ++i)
79 if(AllocationSizes[i].BufferSizeMax > 0)
80 return true;
81 return false;
82 }
83
84 bool UsesImages() const
85 {
86 for(size_t i = 0; i < AllocationSizes.size(); ++i)
87 if(AllocationSizes[i].ImageSizeMax > 0)
88 return true;
89 return false;
90 }
91};
92
93struct PoolTestResult
94{
95 duration TotalTime;
96 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
97 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
98 size_t LostAllocationCount, LostAllocationTotalSize;
99 size_t FailedAllocationCount, FailedAllocationTotalSize;
100};
101
102static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
103
104struct BufferInfo
105{
106 VkBuffer Buffer = VK_NULL_HANDLE;
107 VmaAllocation Allocation = VK_NULL_HANDLE;
108};
109
110static void InitResult(Result& outResult)
111{
112 outResult.TotalTime = duration::zero();
113 outResult.AllocationTimeMin = duration::max();
114 outResult.AllocationTimeAvg = duration::zero();
115 outResult.AllocationTimeMax = duration::min();
116 outResult.DeallocationTimeMin = duration::max();
117 outResult.DeallocationTimeAvg = duration::zero();
118 outResult.DeallocationTimeMax = duration::min();
119 outResult.TotalMemoryAllocated = 0;
120 outResult.FreeRangeSizeAvg = 0;
121 outResult.FreeRangeSizeMax = 0;
122}
123
124class TimeRegisterObj
125{
126public:
127 TimeRegisterObj(duration& min, duration& sum, duration& max) :
128 m_Min(min),
129 m_Sum(sum),
130 m_Max(max),
131 m_TimeBeg(std::chrono::high_resolution_clock::now())
132 {
133 }
134
135 ~TimeRegisterObj()
136 {
137 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
138 m_Sum += d;
139 if(d < m_Min) m_Min = d;
140 if(d > m_Max) m_Max = d;
141 }
142
143private:
144 duration& m_Min;
145 duration& m_Sum;
146 duration& m_Max;
147 time_point m_TimeBeg;
148};
149
150struct PoolTestThreadResult
151{
152 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
153 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
154 size_t AllocationCount, DeallocationCount;
155 size_t LostAllocationCount, LostAllocationTotalSize;
156 size_t FailedAllocationCount, FailedAllocationTotalSize;
157};
158
159class AllocationTimeRegisterObj : public TimeRegisterObj
160{
161public:
162 AllocationTimeRegisterObj(Result& result) :
163 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
164 {
165 }
166};
167
168class DeallocationTimeRegisterObj : public TimeRegisterObj
169{
170public:
171 DeallocationTimeRegisterObj(Result& result) :
172 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
173 {
174 }
175};
176
177class PoolAllocationTimeRegisterObj : public TimeRegisterObj
178{
179public:
180 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
181 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
182 {
183 }
184};
185
186class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
187{
188public:
189 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
190 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
191 {
192 }
193};
194
195VkResult MainTest(Result& outResult, const Config& config)
196{
197 assert(config.ThreadCount > 0);
198
199 InitResult(outResult);
200
201 RandomNumberGenerator mainRand{config.RandSeed};
202
203 time_point timeBeg = std::chrono::high_resolution_clock::now();
204
205 std::atomic<size_t> allocationCount = 0;
206 VkResult res = VK_SUCCESS;
207
208 uint32_t memUsageProbabilitySum =
209 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
210 config.MemUsageProbability[2] + config.MemUsageProbability[3];
211 assert(memUsageProbabilitySum > 0);
212
213 uint32_t allocationSizeProbabilitySum = std::accumulate(
214 config.AllocationSizes.begin(),
215 config.AllocationSizes.end(),
216 0u,
217 [](uint32_t sum, const AllocationSize& allocSize) {
218 return sum + allocSize.Probability;
219 });
220
221 struct Allocation
222 {
223 VkBuffer Buffer;
224 VkImage Image;
225 VmaAllocation Alloc;
226 };
227
228 std::vector<Allocation> commonAllocations;
229 std::mutex commonAllocationsMutex;
230
231 auto Allocate = [&](
232 VkDeviceSize bufferSize,
233 const VkExtent2D imageExtent,
234 RandomNumberGenerator& localRand,
235 VkDeviceSize& totalAllocatedBytes,
236 std::vector<Allocation>& allocations) -> VkResult
237 {
238 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
239
240 uint32_t memUsageIndex = 0;
241 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
242 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
243 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
244
245 VmaAllocationCreateInfo memReq = {};
246 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
247
248 Allocation allocation = {};
249 VmaAllocationInfo allocationInfo;
250
251 // Buffer
252 if(bufferSize > 0)
253 {
254 assert(imageExtent.width == 0);
255 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
256 bufferInfo.size = bufferSize;
257 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
258
259 {
260 AllocationTimeRegisterObj timeRegisterObj{outResult};
261 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
262 }
263 }
264 // Image
265 else
266 {
267 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
268 imageInfo.imageType = VK_IMAGE_TYPE_2D;
269 imageInfo.extent.width = imageExtent.width;
270 imageInfo.extent.height = imageExtent.height;
271 imageInfo.extent.depth = 1;
272 imageInfo.mipLevels = 1;
273 imageInfo.arrayLayers = 1;
274 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
275 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
276 VK_IMAGE_TILING_OPTIMAL :
277 VK_IMAGE_TILING_LINEAR;
278 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
279 switch(memReq.usage)
280 {
281 case VMA_MEMORY_USAGE_GPU_ONLY:
282 switch(localRand.Generate() % 3)
283 {
284 case 0:
285 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
286 break;
287 case 1:
288 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
289 break;
290 case 2:
291 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
292 break;
293 }
294 break;
295 case VMA_MEMORY_USAGE_CPU_ONLY:
296 case VMA_MEMORY_USAGE_CPU_TO_GPU:
297 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
298 break;
299 case VMA_MEMORY_USAGE_GPU_TO_CPU:
300 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
301 break;
302 }
303 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
304 imageInfo.flags = 0;
305
306 {
307 AllocationTimeRegisterObj timeRegisterObj{outResult};
308 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
309 }
310 }
311
312 if(res == VK_SUCCESS)
313 {
314 ++allocationCount;
315 totalAllocatedBytes += allocationInfo.size;
316 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
317 if(useCommonAllocations)
318 {
319 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
320 commonAllocations.push_back(allocation);
321 }
322 else
323 allocations.push_back(allocation);
324 }
325 else
326 {
327 assert(0);
328 }
329 return res;
330 };
331
332 auto GetNextAllocationSize = [&](
333 VkDeviceSize& outBufSize,
334 VkExtent2D& outImageSize,
335 RandomNumberGenerator& localRand)
336 {
337 outBufSize = 0;
338 outImageSize = {0, 0};
339
340 uint32_t allocSizeIndex = 0;
341 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
342 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
343 r -= config.AllocationSizes[allocSizeIndex++].Probability;
344
345 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
346 if(allocSize.BufferSizeMax > 0)
347 {
348 assert(allocSize.ImageSizeMax == 0);
349 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
350 outBufSize = allocSize.BufferSizeMin;
351 else
352 {
353 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
354 outBufSize = outBufSize / 16 * 16;
355 }
356 }
357 else
358 {
359 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
360 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
361 else
362 {
363 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
364 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
365 }
366 }
367 };
368
369 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
370 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
371
372 auto ThreadProc = [&](uint32_t randSeed) -> void
373 {
374 RandomNumberGenerator threadRand(randSeed);
375 VkDeviceSize threadTotalAllocatedBytes = 0;
376 std::vector<Allocation> threadAllocations;
377 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
378 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
379 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
380
381 // BEGIN ALLOCATIONS
382 for(;;)
383 {
384 VkDeviceSize bufferSize = 0;
385 VkExtent2D imageExtent = {};
386 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
387 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
388 threadBeginBytesToAllocate)
389 {
390 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
391 break;
392 }
393 else
394 break;
395 }
396
397 // ADDITIONAL ALLOCATIONS AND FREES
398 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
399 {
400 VkDeviceSize bufferSize = 0;
401 VkExtent2D imageExtent = {};
402 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
403
404 // true = allocate, false = free
405 bool allocate = threadRand.Generate() % 2 != 0;
406
407 if(allocate)
408 {
409 if(threadTotalAllocatedBytes +
410 bufferSize +
411 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
412 threadMaxBytesToAllocate)
413 {
414 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
415 break;
416 }
417 }
418 else
419 {
420 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
421 if(useCommonAllocations)
422 {
423 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
424 if(!commonAllocations.empty())
425 {
426 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
427 VmaAllocationInfo allocationInfo;
428 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
429 if(threadTotalAllocatedBytes >= allocationInfo.size)
430 {
431 DeallocationTimeRegisterObj timeRegisterObj{outResult};
432 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
433 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
434 else
435 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
436 threadTotalAllocatedBytes -= allocationInfo.size;
437 commonAllocations.erase(commonAllocations.begin() + indexToFree);
438 }
439 }
440 }
441 else
442 {
443 if(!threadAllocations.empty())
444 {
445 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
446 VmaAllocationInfo allocationInfo;
447 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
448 if(threadTotalAllocatedBytes >= allocationInfo.size)
449 {
450 DeallocationTimeRegisterObj timeRegisterObj{outResult};
451 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
452 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
453 else
454 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
455 threadTotalAllocatedBytes -= allocationInfo.size;
456 threadAllocations.erase(threadAllocations.begin() + indexToFree);
457 }
458 }
459 }
460 }
461 }
462
463 ++numThreadsReachedMaxAllocations;
464
465 WaitForSingleObject(threadsFinishEvent, INFINITE);
466
467 // DEALLOCATION
468 while(!threadAllocations.empty())
469 {
470 size_t indexToFree = 0;
471 switch(config.FreeOrder)
472 {
473 case FREE_ORDER::FORWARD:
474 indexToFree = 0;
475 break;
476 case FREE_ORDER::BACKWARD:
477 indexToFree = threadAllocations.size() - 1;
478 break;
479 case FREE_ORDER::RANDOM:
480 indexToFree = mainRand.Generate() % threadAllocations.size();
481 break;
482 }
483
484 {
485 DeallocationTimeRegisterObj timeRegisterObj{outResult};
486 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
487 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
488 else
489 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
490 }
491 threadAllocations.erase(threadAllocations.begin() + indexToFree);
492 }
493 };
494
495 uint32_t threadRandSeed = mainRand.Generate();
496 std::vector<std::thread> bkgThreads;
497 for(size_t i = 0; i < config.ThreadCount; ++i)
498 {
499 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
500 }
501
502 // Wait for threads reached max allocations
503 while(numThreadsReachedMaxAllocations < config.ThreadCount)
504 Sleep(0);
505
506 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
507 VmaStats vmaStats = {};
508 vmaCalculateStats(g_hAllocator, &vmaStats);
509 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
510 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
511 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
512
513 // Signal threads to deallocate
514 SetEvent(threadsFinishEvent);
515
516 // Wait for threads finished
517 for(size_t i = 0; i < bkgThreads.size(); ++i)
518 bkgThreads[i].join();
519 bkgThreads.clear();
520
521 CloseHandle(threadsFinishEvent);
522
523 // Deallocate remaining common resources
524 while(!commonAllocations.empty())
525 {
526 size_t indexToFree = 0;
527 switch(config.FreeOrder)
528 {
529 case FREE_ORDER::FORWARD:
530 indexToFree = 0;
531 break;
532 case FREE_ORDER::BACKWARD:
533 indexToFree = commonAllocations.size() - 1;
534 break;
535 case FREE_ORDER::RANDOM:
536 indexToFree = mainRand.Generate() % commonAllocations.size();
537 break;
538 }
539
540 {
541 DeallocationTimeRegisterObj timeRegisterObj{outResult};
542 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
543 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
544 else
545 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
546 }
547 commonAllocations.erase(commonAllocations.begin() + indexToFree);
548 }
549
550 if(allocationCount)
551 {
552 outResult.AllocationTimeAvg /= allocationCount;
553 outResult.DeallocationTimeAvg /= allocationCount;
554 }
555
556 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
557
558 return res;
559}
560
561static void SaveAllocatorStatsToFile(VmaAllocator alloc, const wchar_t* filePath)
562{
563 char* stats;
564 vmaBuildStatsString(alloc, &stats, VK_TRUE);
565 SaveFile(filePath, stats, strlen(stats));
566 vmaFreeStatsString(alloc, stats);
567}
568
569struct AllocInfo
570{
571 VmaAllocation m_Allocation;
572 VkBuffer m_Buffer;
573 VkImage m_Image;
574 uint32_t m_StartValue;
575 union
576 {
577 VkBufferCreateInfo m_BufferInfo;
578 VkImageCreateInfo m_ImageInfo;
579 };
580};
581
582static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
583{
584 outMemReq = {};
585 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
586 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
587}
588
589static void CreateBuffer(
590 VmaPool pool,
591 const VkBufferCreateInfo& bufCreateInfo,
592 bool persistentlyMapped,
593 AllocInfo& outAllocInfo)
594{
595 outAllocInfo = {};
596 outAllocInfo.m_BufferInfo = bufCreateInfo;
597
598 VmaAllocationCreateInfo allocCreateInfo = {};
599 allocCreateInfo.pool = pool;
600 if(persistentlyMapped)
601 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
602
603 VmaAllocationInfo vmaAllocInfo = {};
604 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
605
606 // Setup StartValue and fill.
607 {
608 outAllocInfo.m_StartValue = (uint32_t)rand();
609 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
610 assert((data != nullptr) == persistentlyMapped);
611 if(!persistentlyMapped)
612 {
613 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
614 }
615
616 uint32_t value = outAllocInfo.m_StartValue;
617 assert(bufCreateInfo.size % 4 == 0);
618 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
619 data[i] = value++;
620
621 if(!persistentlyMapped)
622 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
623 }
624}
625
626static void CreateAllocation(AllocInfo& outAllocation, VmaAllocator allocator)
627{
628 outAllocation.m_Allocation = nullptr;
629 outAllocation.m_Buffer = nullptr;
630 outAllocation.m_Image = nullptr;
631 outAllocation.m_StartValue = (uint32_t)rand();
632
633 VmaAllocationCreateInfo vmaMemReq;
634 GetMemReq(vmaMemReq);
635
636 VmaAllocationInfo allocInfo;
637
638 const bool isBuffer = true;//(rand() & 0x1) != 0;
639 const bool isLarge = (rand() % 16) == 0;
640 if(isBuffer)
641 {
642 const uint32_t bufferSize = isLarge ?
643 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
644 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
645
646 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
647 bufferInfo.size = bufferSize;
648 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
649
650 VkResult res = vmaCreateBuffer(allocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
651 outAllocation.m_BufferInfo = bufferInfo;
652 assert(res == VK_SUCCESS);
653 }
654 else
655 {
656 const uint32_t imageSizeX = isLarge ?
657 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
658 rand() % 1024 + 1; // 1 ... 1024
659 const uint32_t imageSizeY = isLarge ?
660 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
661 rand() % 1024 + 1; // 1 ... 1024
662
663 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
664 imageInfo.imageType = VK_IMAGE_TYPE_2D;
665 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
666 imageInfo.extent.width = imageSizeX;
667 imageInfo.extent.height = imageSizeY;
668 imageInfo.extent.depth = 1;
669 imageInfo.mipLevels = 1;
670 imageInfo.arrayLayers = 1;
671 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
672 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
673 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
674 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
675
676 VkResult res = vmaCreateImage(allocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
677 outAllocation.m_ImageInfo = imageInfo;
678 assert(res == VK_SUCCESS);
679 }
680
681 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
682 if(allocInfo.pMappedData == nullptr)
683 {
684 VkResult res = vmaMapMemory(allocator, outAllocation.m_Allocation, (void**)&data);
685 assert(res == VK_SUCCESS);
686 }
687
688 uint32_t value = outAllocation.m_StartValue;
689 assert(allocInfo.size % 4 == 0);
690 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
691 data[i] = value++;
692
693 if(allocInfo.pMappedData == nullptr)
694 vmaUnmapMemory(allocator, outAllocation.m_Allocation);
695}
696
697static void DestroyAllocation(const AllocInfo& allocation)
698{
699 if(allocation.m_Buffer)
700 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
701 else
702 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
703}
704
705static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
706{
707 for(size_t i = allocations.size(); i--; )
708 DestroyAllocation(allocations[i]);
709 allocations.clear();
710}
711
712static void ValidateAllocationData(const AllocInfo& allocation)
713{
714 VmaAllocationInfo allocInfo;
715 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
716
717 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
718 if(allocInfo.pMappedData == nullptr)
719 {
720 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
721 assert(res == VK_SUCCESS);
722 }
723
724 uint32_t value = allocation.m_StartValue;
725 bool ok = true;
726 size_t i;
727 assert(allocInfo.size % 4 == 0);
728 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
729 {
730 if(data[i] != value++)
731 {
732 ok = false;
733 break;
734 }
735 }
736 assert(ok);
737
738 if(allocInfo.pMappedData == nullptr)
739 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
740}
741
742static void RecreateAllocationResource(AllocInfo& allocation)
743{
744 VmaAllocationInfo allocInfo;
745 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
746
747 if(allocation.m_Buffer)
748 {
749 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, nullptr);
750
751 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, nullptr, &allocation.m_Buffer);
752 assert(res == VK_SUCCESS);
753
754 // Just to silence validation layer warnings.
755 VkMemoryRequirements vkMemReq;
756 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
757 assert(vkMemReq.size == allocation.m_BufferInfo.size);
758
759 res = vkBindBufferMemory(g_hDevice, allocation.m_Buffer, allocInfo.deviceMemory, allocInfo.offset);
760 assert(res == VK_SUCCESS);
761 }
762 else
763 {
764 vkDestroyImage(g_hDevice, allocation.m_Image, nullptr);
765
766 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, nullptr, &allocation.m_Image);
767 assert(res == VK_SUCCESS);
768
769 // Just to silence validation layer warnings.
770 VkMemoryRequirements vkMemReq;
771 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
772
773 res = vkBindImageMemory(g_hDevice, allocation.m_Image, allocInfo.deviceMemory, allocInfo.offset);
774 assert(res == VK_SUCCESS);
775 }
776}
777
778static void Defragment(AllocInfo* allocs, size_t allocCount,
779 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
780 VmaDefragmentationStats* defragmentationStats = nullptr)
781{
782 std::vector<VmaAllocation> vmaAllocs(allocCount);
783 for(size_t i = 0; i < allocCount; ++i)
784 vmaAllocs[i] = allocs[i].m_Allocation;
785
786 std::vector<VkBool32> allocChanged(allocCount);
787
788 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
789 defragmentationInfo, defragmentationStats) );
790
791 for(size_t i = 0; i < allocCount; ++i)
792 {
793 if(allocChanged[i])
794 {
795 RecreateAllocationResource(allocs[i]);
796 }
797 }
798}
799
800static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
801{
802 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
803 ValidateAllocationData(allocInfo);
804 });
805}
806
807void TestDefragmentationSimple()
808{
809 wprintf(L"Test defragmentation simple\n");
810
811 RandomNumberGenerator rand(667);
812
813 const VkDeviceSize BUF_SIZE = 0x10000;
814 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
815
816 const VkDeviceSize MIN_BUF_SIZE = 32;
817 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
818 auto RandomBufSize = [&]() -> VkDeviceSize {
819 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
820 };
821
822 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
823 bufCreateInfo.size = BUF_SIZE;
824 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
825
826 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
827 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
828
829 uint32_t memTypeIndex = UINT32_MAX;
830 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
831
832 VmaPoolCreateInfo poolCreateInfo = {};
833 poolCreateInfo.blockSize = BLOCK_SIZE;
834 poolCreateInfo.memoryTypeIndex = memTypeIndex;
835
836 VmaPool pool;
837 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
838
839 std::vector<AllocInfo> allocations;
840
841 // persistentlyMappedOption = 0 - not persistently mapped.
842 // persistentlyMappedOption = 1 - persistently mapped.
843 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
844 {
845 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
846 const bool persistentlyMapped = persistentlyMappedOption != 0;
847
848 // # Test 1
849 // Buffers of fixed size.
850 // Fill 2 blocks. Remove odd buffers. Defragment everything.
851 // Expected result: at least 1 block freed.
852 {
853 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
854 {
855 AllocInfo allocInfo;
856 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
857 allocations.push_back(allocInfo);
858 }
859
860 for(size_t i = 1; i < allocations.size(); ++i)
861 {
862 DestroyAllocation(allocations[i]);
863 allocations.erase(allocations.begin() + i);
864 }
865
866 VmaDefragmentationStats defragStats;
867 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
868 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
869 assert(defragStats.deviceMemoryBlocksFreed >= 1);
870
871 ValidateAllocationsData(allocations.data(), allocations.size());
872
873 DestroyAllAllocations(allocations);
874 }
875
876 // # Test 2
877 // Buffers of fixed size.
878 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
879 // Expected result: Each of 4 interations makes some progress.
880 {
881 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
882 {
883 AllocInfo allocInfo;
884 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
885 allocations.push_back(allocInfo);
886 }
887
888 for(size_t i = 1; i < allocations.size(); ++i)
889 {
890 DestroyAllocation(allocations[i]);
891 allocations.erase(allocations.begin() + i);
892 }
893
894 VmaDefragmentationInfo defragInfo = {};
895 defragInfo.maxAllocationsToMove = 1;
896 defragInfo.maxBytesToMove = BUF_SIZE;
897
898 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
899 {
900 VmaDefragmentationStats defragStats;
901 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
902 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
903 }
904
905 ValidateAllocationsData(allocations.data(), allocations.size());
906
907 DestroyAllAllocations(allocations);
908 }
909
910 // # Test 3
911 // Buffers of variable size.
912 // Create a number of buffers. Remove some percent of them.
913 // Defragment while having some percent of them unmovable.
914 // Expected result: Just simple validation.
915 {
916 for(size_t i = 0; i < 100; ++i)
917 {
918 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
919 localBufCreateInfo.size = RandomBufSize();
920
921 AllocInfo allocInfo;
922 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
923 allocations.push_back(allocInfo);
924 }
925
926 const uint32_t percentToDelete = 60;
927 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
928 for(size_t i = 0; i < numberToDelete; ++i)
929 {
930 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
931 DestroyAllocation(allocations[indexToDelete]);
932 allocations.erase(allocations.begin() + indexToDelete);
933 }
934
935 // Non-movable allocations will be at the beginning of allocations array.
936 const uint32_t percentNonMovable = 20;
937 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
938 for(size_t i = 0; i < numberNonMovable; ++i)
939 {
940 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
941 if(indexNonMovable != i)
942 std::swap(allocations[i], allocations[indexNonMovable]);
943 }
944
945 VmaDefragmentationStats defragStats;
946 Defragment(
947 allocations.data() + numberNonMovable,
948 allocations.size() - numberNonMovable,
949 nullptr, &defragStats);
950
951 ValidateAllocationsData(allocations.data(), allocations.size());
952
953 DestroyAllAllocations(allocations);
954 }
955 }
956
957 vmaDestroyPool(g_hAllocator, pool);
958}
959
960void TestDefragmentationFull()
961{
962 std::vector<AllocInfo> allocations;
963
964 // Create initial allocations.
965 for(size_t i = 0; i < 400; ++i)
966 {
967 AllocInfo allocation;
968 CreateAllocation(allocation, g_hAllocator);
969 allocations.push_back(allocation);
970 }
971
972 // Delete random allocations
973 const size_t allocationsToDeletePercent = 80;
974 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
975 for(size_t i = 0; i < allocationsToDelete; ++i)
976 {
977 size_t index = (size_t)rand() % allocations.size();
978 DestroyAllocation(allocations[index]);
979 allocations.erase(allocations.begin() + index);
980 }
981
982 for(size_t i = 0; i < allocations.size(); ++i)
983 ValidateAllocationData(allocations[i]);
984
985 SaveAllocatorStatsToFile(g_hAllocator, L"Before.csv");
986
987 {
988 std::vector<VmaAllocation> vmaAllocations(allocations.size());
989 for(size_t i = 0; i < allocations.size(); ++i)
990 vmaAllocations[i] = allocations[i].m_Allocation;
991
992 const size_t nonMovablePercent = 0;
993 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
994 for(size_t i = 0; i < nonMovableCount; ++i)
995 {
996 size_t index = (size_t)rand() % vmaAllocations.size();
997 vmaAllocations.erase(vmaAllocations.begin() + index);
998 }
999
1000 const uint32_t defragCount = 1;
1001 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1002 {
1003 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1004
1005 VmaDefragmentationInfo defragmentationInfo;
1006 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1007 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1008
1009 wprintf(L"Defragmentation #%u\n", defragIndex);
1010
1011 time_point begTime = std::chrono::high_resolution_clock::now();
1012
1013 VmaDefragmentationStats stats;
1014 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
1015 assert(res >= 0);
1016
1017 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1018
1019 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1020 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1021 wprintf(L"Time: %.2f s\n", defragmentDuration);
1022
1023 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1024 {
1025 if(allocationsChanged[i])
1026 {
1027 RecreateAllocationResource(allocations[i]);
1028 }
1029 }
1030
1031 for(size_t i = 0; i < allocations.size(); ++i)
1032 ValidateAllocationData(allocations[i]);
1033
1034 wchar_t fileName[MAX_PATH];
1035 swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
1036 SaveAllocatorStatsToFile(g_hAllocator, fileName);
1037 }
1038 }
1039
1040 // Destroy all remaining allocations.
1041 DestroyAllAllocations(allocations);
1042}
1043
1044static void TestUserData()
1045{
1046 VkResult res;
1047
1048 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1049 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1050 bufCreateInfo.size = 0x10000;
1051
1052 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
1053 {
1054 // Opaque pointer
1055 {
1056
1057 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
1058 void* pointerToSomething = &res;
1059
1060 VmaAllocationCreateInfo allocCreateInfo = {};
1061 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1062 allocCreateInfo.pUserData = numberAsPointer;
1063 if(testIndex == 1)
1064 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1065
1066 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1067 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1068 assert(res == VK_SUCCESS);
1069 assert(allocInfo.pUserData = numberAsPointer);
1070
1071 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1072 assert(allocInfo.pUserData == numberAsPointer);
1073
1074 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
1075 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1076 assert(allocInfo.pUserData == pointerToSomething);
1077
1078 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1079 }
1080
1081 // String
1082 {
1083 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
1084 const char* name2 = "2";
1085 const size_t name1Len = strlen(name1);
1086
1087 char* name1Buf = new char[name1Len + 1];
1088 strcpy_s(name1Buf, name1Len + 1, name1);
1089
1090 VmaAllocationCreateInfo allocCreateInfo = {};
1091 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1092 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1093 allocCreateInfo.pUserData = name1Buf;
1094 if(testIndex == 1)
1095 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1096
1097 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1098 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1099 assert(res == VK_SUCCESS);
1100 assert(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
1101 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1102
1103 delete[] name1Buf;
1104
1105 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1106 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1107
1108 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
1109 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1110 assert(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
1111
1112 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
1113 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1114 assert(allocInfo.pUserData == nullptr);
1115
1116 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1117 }
1118 }
1119}
1120
1121static void TestMemoryRequirements()
1122{
1123 VkResult res;
1124 VkBuffer buf;
1125 VmaAllocation alloc;
1126 VmaAllocationInfo allocInfo;
1127
1128 const VkPhysicalDeviceMemoryProperties* memProps;
1129 vmaGetMemoryProperties(g_hAllocator, &memProps);
1130
1131 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1132 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1133 bufInfo.size = 128;
1134
1135 VmaAllocationCreateInfo allocCreateInfo = {};
1136
1137 // No requirements.
1138 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1139 assert(res == VK_SUCCESS);
1140 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1141
1142 // Usage.
1143 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1144 allocCreateInfo.requiredFlags = 0;
1145 allocCreateInfo.preferredFlags = 0;
1146 allocCreateInfo.memoryTypeBits = UINT32_MAX;
1147
1148 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1149 assert(res == VK_SUCCESS);
1150 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1151 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1152
1153 // Required flags, preferred flags.
1154 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
1155 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1156 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1157 allocCreateInfo.memoryTypeBits = 0;
1158
1159 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1160 assert(res == VK_SUCCESS);
1161 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1162 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
1163 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1164
1165 // memoryTypeBits.
1166 const uint32_t memType = allocInfo.memoryType;
1167 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1168 allocCreateInfo.requiredFlags = 0;
1169 allocCreateInfo.preferredFlags = 0;
1170 allocCreateInfo.memoryTypeBits = 1u << memType;
1171
1172 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1173 assert(res == VK_SUCCESS);
1174 assert(allocInfo.memoryType == memType);
1175 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1176
1177}
1178
1179static void TestBasics()
1180{
1181 VkResult res;
1182
1183 TestMemoryRequirements();
1184
1185 // Lost allocation
1186 {
1187 VmaAllocation alloc = VK_NULL_HANDLE;
1188 vmaCreateLostAllocation(g_hAllocator, &alloc);
1189 assert(alloc != VK_NULL_HANDLE);
1190
1191 VmaAllocationInfo allocInfo;
1192 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1193 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1194 assert(allocInfo.size == 0);
1195
1196 vmaFreeMemory(g_hAllocator, alloc);
1197 }
1198
1199 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
1200 {
1201 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1202 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1203 bufCreateInfo.size = 128;
1204
1205 VmaAllocationCreateInfo allocCreateInfo = {};
1206 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1207 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1208
1209 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1210 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1211 assert(res == VK_SUCCESS);
1212
1213 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1214
1215 // Same with OWN_MEMORY.
1216 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1217
1218 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1219 assert(res == VK_SUCCESS);
1220
1221 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1222 }
1223
1224 TestUserData();
1225}
1226
1227void TestHeapSizeLimit()
1228{
1229 const VkDeviceSize HEAP_SIZE_LIMIT = 1ull * 1024 * 1024 * 1024; // 1 GB
1230 const VkDeviceSize BLOCK_SIZE = 128ull * 1024 * 1024; // 128 MB
1231
1232 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
1233 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
1234 {
1235 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
1236 }
1237
1238 VmaAllocatorCreateInfo allocatorCreateInfo = {};
1239 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
1240 allocatorCreateInfo.device = g_hDevice;
1241 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
1242
1243 VmaAllocator hAllocator;
1244 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
1245 assert(res == VK_SUCCESS);
1246
1247 struct Item
1248 {
1249 VkBuffer hBuf;
1250 VmaAllocation hAlloc;
1251 };
1252 std::vector<Item> items;
1253
1254 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1255 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1256
1257 // 1. Allocate two blocks of Own Memory, half the size of BLOCK_SIZE.
1258 VmaAllocationInfo ownAllocInfo;
1259 {
1260 VmaAllocationCreateInfo allocCreateInfo = {};
1261 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1262 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1263
1264 bufCreateInfo.size = BLOCK_SIZE / 2;
1265
1266 for(size_t i = 0; i < 2; ++i)
1267 {
1268 Item item;
1269 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &ownAllocInfo);
1270 assert(res == VK_SUCCESS);
1271 items.push_back(item);
1272 }
1273 }
1274
1275 // Create pool to make sure allocations must be out of this memory type.
1276 VmaPoolCreateInfo poolCreateInfo = {};
1277 poolCreateInfo.memoryTypeIndex = ownAllocInfo.memoryType;
1278 poolCreateInfo.blockSize = BLOCK_SIZE;
1279
1280 VmaPool hPool;
1281 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
1282 assert(res == VK_SUCCESS);
1283
1284 // 2. Allocate normal buffers from all the remaining memory.
1285 {
1286 VmaAllocationCreateInfo allocCreateInfo = {};
1287 allocCreateInfo.pool = hPool;
1288
1289 bufCreateInfo.size = BLOCK_SIZE / 2;
1290
1291 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
1292 for(size_t i = 0; i < bufCount; ++i)
1293 {
1294 Item item;
1295 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
1296 assert(res == VK_SUCCESS);
1297 items.push_back(item);
1298 }
1299 }
1300
1301 // 3. Allocation of one more (even small) buffer should fail.
1302 {
1303 VmaAllocationCreateInfo allocCreateInfo = {};
1304 allocCreateInfo.pool = hPool;
1305
1306 bufCreateInfo.size = 128;
1307
1308 VkBuffer hBuf;
1309 VmaAllocation hAlloc;
1310 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
1311 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1312 }
1313
1314 // Destroy everything.
1315 for(size_t i = items.size(); i--; )
1316 {
1317 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
1318 }
1319
1320 vmaDestroyPool(hAllocator, hPool);
1321
1322 vmaDestroyAllocator(hAllocator);
1323}
1324
1325static void TestPool_SameSize()
1326{
1327 const VkDeviceSize BUF_SIZE = 1024 * 1024;
1328 const size_t BUF_COUNT = 100;
1329 VkResult res;
1330
1331 RandomNumberGenerator rand{123};
1332
1333 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1334 bufferInfo.size = BUF_SIZE;
1335 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1336
1337 uint32_t memoryTypeBits = UINT32_MAX;
1338 {
1339 VkBuffer dummyBuffer;
1340 res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
1341 assert(res == VK_SUCCESS);
1342
1343 VkMemoryRequirements memReq;
1344 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
1345 memoryTypeBits = memReq.memoryTypeBits;
1346
1347 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
1348 }
1349
1350 VmaAllocationCreateInfo poolAllocInfo = {};
1351 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1352 uint32_t memTypeIndex;
1353 res = vmaFindMemoryTypeIndex(
1354 g_hAllocator,
1355 memoryTypeBits,
1356 &poolAllocInfo,
1357 &memTypeIndex);
1358
1359 VmaPoolCreateInfo poolCreateInfo = {};
1360 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1361 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
1362 poolCreateInfo.minBlockCount = 1;
1363 poolCreateInfo.maxBlockCount = 4;
1364 poolCreateInfo.frameInUseCount = 0;
1365
1366 VmaPool pool;
1367 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1368 assert(res == VK_SUCCESS);
1369
1370 vmaSetCurrentFrameIndex(g_hAllocator, 1);
1371
1372 VmaAllocationCreateInfo allocInfo = {};
1373 allocInfo.pool = pool;
1374 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1375 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1376
1377 struct BufItem
1378 {
1379 VkBuffer Buf;
1380 VmaAllocation Alloc;
1381 };
1382 std::vector<BufItem> items;
1383
1384 // Fill entire pool.
1385 for(size_t i = 0; i < BUF_COUNT; ++i)
1386 {
1387 BufItem item;
1388 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1389 assert(res == VK_SUCCESS);
1390 items.push_back(item);
1391 }
1392
1393 // Make sure that another allocation would fail.
1394 {
1395 BufItem item;
1396 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1397 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1398 }
1399
1400 // Validate that no buffer is lost. Also check that they are not mapped.
1401 for(size_t i = 0; i < items.size(); ++i)
1402 {
1403 VmaAllocationInfo allocInfo;
1404 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1405 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1406 assert(allocInfo.pMappedData == nullptr);
1407 }
1408
1409 // Free some percent of random items.
1410 {
1411 const size_t PERCENT_TO_FREE = 10;
1412 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
1413 for(size_t i = 0; i < itemsToFree; ++i)
1414 {
1415 size_t index = (size_t)rand.Generate() % items.size();
1416 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
1417 items.erase(items.begin() + index);
1418 }
1419 }
1420
1421 // Randomly allocate and free items.
1422 {
1423 const size_t OPERATION_COUNT = BUF_COUNT;
1424 for(size_t i = 0; i < OPERATION_COUNT; ++i)
1425 {
1426 bool allocate = rand.Generate() % 2 != 0;
1427 if(allocate)
1428 {
1429 if(items.size() < BUF_COUNT)
1430 {
1431 BufItem item;
1432 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1433 assert(res == VK_SUCCESS);
1434 items.push_back(item);
1435 }
1436 }
1437 else // Free
1438 {
1439 if(!items.empty())
1440 {
1441 size_t index = (size_t)rand.Generate() % items.size();
1442 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
1443 items.erase(items.begin() + index);
1444 }
1445 }
1446 }
1447 }
1448
1449 // Allocate up to maximum.
1450 while(items.size() < BUF_COUNT)
1451 {
1452 BufItem item;
1453 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1454 assert(res == VK_SUCCESS);
1455 items.push_back(item);
1456 }
1457
1458 // Validate that no buffer is lost.
1459 for(size_t i = 0; i < items.size(); ++i)
1460 {
1461 VmaAllocationInfo allocInfo;
1462 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1463 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1464 }
1465
1466 // Next frame.
1467 vmaSetCurrentFrameIndex(g_hAllocator, 2);
1468
1469 // Allocate another BUF_COUNT buffers.
1470 for(size_t i = 0; i < BUF_COUNT; ++i)
1471 {
1472 BufItem item;
1473 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1474 assert(res == VK_SUCCESS);
1475 items.push_back(item);
1476 }
1477
1478 // Make sure the first BUF_COUNT is lost. Delete them.
1479 for(size_t i = 0; i < BUF_COUNT; ++i)
1480 {
1481 VmaAllocationInfo allocInfo;
1482 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1483 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1484 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1485 }
1486 items.erase(items.begin(), items.begin() + BUF_COUNT);
1487
1488 // Validate that no buffer is lost.
1489 for(size_t i = 0; i < items.size(); ++i)
1490 {
1491 VmaAllocationInfo allocInfo;
1492 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1493 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1494 }
1495
1496 // Free one item.
1497 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
1498 items.pop_back();
1499
1500 // Validate statistics.
1501 {
1502 VmaPoolStats poolStats = {};
1503 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1504 assert(poolStats.allocationCount == items.size());
1505 assert(poolStats.size = BUF_COUNT * BUF_SIZE);
1506 assert(poolStats.unusedRangeCount == 1);
1507 assert(poolStats.unusedRangeSizeMax == BUF_SIZE);
1508 assert(poolStats.unusedSize == BUF_SIZE);
1509 }
1510
1511 // Free all remaining items.
1512 for(size_t i = items.size(); i--; )
1513 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1514 items.clear();
1515
1516 // Allocate maximum items again.
1517 for(size_t i = 0; i < BUF_COUNT; ++i)
1518 {
1519 BufItem item;
1520 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1521 assert(res == VK_SUCCESS);
1522 items.push_back(item);
1523 }
1524
1525 // Delete every other item.
1526 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
1527 {
1528 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1529 items.erase(items.begin() + i);
1530 }
1531
1532 // Defragment!
1533 {
1534 std::vector<VmaAllocation> allocationsToDefragment(items.size());
1535 for(size_t i = 0; i < items.size(); ++i)
1536 allocationsToDefragment[i] = items[i].Alloc;
1537
1538 VmaDefragmentationStats defragmentationStats;
1539 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
1540 assert(res == VK_SUCCESS);
1541 assert(defragmentationStats.deviceMemoryBlocksFreed == 2);
1542 }
1543
1544 // Free all remaining items.
1545 for(size_t i = items.size(); i--; )
1546 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1547 items.clear();
1548
1549 ////////////////////////////////////////////////////////////////////////////////
1550 // Test for vmaMakePoolAllocationsLost
1551
1552 // Allocate 4 buffers on frame 10.
1553 vmaSetCurrentFrameIndex(g_hAllocator, 10);
1554 for(size_t i = 0; i < 4; ++i)
1555 {
1556 BufItem item;
1557 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1558 assert(res == VK_SUCCESS);
1559 items.push_back(item);
1560 }
1561
1562 // Touch first 2 of them on frame 11.
1563 vmaSetCurrentFrameIndex(g_hAllocator, 11);
1564 for(size_t i = 0; i < 2; ++i)
1565 {
1566 VmaAllocationInfo allocInfo;
1567 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1568 }
1569
1570 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
1571 size_t lostCount = 0xDEADC0DE;
1572 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
1573 assert(lostCount == 2);
1574
1575 // Make another call. Now 0 should be lost.
1576 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
1577 assert(lostCount == 0);
1578
1579 // Make another call, with null count. Should not crash.
1580 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
1581
1582 // END: Free all remaining items.
1583 for(size_t i = items.size(); i--; )
1584 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1585
1586 items.clear();
1587
Adam Sawickid2924172018-06-11 12:48:46 +02001588 ////////////////////////////////////////////////////////////////////////////////
1589 // Test for allocation too large for pool
1590
1591 {
1592 VmaAllocationCreateInfo allocCreateInfo = {};
1593 allocCreateInfo.pool = pool;
1594
1595 VkMemoryRequirements memReq;
1596 memReq.memoryTypeBits = UINT32_MAX;
1597 memReq.alignment = 1;
1598 memReq.size = poolCreateInfo.blockSize + 4;
1599
1600 VmaAllocation alloc = nullptr;
1601 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
1602 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
1603 }
1604
Adam Sawickib8333fb2018-03-13 16:15:53 +01001605 vmaDestroyPool(g_hAllocator, pool);
1606}
1607
1608static void TestPool_Benchmark(
1609 PoolTestResult& outResult,
1610 const PoolTestConfig& config)
1611{
1612 assert(config.ThreadCount > 0);
1613
1614 RandomNumberGenerator mainRand{config.RandSeed};
1615
1616 uint32_t allocationSizeProbabilitySum = std::accumulate(
1617 config.AllocationSizes.begin(),
1618 config.AllocationSizes.end(),
1619 0u,
1620 [](uint32_t sum, const AllocationSize& allocSize) {
1621 return sum + allocSize.Probability;
1622 });
1623
1624 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1625 bufferInfo.size = 256; // Whatever.
1626 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1627
1628 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1629 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1630 imageInfo.extent.width = 256; // Whatever.
1631 imageInfo.extent.height = 256; // Whatever.
1632 imageInfo.extent.depth = 1;
1633 imageInfo.mipLevels = 1;
1634 imageInfo.arrayLayers = 1;
1635 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
1636 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
1637 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
1638 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
1639 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1640
1641 uint32_t bufferMemoryTypeBits = UINT32_MAX;
1642 {
1643 VkBuffer dummyBuffer;
1644 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
1645 assert(res == VK_SUCCESS);
1646
1647 VkMemoryRequirements memReq;
1648 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
1649 bufferMemoryTypeBits = memReq.memoryTypeBits;
1650
1651 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
1652 }
1653
1654 uint32_t imageMemoryTypeBits = UINT32_MAX;
1655 {
1656 VkImage dummyImage;
1657 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
1658 assert(res == VK_SUCCESS);
1659
1660 VkMemoryRequirements memReq;
1661 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
1662 imageMemoryTypeBits = memReq.memoryTypeBits;
1663
1664 vkDestroyImage(g_hDevice, dummyImage, nullptr);
1665 }
1666
1667 uint32_t memoryTypeBits = 0;
1668 if(config.UsesBuffers() && config.UsesImages())
1669 {
1670 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
1671 if(memoryTypeBits == 0)
1672 {
1673 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
1674 return;
1675 }
1676 }
1677 else if(config.UsesBuffers())
1678 memoryTypeBits = bufferMemoryTypeBits;
1679 else if(config.UsesImages())
1680 memoryTypeBits = imageMemoryTypeBits;
1681 else
1682 assert(0);
1683
1684 VmaPoolCreateInfo poolCreateInfo = {};
1685 poolCreateInfo.memoryTypeIndex = 0;
1686 poolCreateInfo.minBlockCount = 1;
1687 poolCreateInfo.maxBlockCount = 1;
1688 poolCreateInfo.blockSize = config.PoolSize;
1689 poolCreateInfo.frameInUseCount = 1;
1690
1691 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
1692 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1693 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1694
1695 VmaPool pool;
1696 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1697 assert(res == VK_SUCCESS);
1698
1699 // Start time measurement - after creating pool and initializing data structures.
1700 time_point timeBeg = std::chrono::high_resolution_clock::now();
1701
1702 ////////////////////////////////////////////////////////////////////////////////
1703 // ThreadProc
1704 auto ThreadProc = [&](
1705 PoolTestThreadResult* outThreadResult,
1706 uint32_t randSeed,
1707 HANDLE frameStartEvent,
1708 HANDLE frameEndEvent) -> void
1709 {
1710 RandomNumberGenerator threadRand{randSeed};
1711
1712 outThreadResult->AllocationTimeMin = duration::max();
1713 outThreadResult->AllocationTimeSum = duration::zero();
1714 outThreadResult->AllocationTimeMax = duration::min();
1715 outThreadResult->DeallocationTimeMin = duration::max();
1716 outThreadResult->DeallocationTimeSum = duration::zero();
1717 outThreadResult->DeallocationTimeMax = duration::min();
1718 outThreadResult->AllocationCount = 0;
1719 outThreadResult->DeallocationCount = 0;
1720 outThreadResult->LostAllocationCount = 0;
1721 outThreadResult->LostAllocationTotalSize = 0;
1722 outThreadResult->FailedAllocationCount = 0;
1723 outThreadResult->FailedAllocationTotalSize = 0;
1724
1725 struct Item
1726 {
1727 VkDeviceSize BufferSize;
1728 VkExtent2D ImageSize;
1729 VkBuffer Buf;
1730 VkImage Image;
1731 VmaAllocation Alloc;
1732
1733 VkDeviceSize CalcSizeBytes() const
1734 {
1735 return BufferSize +
1736 ImageSize.width * ImageSize.height * 4;
1737 }
1738 };
1739 std::vector<Item> unusedItems, usedItems;
1740
1741 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
1742
1743 // Create all items - all unused, not yet allocated.
1744 for(size_t i = 0; i < threadTotalItemCount; ++i)
1745 {
1746 Item item = {};
1747
1748 uint32_t allocSizeIndex = 0;
1749 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
1750 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
1751 r -= config.AllocationSizes[allocSizeIndex++].Probability;
1752
1753 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
1754 if(allocSize.BufferSizeMax > 0)
1755 {
1756 assert(allocSize.BufferSizeMin > 0);
1757 assert(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
1758 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
1759 item.BufferSize = allocSize.BufferSizeMin;
1760 else
1761 {
1762 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
1763 item.BufferSize = item.BufferSize / 16 * 16;
1764 }
1765 }
1766 else
1767 {
1768 assert(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
1769 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
1770 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
1771 else
1772 {
1773 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
1774 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
1775 }
1776 }
1777
1778 unusedItems.push_back(item);
1779 }
1780
1781 auto Allocate = [&](Item& item) -> VkResult
1782 {
1783 VmaAllocationCreateInfo allocCreateInfo = {};
1784 allocCreateInfo.pool = pool;
1785 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1786 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1787
1788 if(item.BufferSize)
1789 {
1790 bufferInfo.size = item.BufferSize;
1791 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
1792 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
1793 }
1794 else
1795 {
1796 assert(item.ImageSize.width && item.ImageSize.height);
1797
1798 imageInfo.extent.width = item.ImageSize.width;
1799 imageInfo.extent.height = item.ImageSize.height;
1800 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
1801 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
1802 }
1803 };
1804
1805 ////////////////////////////////////////////////////////////////////////////////
1806 // Frames
1807 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
1808 {
1809 WaitForSingleObject(frameStartEvent, INFINITE);
1810
1811 // Always make some percent of used bufs unused, to choose different used ones.
1812 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
1813 for(size_t i = 0; i < bufsToMakeUnused; ++i)
1814 {
1815 size_t index = threadRand.Generate() % usedItems.size();
1816 unusedItems.push_back(usedItems[index]);
1817 usedItems.erase(usedItems.begin() + index);
1818 }
1819
1820 // Determine which bufs we want to use in this frame.
1821 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
1822 / config.ThreadCount;
1823 assert(usedBufCount < usedItems.size() + unusedItems.size());
1824 // Move some used to unused.
1825 while(usedBufCount < usedItems.size())
1826 {
1827 size_t index = threadRand.Generate() % usedItems.size();
1828 unusedItems.push_back(usedItems[index]);
1829 usedItems.erase(usedItems.begin() + index);
1830 }
1831 // Move some unused to used.
1832 while(usedBufCount > usedItems.size())
1833 {
1834 size_t index = threadRand.Generate() % unusedItems.size();
1835 usedItems.push_back(unusedItems[index]);
1836 unusedItems.erase(unusedItems.begin() + index);
1837 }
1838
1839 uint32_t touchExistingCount = 0;
1840 uint32_t touchLostCount = 0;
1841 uint32_t createSucceededCount = 0;
1842 uint32_t createFailedCount = 0;
1843
1844 // Touch all used bufs. If not created or lost, allocate.
1845 for(size_t i = 0; i < usedItems.size(); ++i)
1846 {
1847 Item& item = usedItems[i];
1848 // Not yet created.
1849 if(item.Alloc == VK_NULL_HANDLE)
1850 {
1851 res = Allocate(item);
1852 ++outThreadResult->AllocationCount;
1853 if(res != VK_SUCCESS)
1854 {
1855 item.Alloc = VK_NULL_HANDLE;
1856 item.Buf = VK_NULL_HANDLE;
1857 ++outThreadResult->FailedAllocationCount;
1858 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
1859 ++createFailedCount;
1860 }
1861 else
1862 ++createSucceededCount;
1863 }
1864 else
1865 {
1866 // Touch.
1867 VmaAllocationInfo allocInfo;
1868 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
1869 // Lost.
1870 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1871 {
1872 ++touchLostCount;
1873
1874 // Destroy.
1875 {
1876 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
1877 if(item.Buf)
1878 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
1879 else
1880 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
1881 ++outThreadResult->DeallocationCount;
1882 }
1883 item.Alloc = VK_NULL_HANDLE;
1884 item.Buf = VK_NULL_HANDLE;
1885
1886 ++outThreadResult->LostAllocationCount;
1887 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
1888
1889 // Recreate.
1890 res = Allocate(item);
1891 ++outThreadResult->AllocationCount;
1892 // Creation failed.
1893 if(res != VK_SUCCESS)
1894 {
1895 ++outThreadResult->FailedAllocationCount;
1896 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
1897 ++createFailedCount;
1898 }
1899 else
1900 ++createSucceededCount;
1901 }
1902 else
1903 ++touchExistingCount;
1904 }
1905 }
1906
1907 /*
1908 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
1909 randSeed, frameIndex,
1910 touchExistingCount, touchLostCount,
1911 createSucceededCount, createFailedCount);
1912 */
1913
1914 SetEvent(frameEndEvent);
1915 }
1916
1917 // Free all remaining items.
1918 for(size_t i = usedItems.size(); i--; )
1919 {
1920 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
1921 if(usedItems[i].Buf)
1922 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
1923 else
1924 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
1925 ++outThreadResult->DeallocationCount;
1926 }
1927 for(size_t i = unusedItems.size(); i--; )
1928 {
1929 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
1930 if(unusedItems[i].Buf)
1931 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
1932 else
1933 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
1934 ++outThreadResult->DeallocationCount;
1935 }
1936 };
1937
1938 // Launch threads.
1939 uint32_t threadRandSeed = mainRand.Generate();
1940 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
1941 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
1942 std::vector<std::thread> bkgThreads;
1943 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
1944 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
1945 {
1946 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
1947 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
1948 bkgThreads.emplace_back(std::bind(
1949 ThreadProc,
1950 &threadResults[threadIndex],
1951 threadRandSeed + threadIndex,
1952 frameStartEvents[threadIndex],
1953 frameEndEvents[threadIndex]));
1954 }
1955
1956 // Execute frames.
1957 assert(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
1958 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
1959 {
1960 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
1961 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
1962 SetEvent(frameStartEvents[threadIndex]);
1963 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
1964 }
1965
1966 // Wait for threads finished
1967 for(size_t i = 0; i < bkgThreads.size(); ++i)
1968 {
1969 bkgThreads[i].join();
1970 CloseHandle(frameEndEvents[i]);
1971 CloseHandle(frameStartEvents[i]);
1972 }
1973 bkgThreads.clear();
1974
1975 // Finish time measurement - before destroying pool.
1976 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
1977
1978 vmaDestroyPool(g_hAllocator, pool);
1979
1980 outResult.AllocationTimeMin = duration::max();
1981 outResult.AllocationTimeAvg = duration::zero();
1982 outResult.AllocationTimeMax = duration::min();
1983 outResult.DeallocationTimeMin = duration::max();
1984 outResult.DeallocationTimeAvg = duration::zero();
1985 outResult.DeallocationTimeMax = duration::min();
1986 outResult.LostAllocationCount = 0;
1987 outResult.LostAllocationTotalSize = 0;
1988 outResult.FailedAllocationCount = 0;
1989 outResult.FailedAllocationTotalSize = 0;
1990 size_t allocationCount = 0;
1991 size_t deallocationCount = 0;
1992 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
1993 {
1994 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
1995 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
1996 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
1997 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
1998 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
1999 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
2000 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
2001 allocationCount += threadResult.AllocationCount;
2002 deallocationCount += threadResult.DeallocationCount;
2003 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
2004 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
2005 outResult.LostAllocationCount += threadResult.LostAllocationCount;
2006 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
2007 }
2008 if(allocationCount)
2009 outResult.AllocationTimeAvg /= allocationCount;
2010 if(deallocationCount)
2011 outResult.DeallocationTimeAvg /= deallocationCount;
2012}
2013
2014static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
2015{
2016 if(ptr1 < ptr2)
2017 return ptr1 + size1 > ptr2;
2018 else if(ptr2 < ptr1)
2019 return ptr2 + size2 > ptr1;
2020 else
2021 return true;
2022}
2023
2024static void TestMapping()
2025{
2026 wprintf(L"Testing mapping...\n");
2027
2028 VkResult res;
2029 uint32_t memTypeIndex = UINT32_MAX;
2030
2031 enum TEST
2032 {
2033 TEST_NORMAL,
2034 TEST_POOL,
2035 TEST_DEDICATED,
2036 TEST_COUNT
2037 };
2038 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
2039 {
2040 VmaPool pool = nullptr;
2041 if(testIndex == TEST_POOL)
2042 {
2043 assert(memTypeIndex != UINT32_MAX);
2044 VmaPoolCreateInfo poolInfo = {};
2045 poolInfo.memoryTypeIndex = memTypeIndex;
2046 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
2047 assert(res == VK_SUCCESS);
2048 }
2049
2050 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2051 bufInfo.size = 0x10000;
2052 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2053
2054 VmaAllocationCreateInfo allocCreateInfo = {};
2055 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2056 allocCreateInfo.pool = pool;
2057 if(testIndex == TEST_DEDICATED)
2058 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2059
2060 VmaAllocationInfo allocInfo;
2061
2062 // Mapped manually
2063
2064 // Create 2 buffers.
2065 BufferInfo bufferInfos[3];
2066 for(size_t i = 0; i < 2; ++i)
2067 {
2068 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
2069 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
2070 assert(res == VK_SUCCESS);
2071 assert(allocInfo.pMappedData == nullptr);
2072 memTypeIndex = allocInfo.memoryType;
2073 }
2074
2075 // Map buffer 0.
2076 char* data00 = nullptr;
2077 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
2078 assert(res == VK_SUCCESS && data00 != nullptr);
2079 data00[0xFFFF] = data00[0];
2080
2081 // Map buffer 0 second time.
2082 char* data01 = nullptr;
2083 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
2084 assert(res == VK_SUCCESS && data01 == data00);
2085
2086 // Map buffer 1.
2087 char* data1 = nullptr;
2088 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
2089 assert(res == VK_SUCCESS && data1 != nullptr);
2090 assert(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
2091 data1[0xFFFF] = data1[0];
2092
2093 // Unmap buffer 0 two times.
2094 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
2095 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
2096 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
2097 assert(allocInfo.pMappedData == nullptr);
2098
2099 // Unmap buffer 1.
2100 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
2101 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
2102 assert(allocInfo.pMappedData == nullptr);
2103
2104 // Create 3rd buffer - persistently mapped.
2105 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
2106 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
2107 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
2108 assert(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
2109
2110 // Map buffer 2.
2111 char* data2 = nullptr;
2112 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
2113 assert(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
2114 data2[0xFFFF] = data2[0];
2115
2116 // Unmap buffer 2.
2117 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
2118 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
2119 assert(allocInfo.pMappedData == data2);
2120
2121 // Destroy all buffers.
2122 for(size_t i = 3; i--; )
2123 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
2124
2125 vmaDestroyPool(g_hAllocator, pool);
2126 }
2127}
2128
2129static void TestMappingMultithreaded()
2130{
2131 wprintf(L"Testing mapping multithreaded...\n");
2132
2133 static const uint32_t threadCount = 16;
2134 static const uint32_t bufferCount = 1024;
2135 static const uint32_t threadBufferCount = bufferCount / threadCount;
2136
2137 VkResult res;
2138 volatile uint32_t memTypeIndex = UINT32_MAX;
2139
2140 enum TEST
2141 {
2142 TEST_NORMAL,
2143 TEST_POOL,
2144 TEST_DEDICATED,
2145 TEST_COUNT
2146 };
2147 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
2148 {
2149 VmaPool pool = nullptr;
2150 if(testIndex == TEST_POOL)
2151 {
2152 assert(memTypeIndex != UINT32_MAX);
2153 VmaPoolCreateInfo poolInfo = {};
2154 poolInfo.memoryTypeIndex = memTypeIndex;
2155 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
2156 assert(res == VK_SUCCESS);
2157 }
2158
2159 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2160 bufCreateInfo.size = 0x10000;
2161 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2162
2163 VmaAllocationCreateInfo allocCreateInfo = {};
2164 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2165 allocCreateInfo.pool = pool;
2166 if(testIndex == TEST_DEDICATED)
2167 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2168
2169 std::thread threads[threadCount];
2170 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
2171 {
2172 threads[threadIndex] = std::thread([=, &memTypeIndex](){
2173 // ======== THREAD FUNCTION ========
2174
2175 RandomNumberGenerator rand{threadIndex};
2176
2177 enum class MODE
2178 {
2179 // Don't map this buffer at all.
2180 DONT_MAP,
2181 // Map and quickly unmap.
2182 MAP_FOR_MOMENT,
2183 // Map and unmap before destruction.
2184 MAP_FOR_LONGER,
2185 // Map two times. Quickly unmap, second unmap before destruction.
2186 MAP_TWO_TIMES,
2187 // Create this buffer as persistently mapped.
2188 PERSISTENTLY_MAPPED,
2189 COUNT
2190 };
2191 std::vector<BufferInfo> bufInfos{threadBufferCount};
2192 std::vector<MODE> bufModes{threadBufferCount};
2193
2194 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
2195 {
2196 BufferInfo& bufInfo = bufInfos[bufferIndex];
2197 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
2198 bufModes[bufferIndex] = mode;
2199
2200 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
2201 if(mode == MODE::PERSISTENTLY_MAPPED)
2202 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
2203
2204 VmaAllocationInfo allocInfo;
2205 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
2206 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
2207 assert(res == VK_SUCCESS);
2208
2209 if(memTypeIndex == UINT32_MAX)
2210 memTypeIndex = allocInfo.memoryType;
2211
2212 char* data = nullptr;
2213
2214 if(mode == MODE::PERSISTENTLY_MAPPED)
2215 {
2216 data = (char*)allocInfo.pMappedData;
2217 assert(data != nullptr);
2218 }
2219 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
2220 mode == MODE::MAP_TWO_TIMES)
2221 {
2222 assert(data == nullptr);
2223 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
2224 assert(res == VK_SUCCESS && data != nullptr);
2225
2226 if(mode == MODE::MAP_TWO_TIMES)
2227 {
2228 char* data2 = nullptr;
2229 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
2230 assert(res == VK_SUCCESS && data2 == data);
2231 }
2232 }
2233 else if(mode == MODE::DONT_MAP)
2234 {
2235 assert(allocInfo.pMappedData == nullptr);
2236 }
2237 else
2238 assert(0);
2239
2240 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
2241 if(data)
2242 data[0xFFFF] = data[0];
2243
2244 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
2245 {
2246 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
2247
2248 VmaAllocationInfo allocInfo;
2249 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
2250 if(mode == MODE::MAP_FOR_MOMENT)
2251 assert(allocInfo.pMappedData == nullptr);
2252 else
2253 assert(allocInfo.pMappedData == data);
2254 }
2255
2256 switch(rand.Generate() % 3)
2257 {
2258 case 0: Sleep(0); break; // Yield.
2259 case 1: Sleep(10); break; // 10 ms
2260 // default: No sleep.
2261 }
2262
2263 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
2264 if(data)
2265 data[0xFFFF] = data[0];
2266 }
2267
2268 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
2269 {
2270 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
2271 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
2272 {
2273 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
2274
2275 VmaAllocationInfo allocInfo;
2276 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
2277 assert(allocInfo.pMappedData == nullptr);
2278 }
2279
2280 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
2281 }
2282 });
2283 }
2284
2285 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
2286 threads[threadIndex].join();
2287
2288 vmaDestroyPool(g_hAllocator, pool);
2289 }
2290}
2291
2292static void WriteMainTestResultHeader(FILE* file)
2293{
2294 fprintf(file,
2295 "Code,Test,Time,"
2296 "Config,"
2297 "Total Time (us),"
2298 "Allocation Time Min (us),"
2299 "Allocation Time Avg (us),"
2300 "Allocation Time Max (us),"
2301 "Deallocation Time Min (us),"
2302 "Deallocation Time Avg (us),"
2303 "Deallocation Time Max (us),"
2304 "Total Memory Allocated (B),"
2305 "Free Range Size Avg (B),"
2306 "Free Range Size Max (B)\n");
2307}
2308
2309static void WriteMainTestResult(
2310 FILE* file,
2311 const char* codeDescription,
2312 const char* testDescription,
2313 const Config& config, const Result& result)
2314{
2315 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
2316 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
2317 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
2318 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
2319 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
2320 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
2321 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
2322
2323 time_t rawTime; time(&rawTime);
2324 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
2325 char timeStr[128];
2326 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
2327
2328 fprintf(file,
2329 "%s,%s,%s,"
2330 "BeginBytesToAllocate=%I64u MaxBytesToAllocate=%I64u AdditionalOperationCount=%u ThreadCount=%u FreeOrder=%d,"
2331 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
2332 codeDescription,
2333 testDescription,
2334 timeStr,
2335 config.BeginBytesToAllocate, config.MaxBytesToAllocate, config.AdditionalOperationCount, config.ThreadCount, (uint32_t)config.FreeOrder,
2336 totalTimeSeconds * 1e6f,
2337 allocationTimeMinSeconds * 1e6f,
2338 allocationTimeAvgSeconds * 1e6f,
2339 allocationTimeMaxSeconds * 1e6f,
2340 deallocationTimeMinSeconds * 1e6f,
2341 deallocationTimeAvgSeconds * 1e6f,
2342 deallocationTimeMaxSeconds * 1e6f,
2343 result.TotalMemoryAllocated,
2344 result.FreeRangeSizeAvg,
2345 result.FreeRangeSizeMax);
2346}
2347
2348static void WritePoolTestResultHeader(FILE* file)
2349{
2350 fprintf(file,
2351 "Code,Test,Time,"
2352 "Config,"
2353 "Total Time (us),"
2354 "Allocation Time Min (us),"
2355 "Allocation Time Avg (us),"
2356 "Allocation Time Max (us),"
2357 "Deallocation Time Min (us),"
2358 "Deallocation Time Avg (us),"
2359 "Deallocation Time Max (us),"
2360 "Lost Allocation Count,"
2361 "Lost Allocation Total Size (B),"
2362 "Failed Allocation Count,"
2363 "Failed Allocation Total Size (B)\n");
2364}
2365
2366static void WritePoolTestResult(
2367 FILE* file,
2368 const char* codeDescription,
2369 const char* testDescription,
2370 const PoolTestConfig& config,
2371 const PoolTestResult& result)
2372{
2373 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
2374 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
2375 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
2376 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
2377 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
2378 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
2379 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
2380
2381 time_t rawTime; time(&rawTime);
2382 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
2383 char timeStr[128];
2384 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
2385
2386 fprintf(file,
2387 "%s,%s,%s,"
2388 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
2389 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
2390 // General
2391 codeDescription,
2392 testDescription,
2393 timeStr,
2394 // Config
2395 config.ThreadCount,
2396 (unsigned long long)config.PoolSize,
2397 config.FrameCount,
2398 config.TotalItemCount,
2399 config.UsedItemCountMin,
2400 config.UsedItemCountMax,
2401 config.ItemsToMakeUnusedPercent,
2402 // Results
2403 totalTimeSeconds * 1e6f,
2404 allocationTimeMinSeconds * 1e6f,
2405 allocationTimeAvgSeconds * 1e6f,
2406 allocationTimeMaxSeconds * 1e6f,
2407 deallocationTimeMinSeconds * 1e6f,
2408 deallocationTimeAvgSeconds * 1e6f,
2409 deallocationTimeMaxSeconds * 1e6f,
2410 result.LostAllocationCount,
2411 result.LostAllocationTotalSize,
2412 result.FailedAllocationCount,
2413 result.FailedAllocationTotalSize);
2414}
2415
2416static void PerformCustomMainTest(FILE* file)
2417{
2418 Config config{};
2419 config.RandSeed = 65735476;
2420 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
2421 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
2422 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
2423 config.FreeOrder = FREE_ORDER::FORWARD;
2424 config.ThreadCount = 16;
2425 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
2426
2427 // Buffers
2428 //config.AllocationSizes.push_back({4, 16, 1024});
2429 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
2430
2431 // Images
2432 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
2433 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
2434
2435 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
2436 config.AdditionalOperationCount = 1024;
2437
2438 Result result{};
2439 VkResult res = MainTest(result, config);
2440 assert(res == VK_SUCCESS);
2441 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
2442}
2443
2444static void PerformCustomPoolTest(FILE* file)
2445{
2446 PoolTestConfig config;
2447 config.PoolSize = 100 * 1024 * 1024;
2448 config.RandSeed = 2345764;
2449 config.ThreadCount = 1;
2450 config.FrameCount = 200;
2451 config.ItemsToMakeUnusedPercent = 2;
2452
2453 AllocationSize allocSize = {};
2454 allocSize.BufferSizeMin = 1024;
2455 allocSize.BufferSizeMax = 1024 * 1024;
2456 allocSize.Probability = 1;
2457 config.AllocationSizes.push_back(allocSize);
2458
2459 allocSize.BufferSizeMin = 0;
2460 allocSize.BufferSizeMax = 0;
2461 allocSize.ImageSizeMin = 128;
2462 allocSize.ImageSizeMax = 1024;
2463 allocSize.Probability = 1;
2464 config.AllocationSizes.push_back(allocSize);
2465
2466 config.PoolSize = config.CalcAvgResourceSize() * 200;
2467 config.UsedItemCountMax = 160;
2468 config.TotalItemCount = config.UsedItemCountMax * 10;
2469 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
2470
2471 g_MemoryAliasingWarningEnabled = false;
2472 PoolTestResult result = {};
2473 TestPool_Benchmark(result, config);
2474 g_MemoryAliasingWarningEnabled = true;
2475
2476 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
2477}
2478
2479enum CONFIG_TYPE {
2480 CONFIG_TYPE_MINIMUM,
2481 CONFIG_TYPE_SMALL,
2482 CONFIG_TYPE_AVERAGE,
2483 CONFIG_TYPE_LARGE,
2484 CONFIG_TYPE_MAXIMUM,
2485 CONFIG_TYPE_COUNT
2486};
2487
2488static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
2489//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
2490static const char* CODE_DESCRIPTION = "Foo";
2491
2492static void PerformMainTests(FILE* file)
2493{
2494 uint32_t repeatCount = 1;
2495 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
2496
2497 Config config{};
2498 config.RandSeed = 65735476;
2499 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
2500 config.FreeOrder = FREE_ORDER::FORWARD;
2501
2502 size_t threadCountCount = 1;
2503 switch(ConfigType)
2504 {
2505 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
2506 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
2507 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
2508 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
2509 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
2510 default: assert(0);
2511 }
2512 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
2513 {
2514 std::string desc1;
2515
2516 switch(threadCountIndex)
2517 {
2518 case 0:
2519 desc1 += "1_thread";
2520 config.ThreadCount = 1;
2521 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
2522 break;
2523 case 1:
2524 desc1 += "16_threads+0%_common";
2525 config.ThreadCount = 16;
2526 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
2527 break;
2528 case 2:
2529 desc1 += "16_threads+50%_common";
2530 config.ThreadCount = 16;
2531 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
2532 break;
2533 case 3:
2534 desc1 += "16_threads+100%_common";
2535 config.ThreadCount = 16;
2536 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
2537 break;
2538 case 4:
2539 desc1 += "2_threads+0%_common";
2540 config.ThreadCount = 2;
2541 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
2542 break;
2543 case 5:
2544 desc1 += "2_threads+50%_common";
2545 config.ThreadCount = 2;
2546 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
2547 break;
2548 case 6:
2549 desc1 += "2_threads+100%_common";
2550 config.ThreadCount = 2;
2551 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
2552 break;
2553 default:
2554 assert(0);
2555 }
2556
2557 // 0 = buffers, 1 = images, 2 = buffers and images
2558 size_t buffersVsImagesCount = 2;
2559 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
2560 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
2561 {
2562 std::string desc2 = desc1;
2563 switch(buffersVsImagesIndex)
2564 {
2565 case 0: desc2 += " Buffers"; break;
2566 case 1: desc2 += " Images"; break;
2567 case 2: desc2 += " Buffers+Images"; break;
2568 default: assert(0);
2569 }
2570
2571 // 0 = small, 1 = large, 2 = small and large
2572 size_t smallVsLargeCount = 2;
2573 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
2574 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
2575 {
2576 std::string desc3 = desc2;
2577 switch(smallVsLargeIndex)
2578 {
2579 case 0: desc3 += " Small"; break;
2580 case 1: desc3 += " Large"; break;
2581 case 2: desc3 += " Small+Large"; break;
2582 default: assert(0);
2583 }
2584
2585 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2586 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
2587 else
2588 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
2589
2590 // 0 = varying sizes min...max, 1 = set of constant sizes
2591 size_t constantSizesCount = 1;
2592 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
2593 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
2594 {
2595 std::string desc4 = desc3;
2596 switch(constantSizesIndex)
2597 {
2598 case 0: desc4 += " Varying_sizes"; break;
2599 case 1: desc4 += " Constant_sizes"; break;
2600 default: assert(0);
2601 }
2602
2603 config.AllocationSizes.clear();
2604 // Buffers present
2605 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
2606 {
2607 // Small
2608 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
2609 {
2610 // Varying size
2611 if(constantSizesIndex == 0)
2612 config.AllocationSizes.push_back({4, 16, 1024});
2613 // Constant sizes
2614 else
2615 {
2616 config.AllocationSizes.push_back({1, 16, 16});
2617 config.AllocationSizes.push_back({1, 64, 64});
2618 config.AllocationSizes.push_back({1, 256, 256});
2619 config.AllocationSizes.push_back({1, 1024, 1024});
2620 }
2621 }
2622 // Large
2623 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2624 {
2625 // Varying size
2626 if(constantSizesIndex == 0)
2627 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
2628 // Constant sizes
2629 else
2630 {
2631 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
2632 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
2633 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
2634 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
2635 }
2636 }
2637 }
2638 // Images present
2639 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
2640 {
2641 // Small
2642 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
2643 {
2644 // Varying size
2645 if(constantSizesIndex == 0)
2646 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
2647 // Constant sizes
2648 else
2649 {
2650 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
2651 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
2652 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
2653 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
2654 }
2655 }
2656 // Large
2657 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2658 {
2659 // Varying size
2660 if(constantSizesIndex == 0)
2661 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
2662 // Constant sizes
2663 else
2664 {
2665 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
2666 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
2667 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
2668 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
2669 }
2670 }
2671 }
2672
2673 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
2674 size_t beginBytesToAllocateCount = 1;
2675 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
2676 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
2677 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
2678 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
2679 {
2680 std::string desc5 = desc4;
2681
2682 switch(beginBytesToAllocateIndex)
2683 {
2684 case 0:
2685 desc5 += " Allocate_100%";
2686 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
2687 config.AdditionalOperationCount = 0;
2688 break;
2689 case 1:
2690 desc5 += " Allocate_50%+Operations";
2691 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
2692 config.AdditionalOperationCount = 1024;
2693 break;
2694 case 2:
2695 desc5 += " Allocate_5%+Operations";
2696 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
2697 config.AdditionalOperationCount = 1024;
2698 break;
2699 case 3:
2700 desc5 += " Allocate_95%+Operations";
2701 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
2702 config.AdditionalOperationCount = 1024;
2703 break;
2704 default:
2705 assert(0);
2706 }
2707
2708 const char* testDescription = desc5.c_str();
2709
2710 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
2711 {
2712 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
2713
2714 Result result{};
2715 VkResult res = MainTest(result, config);
2716 assert(res == VK_SUCCESS);
2717 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
2718 }
2719 }
2720 }
2721 }
2722 }
2723 }
2724}
2725
2726static void PerformPoolTests(FILE* file)
2727{
2728 const size_t AVG_RESOURCES_PER_POOL = 300;
2729
2730 uint32_t repeatCount = 1;
2731 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
2732
2733 PoolTestConfig config{};
2734 config.RandSeed = 2346343;
2735 config.FrameCount = 200;
2736 config.ItemsToMakeUnusedPercent = 2;
2737
2738 size_t threadCountCount = 1;
2739 switch(ConfigType)
2740 {
2741 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
2742 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
2743 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
2744 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
2745 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
2746 default: assert(0);
2747 }
2748 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
2749 {
2750 std::string desc1;
2751
2752 switch(threadCountIndex)
2753 {
2754 case 0:
2755 desc1 += "1_thread";
2756 config.ThreadCount = 1;
2757 break;
2758 case 1:
2759 desc1 += "16_threads";
2760 config.ThreadCount = 16;
2761 break;
2762 case 2:
2763 desc1 += "2_threads";
2764 config.ThreadCount = 2;
2765 break;
2766 default:
2767 assert(0);
2768 }
2769
2770 // 0 = buffers, 1 = images, 2 = buffers and images
2771 size_t buffersVsImagesCount = 2;
2772 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
2773 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
2774 {
2775 std::string desc2 = desc1;
2776 switch(buffersVsImagesIndex)
2777 {
2778 case 0: desc2 += " Buffers"; break;
2779 case 1: desc2 += " Images"; break;
2780 case 2: desc2 += " Buffers+Images"; break;
2781 default: assert(0);
2782 }
2783
2784 // 0 = small, 1 = large, 2 = small and large
2785 size_t smallVsLargeCount = 2;
2786 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
2787 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
2788 {
2789 std::string desc3 = desc2;
2790 switch(smallVsLargeIndex)
2791 {
2792 case 0: desc3 += " Small"; break;
2793 case 1: desc3 += " Large"; break;
2794 case 2: desc3 += " Small+Large"; break;
2795 default: assert(0);
2796 }
2797
2798 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2799 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
2800 else
2801 config.PoolSize = 4ull * 1024 * 1024;
2802
2803 // 0 = varying sizes min...max, 1 = set of constant sizes
2804 size_t constantSizesCount = 1;
2805 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
2806 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
2807 {
2808 std::string desc4 = desc3;
2809 switch(constantSizesIndex)
2810 {
2811 case 0: desc4 += " Varying_sizes"; break;
2812 case 1: desc4 += " Constant_sizes"; break;
2813 default: assert(0);
2814 }
2815
2816 config.AllocationSizes.clear();
2817 // Buffers present
2818 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
2819 {
2820 // Small
2821 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
2822 {
2823 // Varying size
2824 if(constantSizesIndex == 0)
2825 config.AllocationSizes.push_back({4, 16, 1024});
2826 // Constant sizes
2827 else
2828 {
2829 config.AllocationSizes.push_back({1, 16, 16});
2830 config.AllocationSizes.push_back({1, 64, 64});
2831 config.AllocationSizes.push_back({1, 256, 256});
2832 config.AllocationSizes.push_back({1, 1024, 1024});
2833 }
2834 }
2835 // Large
2836 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2837 {
2838 // Varying size
2839 if(constantSizesIndex == 0)
2840 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
2841 // Constant sizes
2842 else
2843 {
2844 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
2845 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
2846 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
2847 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
2848 }
2849 }
2850 }
2851 // Images present
2852 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
2853 {
2854 // Small
2855 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
2856 {
2857 // Varying size
2858 if(constantSizesIndex == 0)
2859 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
2860 // Constant sizes
2861 else
2862 {
2863 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
2864 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
2865 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
2866 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
2867 }
2868 }
2869 // Large
2870 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2871 {
2872 // Varying size
2873 if(constantSizesIndex == 0)
2874 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
2875 // Constant sizes
2876 else
2877 {
2878 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
2879 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
2880 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
2881 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
2882 }
2883 }
2884 }
2885
2886 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
2887 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
2888
2889 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
2890 size_t subscriptionModeCount;
2891 switch(ConfigType)
2892 {
2893 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
2894 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
2895 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
2896 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
2897 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
2898 default: assert(0);
2899 }
2900 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
2901 {
2902 std::string desc5 = desc4;
2903
2904 switch(subscriptionModeIndex)
2905 {
2906 case 0:
2907 desc5 += " Subscription_66%";
2908 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
2909 break;
2910 case 1:
2911 desc5 += " Subscription_133%";
2912 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
2913 break;
2914 case 2:
2915 desc5 += " Subscription_100%";
2916 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
2917 break;
2918 case 3:
2919 desc5 += " Subscription_33%";
2920 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
2921 break;
2922 case 4:
2923 desc5 += " Subscription_166%";
2924 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
2925 break;
2926 default:
2927 assert(0);
2928 }
2929
2930 config.TotalItemCount = config.UsedItemCountMax * 5;
2931 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
2932
2933 const char* testDescription = desc5.c_str();
2934
2935 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
2936 {
2937 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
2938
2939 PoolTestResult result{};
2940 g_MemoryAliasingWarningEnabled = false;
2941 TestPool_Benchmark(result, config);
2942 g_MemoryAliasingWarningEnabled = true;
2943 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
2944 }
2945 }
2946 }
2947 }
2948 }
2949 }
2950}
2951
2952void Test()
2953{
2954 wprintf(L"TESTING:\n");
2955
2956 // # Simple tests
2957
2958 TestBasics();
2959 TestPool_SameSize();
2960 TestHeapSizeLimit();
2961 TestMapping();
2962 TestMappingMultithreaded();
2963 TestDefragmentationSimple();
2964 TestDefragmentationFull();
2965
2966 // # Detailed tests
2967 FILE* file;
2968 fopen_s(&file, "Results.csv", "w");
2969 assert(file != NULL);
2970
2971 WriteMainTestResultHeader(file);
2972 PerformMainTests(file);
2973 //PerformCustomMainTest(file);
2974
2975 WritePoolTestResultHeader(file);
2976 PerformPoolTests(file);
2977 //PerformCustomPoolTest(file);
2978
2979 fclose(file);
2980
2981 wprintf(L"Done.\n");
2982}
2983
Adam Sawickif1a793c2018-03-13 15:42:22 +01002984#endif // #ifdef _WIN32