blob: 552f3cc41c81f181d07b569a58a860f274aee0ee [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
Adam Sawickie44c6262018-06-15 14:30:39 +0200561static void SaveAllocatorStatsToFile(const wchar_t* filePath)
Adam Sawickib8333fb2018-03-13 16:15:53 +0100562{
563 char* stats;
Adam Sawickie44c6262018-06-15 14:30:39 +0200564 vmaBuildStatsString(g_hAllocator, &stats, VK_TRUE);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100565 SaveFile(filePath, stats, strlen(stats));
Adam Sawickie44c6262018-06-15 14:30:39 +0200566 vmaFreeStatsString(g_hAllocator, stats);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100567}
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
Adam Sawickie44c6262018-06-15 14:30:39 +0200985 SaveAllocatorStatsToFile(L"Before.csv");
Adam Sawickib8333fb2018-03-13 16:15:53 +0100986
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);
Adam Sawickie44c6262018-06-15 14:30:39 +02001036 SaveAllocatorStatsToFile(fileName);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001037 }
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
Adam Sawicki212a4a62018-06-14 15:44:45 +02001325#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02001326static void TestDebugMargin()
1327{
1328 if(VMA_DEBUG_MARGIN == 0)
1329 {
1330 return;
1331 }
1332
1333 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02001334 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02001335
1336 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02001337 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02001338
1339 // Create few buffers of different size.
1340 const size_t BUF_COUNT = 10;
1341 BufferInfo buffers[BUF_COUNT];
1342 VmaAllocationInfo allocInfo[BUF_COUNT];
1343 for(size_t i = 0; i < 10; ++i)
1344 {
1345 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02001346 // Last one will be mapped.
1347 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02001348
1349 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
1350 assert(res == VK_SUCCESS);
1351 // Margin is preserved also at the beginning of a block.
1352 assert(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02001353
1354 if(i == BUF_COUNT - 1)
1355 {
1356 // Fill with data.
1357 assert(allocInfo[i].pMappedData != nullptr);
1358 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
1359 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
1360 }
Adam Sawicki73b16652018-06-11 16:39:25 +02001361 }
1362
1363 // Check if their offsets preserve margin between them.
1364 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
1365 {
1366 if(lhs.deviceMemory != rhs.deviceMemory)
1367 {
1368 return lhs.deviceMemory < rhs.deviceMemory;
1369 }
1370 return lhs.offset < rhs.offset;
1371 });
1372 for(size_t i = 1; i < BUF_COUNT; ++i)
1373 {
1374 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
1375 {
1376 assert(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
1377 }
1378 }
1379
Adam Sawicki212a4a62018-06-14 15:44:45 +02001380 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
1381 assert(res == VK_SUCCESS);
1382
Adam Sawicki73b16652018-06-11 16:39:25 +02001383 // Destroy all buffers.
1384 for(size_t i = BUF_COUNT; i--; )
1385 {
1386 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
1387 }
1388}
Adam Sawicki212a4a62018-06-14 15:44:45 +02001389#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02001390
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001391static void TestLinearAllocator()
1392{
1393 wprintf(L"Test linear allocator\n");
1394
1395 RandomNumberGenerator rand{645332};
1396
1397 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1398 sampleBufCreateInfo.size = 1024; // Whatever.
1399 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1400
1401 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1402 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1403
1404 VmaPoolCreateInfo poolCreateInfo = {};
1405 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1406 assert(res == VK_SUCCESS);
1407
Adam Sawickiee082772018-06-20 17:45:49 +02001408 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001409 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1410 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
1411
1412 VmaPool pool = nullptr;
1413 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1414 assert(res == VK_SUCCESS);
1415
1416 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1417
1418 VmaAllocationCreateInfo allocCreateInfo = {};
1419 allocCreateInfo.pool = pool;
1420
1421 constexpr size_t maxBufCount = 100;
1422 std::vector<BufferInfo> bufInfo;
1423
1424 constexpr VkDeviceSize bufSizeMin = 16;
1425 constexpr VkDeviceSize bufSizeMax = 1024;
1426 VmaAllocationInfo allocInfo;
1427 VkDeviceSize prevOffset = 0;
1428
1429 // Test one-time free.
1430 for(size_t i = 0; i < 2; ++i)
1431 {
1432 // Allocate number of buffers of varying size that surely fit into this block.
1433 VkDeviceSize bufSumSize = 0;
1434 for(size_t i = 0; i < maxBufCount; ++i)
1435 {
1436 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1437 BufferInfo newBufInfo;
1438 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1439 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1440 assert(res == VK_SUCCESS);
1441 assert(i == 0 || allocInfo.offset > prevOffset);
1442 bufInfo.push_back(newBufInfo);
1443 prevOffset = allocInfo.offset;
1444 bufSumSize += bufCreateInfo.size;
1445 }
1446
1447 // Validate pool stats.
1448 VmaPoolStats stats;
1449 vmaGetPoolStats(g_hAllocator, pool, &stats);
1450 assert(stats.size == poolCreateInfo.blockSize);
1451 assert(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
1452 assert(stats.allocationCount == bufInfo.size());
1453
1454 // Destroy the buffers in random order.
1455 while(!bufInfo.empty())
1456 {
1457 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1458 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1459 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1460 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1461 }
1462 }
1463
1464 // Test stack.
1465 {
1466 // Allocate number of buffers of varying size that surely fit into this block.
1467 for(size_t i = 0; i < maxBufCount; ++i)
1468 {
1469 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1470 BufferInfo newBufInfo;
1471 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1472 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1473 assert(res == VK_SUCCESS);
1474 assert(i == 0 || allocInfo.offset > prevOffset);
1475 bufInfo.push_back(newBufInfo);
1476 prevOffset = allocInfo.offset;
1477 }
1478
1479 // Destroy few buffers from top of the stack.
1480 for(size_t i = 0; i < maxBufCount / 5; ++i)
1481 {
1482 const BufferInfo& currBufInfo = bufInfo.back();
1483 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1484 bufInfo.pop_back();
1485 }
1486
1487 // Create some more
1488 for(size_t i = 0; i < maxBufCount / 5; ++i)
1489 {
1490 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1491 BufferInfo newBufInfo;
1492 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1493 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1494 assert(res == VK_SUCCESS);
1495 assert(i == 0 || allocInfo.offset > prevOffset);
1496 bufInfo.push_back(newBufInfo);
1497 prevOffset = allocInfo.offset;
1498 }
1499
1500 // Destroy the buffers in reverse order.
1501 while(!bufInfo.empty())
1502 {
1503 const BufferInfo& currBufInfo = bufInfo.back();
1504 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1505 bufInfo.pop_back();
1506 }
1507 }
1508
Adam Sawickiee082772018-06-20 17:45:49 +02001509 // Test ring buffer.
1510 {
1511 // Allocate number of buffers that surely fit into this block.
1512 bufCreateInfo.size = bufSizeMax;
1513 for(size_t i = 0; i < maxBufCount; ++i)
1514 {
1515 BufferInfo newBufInfo;
1516 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1517 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1518 assert(res == VK_SUCCESS);
1519 assert(i == 0 || allocInfo.offset > prevOffset);
1520 bufInfo.push_back(newBufInfo);
1521 prevOffset = allocInfo.offset;
1522 }
1523
1524 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
1525 const size_t buffersPerIter = maxBufCount / 10 - 1;
1526 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
1527 for(size_t iter = 0; iter < iterCount; ++iter)
1528 {
1529 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1530 {
1531 const BufferInfo& currBufInfo = bufInfo.front();
1532 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1533 bufInfo.erase(bufInfo.begin());
1534 }
1535 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1536 {
1537 BufferInfo newBufInfo;
1538 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1539 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1540 assert(res == VK_SUCCESS);
1541 bufInfo.push_back(newBufInfo);
1542 }
1543 }
1544
1545 // Allocate buffers until we reach out-of-memory.
1546 uint32_t debugIndex = 0;
1547 while(res == VK_SUCCESS)
1548 {
1549 BufferInfo newBufInfo;
1550 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1551 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1552 if(res == VK_SUCCESS)
1553 {
1554 bufInfo.push_back(newBufInfo);
1555 }
1556 else
1557 {
1558 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1559 }
1560 ++debugIndex;
1561 }
1562
1563 // Destroy the buffers in random order.
1564 while(!bufInfo.empty())
1565 {
1566 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1567 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1568 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1569 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1570 }
1571 }
1572
Adam Sawicki680b2252018-08-22 14:47:32 +02001573 // Test double stack.
1574 {
1575 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
1576 VkDeviceSize prevOffsetLower = 0;
1577 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
1578 for(size_t i = 0; i < maxBufCount; ++i)
1579 {
1580 const bool upperAddress = (i % 2) != 0;
1581 if(upperAddress)
1582 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1583 else
1584 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1585 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1586 BufferInfo newBufInfo;
1587 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1588 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1589 assert(res == VK_SUCCESS);
1590 if(upperAddress)
1591 {
1592 assert(allocInfo.offset < prevOffsetUpper);
1593 prevOffsetUpper = allocInfo.offset;
1594 }
1595 else
1596 {
1597 assert(allocInfo.offset >= prevOffsetLower);
1598 prevOffsetLower = allocInfo.offset;
1599 }
1600 assert(prevOffsetLower < prevOffsetUpper);
1601 bufInfo.push_back(newBufInfo);
1602 }
1603
1604 // Destroy few buffers from top of the stack.
1605 for(size_t i = 0; i < maxBufCount / 5; ++i)
1606 {
1607 const BufferInfo& currBufInfo = bufInfo.back();
1608 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1609 bufInfo.pop_back();
1610 }
1611
1612 // Create some more
1613 for(size_t i = 0; i < maxBufCount / 5; ++i)
1614 {
1615 const bool upperAddress = (i % 2) != 0;
1616 if(upperAddress)
1617 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1618 else
1619 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1620 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1621 BufferInfo newBufInfo;
1622 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1623 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1624 assert(res == VK_SUCCESS);
1625 bufInfo.push_back(newBufInfo);
1626 }
1627
1628 // Destroy the buffers in reverse order.
1629 while(!bufInfo.empty())
1630 {
1631 const BufferInfo& currBufInfo = bufInfo.back();
1632 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1633 bufInfo.pop_back();
1634 }
1635
1636 // Create buffers on both sides until we reach out of memory.
1637 prevOffsetLower = 0;
1638 prevOffsetUpper = poolCreateInfo.blockSize;
1639 res = VK_SUCCESS;
1640 for(size_t i = 0; res == VK_SUCCESS; ++i)
1641 {
1642 const bool upperAddress = (i % 2) != 0;
1643 if(upperAddress)
1644 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1645 else
1646 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1647 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1648 BufferInfo newBufInfo;
1649 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1650 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1651 if(res == VK_SUCCESS)
1652 {
1653 if(upperAddress)
1654 {
1655 assert(allocInfo.offset < prevOffsetUpper);
1656 prevOffsetUpper = allocInfo.offset;
1657 }
1658 else
1659 {
1660 assert(allocInfo.offset >= prevOffsetLower);
1661 prevOffsetLower = allocInfo.offset;
1662 }
1663 assert(prevOffsetLower < prevOffsetUpper);
1664 bufInfo.push_back(newBufInfo);
1665 }
1666 }
1667
1668 // Destroy the buffers in random order.
1669 while(!bufInfo.empty())
1670 {
1671 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1672 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1673 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1674 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1675 }
1676
1677 // Create buffers on upper side only, constant size, until we reach out of memory.
1678 prevOffsetUpper = poolCreateInfo.blockSize;
1679 res = VK_SUCCESS;
1680 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1681 bufCreateInfo.size = bufSizeMax;
1682 for(size_t i = 0; res == VK_SUCCESS; ++i)
1683 {
1684 BufferInfo newBufInfo;
1685 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1686 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1687 if(res == VK_SUCCESS)
1688 {
1689 assert(allocInfo.offset < prevOffsetUpper);
1690 prevOffsetUpper = allocInfo.offset;
1691 bufInfo.push_back(newBufInfo);
1692 }
1693 }
1694
1695 // Destroy the buffers in reverse order.
1696 while(!bufInfo.empty())
1697 {
1698 const BufferInfo& currBufInfo = bufInfo.back();
1699 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1700 bufInfo.pop_back();
1701 }
1702 }
1703
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001704 vmaDestroyPool(g_hAllocator, pool);
1705}
1706
Adam Sawickib8333fb2018-03-13 16:15:53 +01001707static void TestPool_SameSize()
1708{
1709 const VkDeviceSize BUF_SIZE = 1024 * 1024;
1710 const size_t BUF_COUNT = 100;
1711 VkResult res;
1712
1713 RandomNumberGenerator rand{123};
1714
1715 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1716 bufferInfo.size = BUF_SIZE;
1717 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1718
1719 uint32_t memoryTypeBits = UINT32_MAX;
1720 {
1721 VkBuffer dummyBuffer;
1722 res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
1723 assert(res == VK_SUCCESS);
1724
1725 VkMemoryRequirements memReq;
1726 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
1727 memoryTypeBits = memReq.memoryTypeBits;
1728
1729 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
1730 }
1731
1732 VmaAllocationCreateInfo poolAllocInfo = {};
1733 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1734 uint32_t memTypeIndex;
1735 res = vmaFindMemoryTypeIndex(
1736 g_hAllocator,
1737 memoryTypeBits,
1738 &poolAllocInfo,
1739 &memTypeIndex);
1740
1741 VmaPoolCreateInfo poolCreateInfo = {};
1742 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1743 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
1744 poolCreateInfo.minBlockCount = 1;
1745 poolCreateInfo.maxBlockCount = 4;
1746 poolCreateInfo.frameInUseCount = 0;
1747
1748 VmaPool pool;
1749 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1750 assert(res == VK_SUCCESS);
1751
1752 vmaSetCurrentFrameIndex(g_hAllocator, 1);
1753
1754 VmaAllocationCreateInfo allocInfo = {};
1755 allocInfo.pool = pool;
1756 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1757 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1758
1759 struct BufItem
1760 {
1761 VkBuffer Buf;
1762 VmaAllocation Alloc;
1763 };
1764 std::vector<BufItem> items;
1765
1766 // Fill entire pool.
1767 for(size_t i = 0; i < BUF_COUNT; ++i)
1768 {
1769 BufItem item;
1770 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1771 assert(res == VK_SUCCESS);
1772 items.push_back(item);
1773 }
1774
1775 // Make sure that another allocation would fail.
1776 {
1777 BufItem item;
1778 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1779 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1780 }
1781
1782 // Validate that no buffer is lost. Also check that they are not mapped.
1783 for(size_t i = 0; i < items.size(); ++i)
1784 {
1785 VmaAllocationInfo allocInfo;
1786 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1787 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1788 assert(allocInfo.pMappedData == nullptr);
1789 }
1790
1791 // Free some percent of random items.
1792 {
1793 const size_t PERCENT_TO_FREE = 10;
1794 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
1795 for(size_t i = 0; i < itemsToFree; ++i)
1796 {
1797 size_t index = (size_t)rand.Generate() % items.size();
1798 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
1799 items.erase(items.begin() + index);
1800 }
1801 }
1802
1803 // Randomly allocate and free items.
1804 {
1805 const size_t OPERATION_COUNT = BUF_COUNT;
1806 for(size_t i = 0; i < OPERATION_COUNT; ++i)
1807 {
1808 bool allocate = rand.Generate() % 2 != 0;
1809 if(allocate)
1810 {
1811 if(items.size() < BUF_COUNT)
1812 {
1813 BufItem item;
1814 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1815 assert(res == VK_SUCCESS);
1816 items.push_back(item);
1817 }
1818 }
1819 else // Free
1820 {
1821 if(!items.empty())
1822 {
1823 size_t index = (size_t)rand.Generate() % items.size();
1824 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
1825 items.erase(items.begin() + index);
1826 }
1827 }
1828 }
1829 }
1830
1831 // Allocate up to maximum.
1832 while(items.size() < BUF_COUNT)
1833 {
1834 BufItem item;
1835 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1836 assert(res == VK_SUCCESS);
1837 items.push_back(item);
1838 }
1839
1840 // Validate that no buffer is lost.
1841 for(size_t i = 0; i < items.size(); ++i)
1842 {
1843 VmaAllocationInfo allocInfo;
1844 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1845 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1846 }
1847
1848 // Next frame.
1849 vmaSetCurrentFrameIndex(g_hAllocator, 2);
1850
1851 // Allocate another BUF_COUNT buffers.
1852 for(size_t i = 0; i < BUF_COUNT; ++i)
1853 {
1854 BufItem item;
1855 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1856 assert(res == VK_SUCCESS);
1857 items.push_back(item);
1858 }
1859
1860 // Make sure the first BUF_COUNT is lost. Delete them.
1861 for(size_t i = 0; i < BUF_COUNT; ++i)
1862 {
1863 VmaAllocationInfo allocInfo;
1864 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1865 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1866 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1867 }
1868 items.erase(items.begin(), items.begin() + BUF_COUNT);
1869
1870 // Validate that no buffer is lost.
1871 for(size_t i = 0; i < items.size(); ++i)
1872 {
1873 VmaAllocationInfo allocInfo;
1874 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1875 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1876 }
1877
1878 // Free one item.
1879 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
1880 items.pop_back();
1881
1882 // Validate statistics.
1883 {
1884 VmaPoolStats poolStats = {};
1885 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1886 assert(poolStats.allocationCount == items.size());
1887 assert(poolStats.size = BUF_COUNT * BUF_SIZE);
1888 assert(poolStats.unusedRangeCount == 1);
1889 assert(poolStats.unusedRangeSizeMax == BUF_SIZE);
1890 assert(poolStats.unusedSize == BUF_SIZE);
1891 }
1892
1893 // Free all remaining items.
1894 for(size_t i = items.size(); i--; )
1895 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1896 items.clear();
1897
1898 // Allocate maximum items again.
1899 for(size_t i = 0; i < BUF_COUNT; ++i)
1900 {
1901 BufItem item;
1902 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1903 assert(res == VK_SUCCESS);
1904 items.push_back(item);
1905 }
1906
1907 // Delete every other item.
1908 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
1909 {
1910 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1911 items.erase(items.begin() + i);
1912 }
1913
1914 // Defragment!
1915 {
1916 std::vector<VmaAllocation> allocationsToDefragment(items.size());
1917 for(size_t i = 0; i < items.size(); ++i)
1918 allocationsToDefragment[i] = items[i].Alloc;
1919
1920 VmaDefragmentationStats defragmentationStats;
1921 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
1922 assert(res == VK_SUCCESS);
1923 assert(defragmentationStats.deviceMemoryBlocksFreed == 2);
1924 }
1925
1926 // Free all remaining items.
1927 for(size_t i = items.size(); i--; )
1928 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1929 items.clear();
1930
1931 ////////////////////////////////////////////////////////////////////////////////
1932 // Test for vmaMakePoolAllocationsLost
1933
1934 // Allocate 4 buffers on frame 10.
1935 vmaSetCurrentFrameIndex(g_hAllocator, 10);
1936 for(size_t i = 0; i < 4; ++i)
1937 {
1938 BufItem item;
1939 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1940 assert(res == VK_SUCCESS);
1941 items.push_back(item);
1942 }
1943
1944 // Touch first 2 of them on frame 11.
1945 vmaSetCurrentFrameIndex(g_hAllocator, 11);
1946 for(size_t i = 0; i < 2; ++i)
1947 {
1948 VmaAllocationInfo allocInfo;
1949 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1950 }
1951
1952 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
1953 size_t lostCount = 0xDEADC0DE;
1954 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
1955 assert(lostCount == 2);
1956
1957 // Make another call. Now 0 should be lost.
1958 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
1959 assert(lostCount == 0);
1960
1961 // Make another call, with null count. Should not crash.
1962 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
1963
1964 // END: Free all remaining items.
1965 for(size_t i = items.size(); i--; )
1966 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1967
1968 items.clear();
1969
Adam Sawickid2924172018-06-11 12:48:46 +02001970 ////////////////////////////////////////////////////////////////////////////////
1971 // Test for allocation too large for pool
1972
1973 {
1974 VmaAllocationCreateInfo allocCreateInfo = {};
1975 allocCreateInfo.pool = pool;
1976
1977 VkMemoryRequirements memReq;
1978 memReq.memoryTypeBits = UINT32_MAX;
1979 memReq.alignment = 1;
1980 memReq.size = poolCreateInfo.blockSize + 4;
1981
1982 VmaAllocation alloc = nullptr;
1983 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
1984 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
1985 }
1986
Adam Sawickib8333fb2018-03-13 16:15:53 +01001987 vmaDestroyPool(g_hAllocator, pool);
1988}
1989
Adam Sawickie44c6262018-06-15 14:30:39 +02001990static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
1991{
1992 const uint8_t* pBytes = (const uint8_t*)pMemory;
1993 for(size_t i = 0; i < size; ++i)
1994 {
1995 if(pBytes[i] != pattern)
1996 {
1997 return false;
1998 }
1999 }
2000 return true;
2001}
2002
2003static void TestAllocationsInitialization()
2004{
2005 VkResult res;
2006
2007 const size_t BUF_SIZE = 1024;
2008
2009 // Create pool.
2010
2011 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2012 bufInfo.size = BUF_SIZE;
2013 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2014
2015 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
2016 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2017
2018 VmaPoolCreateInfo poolCreateInfo = {};
2019 poolCreateInfo.blockSize = BUF_SIZE * 10;
2020 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
2021 poolCreateInfo.maxBlockCount = 1;
2022 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2023 assert(res == VK_SUCCESS);
2024
2025 VmaAllocationCreateInfo bufAllocCreateInfo = {};
2026 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
2027 assert(res == VK_SUCCESS);
2028
2029 // Create one persistently mapped buffer to keep memory of this block mapped,
2030 // so that pointer to mapped data will remain (more or less...) valid even
2031 // after destruction of other allocations.
2032
2033 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2034 VkBuffer firstBuf;
2035 VmaAllocation firstAlloc;
2036 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
2037 assert(res == VK_SUCCESS);
2038
2039 // Test buffers.
2040
2041 for(uint32_t i = 0; i < 2; ++i)
2042 {
2043 const bool persistentlyMapped = i == 0;
2044 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
2045 VkBuffer buf;
2046 VmaAllocation alloc;
2047 VmaAllocationInfo allocInfo;
2048 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
2049 assert(res == VK_SUCCESS);
2050
2051 void* pMappedData;
2052 if(!persistentlyMapped)
2053 {
2054 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
2055 assert(res == VK_SUCCESS);
2056 }
2057 else
2058 {
2059 pMappedData = allocInfo.pMappedData;
2060 }
2061
2062 // Validate initialized content
2063 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
2064 assert(valid);
2065
2066 if(!persistentlyMapped)
2067 {
2068 vmaUnmapMemory(g_hAllocator, alloc);
2069 }
2070
2071 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2072
2073 // Validate freed content
2074 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
2075 assert(valid);
2076 }
2077
2078 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
2079 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
2080}
2081
Adam Sawickib8333fb2018-03-13 16:15:53 +01002082static void TestPool_Benchmark(
2083 PoolTestResult& outResult,
2084 const PoolTestConfig& config)
2085{
2086 assert(config.ThreadCount > 0);
2087
2088 RandomNumberGenerator mainRand{config.RandSeed};
2089
2090 uint32_t allocationSizeProbabilitySum = std::accumulate(
2091 config.AllocationSizes.begin(),
2092 config.AllocationSizes.end(),
2093 0u,
2094 [](uint32_t sum, const AllocationSize& allocSize) {
2095 return sum + allocSize.Probability;
2096 });
2097
2098 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2099 bufferInfo.size = 256; // Whatever.
2100 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2101
2102 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2103 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2104 imageInfo.extent.width = 256; // Whatever.
2105 imageInfo.extent.height = 256; // Whatever.
2106 imageInfo.extent.depth = 1;
2107 imageInfo.mipLevels = 1;
2108 imageInfo.arrayLayers = 1;
2109 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2110 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
2111 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2112 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
2113 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2114
2115 uint32_t bufferMemoryTypeBits = UINT32_MAX;
2116 {
2117 VkBuffer dummyBuffer;
2118 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2119 assert(res == VK_SUCCESS);
2120
2121 VkMemoryRequirements memReq;
2122 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2123 bufferMemoryTypeBits = memReq.memoryTypeBits;
2124
2125 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2126 }
2127
2128 uint32_t imageMemoryTypeBits = UINT32_MAX;
2129 {
2130 VkImage dummyImage;
2131 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
2132 assert(res == VK_SUCCESS);
2133
2134 VkMemoryRequirements memReq;
2135 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
2136 imageMemoryTypeBits = memReq.memoryTypeBits;
2137
2138 vkDestroyImage(g_hDevice, dummyImage, nullptr);
2139 }
2140
2141 uint32_t memoryTypeBits = 0;
2142 if(config.UsesBuffers() && config.UsesImages())
2143 {
2144 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
2145 if(memoryTypeBits == 0)
2146 {
2147 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
2148 return;
2149 }
2150 }
2151 else if(config.UsesBuffers())
2152 memoryTypeBits = bufferMemoryTypeBits;
2153 else if(config.UsesImages())
2154 memoryTypeBits = imageMemoryTypeBits;
2155 else
2156 assert(0);
2157
2158 VmaPoolCreateInfo poolCreateInfo = {};
2159 poolCreateInfo.memoryTypeIndex = 0;
2160 poolCreateInfo.minBlockCount = 1;
2161 poolCreateInfo.maxBlockCount = 1;
2162 poolCreateInfo.blockSize = config.PoolSize;
2163 poolCreateInfo.frameInUseCount = 1;
2164
2165 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
2166 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2167 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2168
2169 VmaPool pool;
2170 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2171 assert(res == VK_SUCCESS);
2172
2173 // Start time measurement - after creating pool and initializing data structures.
2174 time_point timeBeg = std::chrono::high_resolution_clock::now();
2175
2176 ////////////////////////////////////////////////////////////////////////////////
2177 // ThreadProc
2178 auto ThreadProc = [&](
2179 PoolTestThreadResult* outThreadResult,
2180 uint32_t randSeed,
2181 HANDLE frameStartEvent,
2182 HANDLE frameEndEvent) -> void
2183 {
2184 RandomNumberGenerator threadRand{randSeed};
2185
2186 outThreadResult->AllocationTimeMin = duration::max();
2187 outThreadResult->AllocationTimeSum = duration::zero();
2188 outThreadResult->AllocationTimeMax = duration::min();
2189 outThreadResult->DeallocationTimeMin = duration::max();
2190 outThreadResult->DeallocationTimeSum = duration::zero();
2191 outThreadResult->DeallocationTimeMax = duration::min();
2192 outThreadResult->AllocationCount = 0;
2193 outThreadResult->DeallocationCount = 0;
2194 outThreadResult->LostAllocationCount = 0;
2195 outThreadResult->LostAllocationTotalSize = 0;
2196 outThreadResult->FailedAllocationCount = 0;
2197 outThreadResult->FailedAllocationTotalSize = 0;
2198
2199 struct Item
2200 {
2201 VkDeviceSize BufferSize;
2202 VkExtent2D ImageSize;
2203 VkBuffer Buf;
2204 VkImage Image;
2205 VmaAllocation Alloc;
2206
2207 VkDeviceSize CalcSizeBytes() const
2208 {
2209 return BufferSize +
2210 ImageSize.width * ImageSize.height * 4;
2211 }
2212 };
2213 std::vector<Item> unusedItems, usedItems;
2214
2215 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
2216
2217 // Create all items - all unused, not yet allocated.
2218 for(size_t i = 0; i < threadTotalItemCount; ++i)
2219 {
2220 Item item = {};
2221
2222 uint32_t allocSizeIndex = 0;
2223 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
2224 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
2225 r -= config.AllocationSizes[allocSizeIndex++].Probability;
2226
2227 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
2228 if(allocSize.BufferSizeMax > 0)
2229 {
2230 assert(allocSize.BufferSizeMin > 0);
2231 assert(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
2232 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
2233 item.BufferSize = allocSize.BufferSizeMin;
2234 else
2235 {
2236 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
2237 item.BufferSize = item.BufferSize / 16 * 16;
2238 }
2239 }
2240 else
2241 {
2242 assert(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
2243 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
2244 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
2245 else
2246 {
2247 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2248 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2249 }
2250 }
2251
2252 unusedItems.push_back(item);
2253 }
2254
2255 auto Allocate = [&](Item& item) -> VkResult
2256 {
2257 VmaAllocationCreateInfo allocCreateInfo = {};
2258 allocCreateInfo.pool = pool;
2259 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2260 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2261
2262 if(item.BufferSize)
2263 {
2264 bufferInfo.size = item.BufferSize;
2265 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2266 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
2267 }
2268 else
2269 {
2270 assert(item.ImageSize.width && item.ImageSize.height);
2271
2272 imageInfo.extent.width = item.ImageSize.width;
2273 imageInfo.extent.height = item.ImageSize.height;
2274 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2275 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
2276 }
2277 };
2278
2279 ////////////////////////////////////////////////////////////////////////////////
2280 // Frames
2281 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2282 {
2283 WaitForSingleObject(frameStartEvent, INFINITE);
2284
2285 // Always make some percent of used bufs unused, to choose different used ones.
2286 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
2287 for(size_t i = 0; i < bufsToMakeUnused; ++i)
2288 {
2289 size_t index = threadRand.Generate() % usedItems.size();
2290 unusedItems.push_back(usedItems[index]);
2291 usedItems.erase(usedItems.begin() + index);
2292 }
2293
2294 // Determine which bufs we want to use in this frame.
2295 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
2296 / config.ThreadCount;
2297 assert(usedBufCount < usedItems.size() + unusedItems.size());
2298 // Move some used to unused.
2299 while(usedBufCount < usedItems.size())
2300 {
2301 size_t index = threadRand.Generate() % usedItems.size();
2302 unusedItems.push_back(usedItems[index]);
2303 usedItems.erase(usedItems.begin() + index);
2304 }
2305 // Move some unused to used.
2306 while(usedBufCount > usedItems.size())
2307 {
2308 size_t index = threadRand.Generate() % unusedItems.size();
2309 usedItems.push_back(unusedItems[index]);
2310 unusedItems.erase(unusedItems.begin() + index);
2311 }
2312
2313 uint32_t touchExistingCount = 0;
2314 uint32_t touchLostCount = 0;
2315 uint32_t createSucceededCount = 0;
2316 uint32_t createFailedCount = 0;
2317
2318 // Touch all used bufs. If not created or lost, allocate.
2319 for(size_t i = 0; i < usedItems.size(); ++i)
2320 {
2321 Item& item = usedItems[i];
2322 // Not yet created.
2323 if(item.Alloc == VK_NULL_HANDLE)
2324 {
2325 res = Allocate(item);
2326 ++outThreadResult->AllocationCount;
2327 if(res != VK_SUCCESS)
2328 {
2329 item.Alloc = VK_NULL_HANDLE;
2330 item.Buf = VK_NULL_HANDLE;
2331 ++outThreadResult->FailedAllocationCount;
2332 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
2333 ++createFailedCount;
2334 }
2335 else
2336 ++createSucceededCount;
2337 }
2338 else
2339 {
2340 // Touch.
2341 VmaAllocationInfo allocInfo;
2342 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
2343 // Lost.
2344 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
2345 {
2346 ++touchLostCount;
2347
2348 // Destroy.
2349 {
2350 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2351 if(item.Buf)
2352 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
2353 else
2354 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
2355 ++outThreadResult->DeallocationCount;
2356 }
2357 item.Alloc = VK_NULL_HANDLE;
2358 item.Buf = VK_NULL_HANDLE;
2359
2360 ++outThreadResult->LostAllocationCount;
2361 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
2362
2363 // Recreate.
2364 res = Allocate(item);
2365 ++outThreadResult->AllocationCount;
2366 // Creation failed.
2367 if(res != VK_SUCCESS)
2368 {
2369 ++outThreadResult->FailedAllocationCount;
2370 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
2371 ++createFailedCount;
2372 }
2373 else
2374 ++createSucceededCount;
2375 }
2376 else
2377 ++touchExistingCount;
2378 }
2379 }
2380
2381 /*
2382 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
2383 randSeed, frameIndex,
2384 touchExistingCount, touchLostCount,
2385 createSucceededCount, createFailedCount);
2386 */
2387
2388 SetEvent(frameEndEvent);
2389 }
2390
2391 // Free all remaining items.
2392 for(size_t i = usedItems.size(); i--; )
2393 {
2394 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2395 if(usedItems[i].Buf)
2396 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
2397 else
2398 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
2399 ++outThreadResult->DeallocationCount;
2400 }
2401 for(size_t i = unusedItems.size(); i--; )
2402 {
2403 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
2404 if(unusedItems[i].Buf)
2405 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
2406 else
2407 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
2408 ++outThreadResult->DeallocationCount;
2409 }
2410 };
2411
2412 // Launch threads.
2413 uint32_t threadRandSeed = mainRand.Generate();
2414 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
2415 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
2416 std::vector<std::thread> bkgThreads;
2417 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
2418 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2419 {
2420 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2421 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2422 bkgThreads.emplace_back(std::bind(
2423 ThreadProc,
2424 &threadResults[threadIndex],
2425 threadRandSeed + threadIndex,
2426 frameStartEvents[threadIndex],
2427 frameEndEvents[threadIndex]));
2428 }
2429
2430 // Execute frames.
2431 assert(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
2432 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2433 {
2434 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
2435 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2436 SetEvent(frameStartEvents[threadIndex]);
2437 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
2438 }
2439
2440 // Wait for threads finished
2441 for(size_t i = 0; i < bkgThreads.size(); ++i)
2442 {
2443 bkgThreads[i].join();
2444 CloseHandle(frameEndEvents[i]);
2445 CloseHandle(frameStartEvents[i]);
2446 }
2447 bkgThreads.clear();
2448
2449 // Finish time measurement - before destroying pool.
2450 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
2451
2452 vmaDestroyPool(g_hAllocator, pool);
2453
2454 outResult.AllocationTimeMin = duration::max();
2455 outResult.AllocationTimeAvg = duration::zero();
2456 outResult.AllocationTimeMax = duration::min();
2457 outResult.DeallocationTimeMin = duration::max();
2458 outResult.DeallocationTimeAvg = duration::zero();
2459 outResult.DeallocationTimeMax = duration::min();
2460 outResult.LostAllocationCount = 0;
2461 outResult.LostAllocationTotalSize = 0;
2462 outResult.FailedAllocationCount = 0;
2463 outResult.FailedAllocationTotalSize = 0;
2464 size_t allocationCount = 0;
2465 size_t deallocationCount = 0;
2466 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2467 {
2468 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
2469 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
2470 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
2471 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
2472 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
2473 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
2474 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
2475 allocationCount += threadResult.AllocationCount;
2476 deallocationCount += threadResult.DeallocationCount;
2477 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
2478 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
2479 outResult.LostAllocationCount += threadResult.LostAllocationCount;
2480 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
2481 }
2482 if(allocationCount)
2483 outResult.AllocationTimeAvg /= allocationCount;
2484 if(deallocationCount)
2485 outResult.DeallocationTimeAvg /= deallocationCount;
2486}
2487
2488static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
2489{
2490 if(ptr1 < ptr2)
2491 return ptr1 + size1 > ptr2;
2492 else if(ptr2 < ptr1)
2493 return ptr2 + size2 > ptr1;
2494 else
2495 return true;
2496}
2497
2498static void TestMapping()
2499{
2500 wprintf(L"Testing mapping...\n");
2501
2502 VkResult res;
2503 uint32_t memTypeIndex = UINT32_MAX;
2504
2505 enum TEST
2506 {
2507 TEST_NORMAL,
2508 TEST_POOL,
2509 TEST_DEDICATED,
2510 TEST_COUNT
2511 };
2512 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
2513 {
2514 VmaPool pool = nullptr;
2515 if(testIndex == TEST_POOL)
2516 {
2517 assert(memTypeIndex != UINT32_MAX);
2518 VmaPoolCreateInfo poolInfo = {};
2519 poolInfo.memoryTypeIndex = memTypeIndex;
2520 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
2521 assert(res == VK_SUCCESS);
2522 }
2523
2524 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2525 bufInfo.size = 0x10000;
2526 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2527
2528 VmaAllocationCreateInfo allocCreateInfo = {};
2529 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2530 allocCreateInfo.pool = pool;
2531 if(testIndex == TEST_DEDICATED)
2532 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2533
2534 VmaAllocationInfo allocInfo;
2535
2536 // Mapped manually
2537
2538 // Create 2 buffers.
2539 BufferInfo bufferInfos[3];
2540 for(size_t i = 0; i < 2; ++i)
2541 {
2542 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
2543 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
2544 assert(res == VK_SUCCESS);
2545 assert(allocInfo.pMappedData == nullptr);
2546 memTypeIndex = allocInfo.memoryType;
2547 }
2548
2549 // Map buffer 0.
2550 char* data00 = nullptr;
2551 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
2552 assert(res == VK_SUCCESS && data00 != nullptr);
2553 data00[0xFFFF] = data00[0];
2554
2555 // Map buffer 0 second time.
2556 char* data01 = nullptr;
2557 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
2558 assert(res == VK_SUCCESS && data01 == data00);
2559
2560 // Map buffer 1.
2561 char* data1 = nullptr;
2562 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
2563 assert(res == VK_SUCCESS && data1 != nullptr);
2564 assert(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
2565 data1[0xFFFF] = data1[0];
2566
2567 // Unmap buffer 0 two times.
2568 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
2569 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
2570 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
2571 assert(allocInfo.pMappedData == nullptr);
2572
2573 // Unmap buffer 1.
2574 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
2575 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
2576 assert(allocInfo.pMappedData == nullptr);
2577
2578 // Create 3rd buffer - persistently mapped.
2579 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
2580 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
2581 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
2582 assert(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
2583
2584 // Map buffer 2.
2585 char* data2 = nullptr;
2586 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
2587 assert(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
2588 data2[0xFFFF] = data2[0];
2589
2590 // Unmap buffer 2.
2591 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
2592 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
2593 assert(allocInfo.pMappedData == data2);
2594
2595 // Destroy all buffers.
2596 for(size_t i = 3; i--; )
2597 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
2598
2599 vmaDestroyPool(g_hAllocator, pool);
2600 }
2601}
2602
2603static void TestMappingMultithreaded()
2604{
2605 wprintf(L"Testing mapping multithreaded...\n");
2606
2607 static const uint32_t threadCount = 16;
2608 static const uint32_t bufferCount = 1024;
2609 static const uint32_t threadBufferCount = bufferCount / threadCount;
2610
2611 VkResult res;
2612 volatile uint32_t memTypeIndex = UINT32_MAX;
2613
2614 enum TEST
2615 {
2616 TEST_NORMAL,
2617 TEST_POOL,
2618 TEST_DEDICATED,
2619 TEST_COUNT
2620 };
2621 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
2622 {
2623 VmaPool pool = nullptr;
2624 if(testIndex == TEST_POOL)
2625 {
2626 assert(memTypeIndex != UINT32_MAX);
2627 VmaPoolCreateInfo poolInfo = {};
2628 poolInfo.memoryTypeIndex = memTypeIndex;
2629 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
2630 assert(res == VK_SUCCESS);
2631 }
2632
2633 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2634 bufCreateInfo.size = 0x10000;
2635 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2636
2637 VmaAllocationCreateInfo allocCreateInfo = {};
2638 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2639 allocCreateInfo.pool = pool;
2640 if(testIndex == TEST_DEDICATED)
2641 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2642
2643 std::thread threads[threadCount];
2644 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
2645 {
2646 threads[threadIndex] = std::thread([=, &memTypeIndex](){
2647 // ======== THREAD FUNCTION ========
2648
2649 RandomNumberGenerator rand{threadIndex};
2650
2651 enum class MODE
2652 {
2653 // Don't map this buffer at all.
2654 DONT_MAP,
2655 // Map and quickly unmap.
2656 MAP_FOR_MOMENT,
2657 // Map and unmap before destruction.
2658 MAP_FOR_LONGER,
2659 // Map two times. Quickly unmap, second unmap before destruction.
2660 MAP_TWO_TIMES,
2661 // Create this buffer as persistently mapped.
2662 PERSISTENTLY_MAPPED,
2663 COUNT
2664 };
2665 std::vector<BufferInfo> bufInfos{threadBufferCount};
2666 std::vector<MODE> bufModes{threadBufferCount};
2667
2668 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
2669 {
2670 BufferInfo& bufInfo = bufInfos[bufferIndex];
2671 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
2672 bufModes[bufferIndex] = mode;
2673
2674 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
2675 if(mode == MODE::PERSISTENTLY_MAPPED)
2676 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
2677
2678 VmaAllocationInfo allocInfo;
2679 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
2680 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
2681 assert(res == VK_SUCCESS);
2682
2683 if(memTypeIndex == UINT32_MAX)
2684 memTypeIndex = allocInfo.memoryType;
2685
2686 char* data = nullptr;
2687
2688 if(mode == MODE::PERSISTENTLY_MAPPED)
2689 {
2690 data = (char*)allocInfo.pMappedData;
2691 assert(data != nullptr);
2692 }
2693 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
2694 mode == MODE::MAP_TWO_TIMES)
2695 {
2696 assert(data == nullptr);
2697 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
2698 assert(res == VK_SUCCESS && data != nullptr);
2699
2700 if(mode == MODE::MAP_TWO_TIMES)
2701 {
2702 char* data2 = nullptr;
2703 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
2704 assert(res == VK_SUCCESS && data2 == data);
2705 }
2706 }
2707 else if(mode == MODE::DONT_MAP)
2708 {
2709 assert(allocInfo.pMappedData == nullptr);
2710 }
2711 else
2712 assert(0);
2713
2714 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
2715 if(data)
2716 data[0xFFFF] = data[0];
2717
2718 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
2719 {
2720 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
2721
2722 VmaAllocationInfo allocInfo;
2723 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
2724 if(mode == MODE::MAP_FOR_MOMENT)
2725 assert(allocInfo.pMappedData == nullptr);
2726 else
2727 assert(allocInfo.pMappedData == data);
2728 }
2729
2730 switch(rand.Generate() % 3)
2731 {
2732 case 0: Sleep(0); break; // Yield.
2733 case 1: Sleep(10); break; // 10 ms
2734 // default: No sleep.
2735 }
2736
2737 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
2738 if(data)
2739 data[0xFFFF] = data[0];
2740 }
2741
2742 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
2743 {
2744 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
2745 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
2746 {
2747 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
2748
2749 VmaAllocationInfo allocInfo;
2750 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
2751 assert(allocInfo.pMappedData == nullptr);
2752 }
2753
2754 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
2755 }
2756 });
2757 }
2758
2759 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
2760 threads[threadIndex].join();
2761
2762 vmaDestroyPool(g_hAllocator, pool);
2763 }
2764}
2765
2766static void WriteMainTestResultHeader(FILE* file)
2767{
2768 fprintf(file,
2769 "Code,Test,Time,"
2770 "Config,"
2771 "Total Time (us),"
2772 "Allocation Time Min (us),"
2773 "Allocation Time Avg (us),"
2774 "Allocation Time Max (us),"
2775 "Deallocation Time Min (us),"
2776 "Deallocation Time Avg (us),"
2777 "Deallocation Time Max (us),"
2778 "Total Memory Allocated (B),"
2779 "Free Range Size Avg (B),"
2780 "Free Range Size Max (B)\n");
2781}
2782
2783static void WriteMainTestResult(
2784 FILE* file,
2785 const char* codeDescription,
2786 const char* testDescription,
2787 const Config& config, const Result& result)
2788{
2789 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
2790 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
2791 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
2792 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
2793 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
2794 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
2795 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
2796
2797 time_t rawTime; time(&rawTime);
2798 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
2799 char timeStr[128];
2800 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
2801
2802 fprintf(file,
2803 "%s,%s,%s,"
2804 "BeginBytesToAllocate=%I64u MaxBytesToAllocate=%I64u AdditionalOperationCount=%u ThreadCount=%u FreeOrder=%d,"
2805 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
2806 codeDescription,
2807 testDescription,
2808 timeStr,
2809 config.BeginBytesToAllocate, config.MaxBytesToAllocate, config.AdditionalOperationCount, config.ThreadCount, (uint32_t)config.FreeOrder,
2810 totalTimeSeconds * 1e6f,
2811 allocationTimeMinSeconds * 1e6f,
2812 allocationTimeAvgSeconds * 1e6f,
2813 allocationTimeMaxSeconds * 1e6f,
2814 deallocationTimeMinSeconds * 1e6f,
2815 deallocationTimeAvgSeconds * 1e6f,
2816 deallocationTimeMaxSeconds * 1e6f,
2817 result.TotalMemoryAllocated,
2818 result.FreeRangeSizeAvg,
2819 result.FreeRangeSizeMax);
2820}
2821
2822static void WritePoolTestResultHeader(FILE* file)
2823{
2824 fprintf(file,
2825 "Code,Test,Time,"
2826 "Config,"
2827 "Total Time (us),"
2828 "Allocation Time Min (us),"
2829 "Allocation Time Avg (us),"
2830 "Allocation Time Max (us),"
2831 "Deallocation Time Min (us),"
2832 "Deallocation Time Avg (us),"
2833 "Deallocation Time Max (us),"
2834 "Lost Allocation Count,"
2835 "Lost Allocation Total Size (B),"
2836 "Failed Allocation Count,"
2837 "Failed Allocation Total Size (B)\n");
2838}
2839
2840static void WritePoolTestResult(
2841 FILE* file,
2842 const char* codeDescription,
2843 const char* testDescription,
2844 const PoolTestConfig& config,
2845 const PoolTestResult& result)
2846{
2847 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
2848 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
2849 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
2850 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
2851 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
2852 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
2853 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
2854
2855 time_t rawTime; time(&rawTime);
2856 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
2857 char timeStr[128];
2858 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
2859
2860 fprintf(file,
2861 "%s,%s,%s,"
2862 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
2863 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
2864 // General
2865 codeDescription,
2866 testDescription,
2867 timeStr,
2868 // Config
2869 config.ThreadCount,
2870 (unsigned long long)config.PoolSize,
2871 config.FrameCount,
2872 config.TotalItemCount,
2873 config.UsedItemCountMin,
2874 config.UsedItemCountMax,
2875 config.ItemsToMakeUnusedPercent,
2876 // Results
2877 totalTimeSeconds * 1e6f,
2878 allocationTimeMinSeconds * 1e6f,
2879 allocationTimeAvgSeconds * 1e6f,
2880 allocationTimeMaxSeconds * 1e6f,
2881 deallocationTimeMinSeconds * 1e6f,
2882 deallocationTimeAvgSeconds * 1e6f,
2883 deallocationTimeMaxSeconds * 1e6f,
2884 result.LostAllocationCount,
2885 result.LostAllocationTotalSize,
2886 result.FailedAllocationCount,
2887 result.FailedAllocationTotalSize);
2888}
2889
2890static void PerformCustomMainTest(FILE* file)
2891{
2892 Config config{};
2893 config.RandSeed = 65735476;
2894 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
2895 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
2896 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
2897 config.FreeOrder = FREE_ORDER::FORWARD;
2898 config.ThreadCount = 16;
2899 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
2900
2901 // Buffers
2902 //config.AllocationSizes.push_back({4, 16, 1024});
2903 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
2904
2905 // Images
2906 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
2907 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
2908
2909 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
2910 config.AdditionalOperationCount = 1024;
2911
2912 Result result{};
2913 VkResult res = MainTest(result, config);
2914 assert(res == VK_SUCCESS);
2915 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
2916}
2917
2918static void PerformCustomPoolTest(FILE* file)
2919{
2920 PoolTestConfig config;
2921 config.PoolSize = 100 * 1024 * 1024;
2922 config.RandSeed = 2345764;
2923 config.ThreadCount = 1;
2924 config.FrameCount = 200;
2925 config.ItemsToMakeUnusedPercent = 2;
2926
2927 AllocationSize allocSize = {};
2928 allocSize.BufferSizeMin = 1024;
2929 allocSize.BufferSizeMax = 1024 * 1024;
2930 allocSize.Probability = 1;
2931 config.AllocationSizes.push_back(allocSize);
2932
2933 allocSize.BufferSizeMin = 0;
2934 allocSize.BufferSizeMax = 0;
2935 allocSize.ImageSizeMin = 128;
2936 allocSize.ImageSizeMax = 1024;
2937 allocSize.Probability = 1;
2938 config.AllocationSizes.push_back(allocSize);
2939
2940 config.PoolSize = config.CalcAvgResourceSize() * 200;
2941 config.UsedItemCountMax = 160;
2942 config.TotalItemCount = config.UsedItemCountMax * 10;
2943 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
2944
2945 g_MemoryAliasingWarningEnabled = false;
2946 PoolTestResult result = {};
2947 TestPool_Benchmark(result, config);
2948 g_MemoryAliasingWarningEnabled = true;
2949
2950 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
2951}
2952
2953enum CONFIG_TYPE {
2954 CONFIG_TYPE_MINIMUM,
2955 CONFIG_TYPE_SMALL,
2956 CONFIG_TYPE_AVERAGE,
2957 CONFIG_TYPE_LARGE,
2958 CONFIG_TYPE_MAXIMUM,
2959 CONFIG_TYPE_COUNT
2960};
2961
2962static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
2963//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
2964static const char* CODE_DESCRIPTION = "Foo";
2965
2966static void PerformMainTests(FILE* file)
2967{
2968 uint32_t repeatCount = 1;
2969 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
2970
2971 Config config{};
2972 config.RandSeed = 65735476;
2973 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
2974 config.FreeOrder = FREE_ORDER::FORWARD;
2975
2976 size_t threadCountCount = 1;
2977 switch(ConfigType)
2978 {
2979 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
2980 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
2981 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
2982 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
2983 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
2984 default: assert(0);
2985 }
2986 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
2987 {
2988 std::string desc1;
2989
2990 switch(threadCountIndex)
2991 {
2992 case 0:
2993 desc1 += "1_thread";
2994 config.ThreadCount = 1;
2995 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
2996 break;
2997 case 1:
2998 desc1 += "16_threads+0%_common";
2999 config.ThreadCount = 16;
3000 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3001 break;
3002 case 2:
3003 desc1 += "16_threads+50%_common";
3004 config.ThreadCount = 16;
3005 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3006 break;
3007 case 3:
3008 desc1 += "16_threads+100%_common";
3009 config.ThreadCount = 16;
3010 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3011 break;
3012 case 4:
3013 desc1 += "2_threads+0%_common";
3014 config.ThreadCount = 2;
3015 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3016 break;
3017 case 5:
3018 desc1 += "2_threads+50%_common";
3019 config.ThreadCount = 2;
3020 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3021 break;
3022 case 6:
3023 desc1 += "2_threads+100%_common";
3024 config.ThreadCount = 2;
3025 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3026 break;
3027 default:
3028 assert(0);
3029 }
3030
3031 // 0 = buffers, 1 = images, 2 = buffers and images
3032 size_t buffersVsImagesCount = 2;
3033 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3034 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3035 {
3036 std::string desc2 = desc1;
3037 switch(buffersVsImagesIndex)
3038 {
3039 case 0: desc2 += " Buffers"; break;
3040 case 1: desc2 += " Images"; break;
3041 case 2: desc2 += " Buffers+Images"; break;
3042 default: assert(0);
3043 }
3044
3045 // 0 = small, 1 = large, 2 = small and large
3046 size_t smallVsLargeCount = 2;
3047 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3048 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3049 {
3050 std::string desc3 = desc2;
3051 switch(smallVsLargeIndex)
3052 {
3053 case 0: desc3 += " Small"; break;
3054 case 1: desc3 += " Large"; break;
3055 case 2: desc3 += " Small+Large"; break;
3056 default: assert(0);
3057 }
3058
3059 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3060 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3061 else
3062 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
3063
3064 // 0 = varying sizes min...max, 1 = set of constant sizes
3065 size_t constantSizesCount = 1;
3066 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3067 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3068 {
3069 std::string desc4 = desc3;
3070 switch(constantSizesIndex)
3071 {
3072 case 0: desc4 += " Varying_sizes"; break;
3073 case 1: desc4 += " Constant_sizes"; break;
3074 default: assert(0);
3075 }
3076
3077 config.AllocationSizes.clear();
3078 // Buffers present
3079 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3080 {
3081 // Small
3082 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3083 {
3084 // Varying size
3085 if(constantSizesIndex == 0)
3086 config.AllocationSizes.push_back({4, 16, 1024});
3087 // Constant sizes
3088 else
3089 {
3090 config.AllocationSizes.push_back({1, 16, 16});
3091 config.AllocationSizes.push_back({1, 64, 64});
3092 config.AllocationSizes.push_back({1, 256, 256});
3093 config.AllocationSizes.push_back({1, 1024, 1024});
3094 }
3095 }
3096 // Large
3097 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3098 {
3099 // Varying size
3100 if(constantSizesIndex == 0)
3101 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3102 // Constant sizes
3103 else
3104 {
3105 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3106 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3107 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3108 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3109 }
3110 }
3111 }
3112 // Images present
3113 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3114 {
3115 // Small
3116 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3117 {
3118 // Varying size
3119 if(constantSizesIndex == 0)
3120 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3121 // Constant sizes
3122 else
3123 {
3124 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3125 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3126 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3127 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3128 }
3129 }
3130 // Large
3131 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3132 {
3133 // Varying size
3134 if(constantSizesIndex == 0)
3135 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3136 // Constant sizes
3137 else
3138 {
3139 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3140 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3141 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3142 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3143 }
3144 }
3145 }
3146
3147 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
3148 size_t beginBytesToAllocateCount = 1;
3149 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
3150 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
3151 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
3152 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
3153 {
3154 std::string desc5 = desc4;
3155
3156 switch(beginBytesToAllocateIndex)
3157 {
3158 case 0:
3159 desc5 += " Allocate_100%";
3160 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
3161 config.AdditionalOperationCount = 0;
3162 break;
3163 case 1:
3164 desc5 += " Allocate_50%+Operations";
3165 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
3166 config.AdditionalOperationCount = 1024;
3167 break;
3168 case 2:
3169 desc5 += " Allocate_5%+Operations";
3170 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3171 config.AdditionalOperationCount = 1024;
3172 break;
3173 case 3:
3174 desc5 += " Allocate_95%+Operations";
3175 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
3176 config.AdditionalOperationCount = 1024;
3177 break;
3178 default:
3179 assert(0);
3180 }
3181
3182 const char* testDescription = desc5.c_str();
3183
3184 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3185 {
3186 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3187
3188 Result result{};
3189 VkResult res = MainTest(result, config);
3190 assert(res == VK_SUCCESS);
3191 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3192 }
3193 }
3194 }
3195 }
3196 }
3197 }
3198}
3199
3200static void PerformPoolTests(FILE* file)
3201{
3202 const size_t AVG_RESOURCES_PER_POOL = 300;
3203
3204 uint32_t repeatCount = 1;
3205 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3206
3207 PoolTestConfig config{};
3208 config.RandSeed = 2346343;
3209 config.FrameCount = 200;
3210 config.ItemsToMakeUnusedPercent = 2;
3211
3212 size_t threadCountCount = 1;
3213 switch(ConfigType)
3214 {
3215 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3216 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3217 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
3218 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
3219 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
3220 default: assert(0);
3221 }
3222 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3223 {
3224 std::string desc1;
3225
3226 switch(threadCountIndex)
3227 {
3228 case 0:
3229 desc1 += "1_thread";
3230 config.ThreadCount = 1;
3231 break;
3232 case 1:
3233 desc1 += "16_threads";
3234 config.ThreadCount = 16;
3235 break;
3236 case 2:
3237 desc1 += "2_threads";
3238 config.ThreadCount = 2;
3239 break;
3240 default:
3241 assert(0);
3242 }
3243
3244 // 0 = buffers, 1 = images, 2 = buffers and images
3245 size_t buffersVsImagesCount = 2;
3246 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3247 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3248 {
3249 std::string desc2 = desc1;
3250 switch(buffersVsImagesIndex)
3251 {
3252 case 0: desc2 += " Buffers"; break;
3253 case 1: desc2 += " Images"; break;
3254 case 2: desc2 += " Buffers+Images"; break;
3255 default: assert(0);
3256 }
3257
3258 // 0 = small, 1 = large, 2 = small and large
3259 size_t smallVsLargeCount = 2;
3260 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3261 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3262 {
3263 std::string desc3 = desc2;
3264 switch(smallVsLargeIndex)
3265 {
3266 case 0: desc3 += " Small"; break;
3267 case 1: desc3 += " Large"; break;
3268 case 2: desc3 += " Small+Large"; break;
3269 default: assert(0);
3270 }
3271
3272 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3273 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
3274 else
3275 config.PoolSize = 4ull * 1024 * 1024;
3276
3277 // 0 = varying sizes min...max, 1 = set of constant sizes
3278 size_t constantSizesCount = 1;
3279 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3280 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3281 {
3282 std::string desc4 = desc3;
3283 switch(constantSizesIndex)
3284 {
3285 case 0: desc4 += " Varying_sizes"; break;
3286 case 1: desc4 += " Constant_sizes"; break;
3287 default: assert(0);
3288 }
3289
3290 config.AllocationSizes.clear();
3291 // Buffers present
3292 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3293 {
3294 // Small
3295 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3296 {
3297 // Varying size
3298 if(constantSizesIndex == 0)
3299 config.AllocationSizes.push_back({4, 16, 1024});
3300 // Constant sizes
3301 else
3302 {
3303 config.AllocationSizes.push_back({1, 16, 16});
3304 config.AllocationSizes.push_back({1, 64, 64});
3305 config.AllocationSizes.push_back({1, 256, 256});
3306 config.AllocationSizes.push_back({1, 1024, 1024});
3307 }
3308 }
3309 // Large
3310 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3311 {
3312 // Varying size
3313 if(constantSizesIndex == 0)
3314 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3315 // Constant sizes
3316 else
3317 {
3318 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3319 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3320 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3321 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3322 }
3323 }
3324 }
3325 // Images present
3326 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3327 {
3328 // Small
3329 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3330 {
3331 // Varying size
3332 if(constantSizesIndex == 0)
3333 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3334 // Constant sizes
3335 else
3336 {
3337 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3338 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3339 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3340 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3341 }
3342 }
3343 // Large
3344 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3345 {
3346 // Varying size
3347 if(constantSizesIndex == 0)
3348 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3349 // Constant sizes
3350 else
3351 {
3352 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3353 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3354 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3355 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3356 }
3357 }
3358 }
3359
3360 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
3361 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
3362
3363 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
3364 size_t subscriptionModeCount;
3365 switch(ConfigType)
3366 {
3367 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
3368 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
3369 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
3370 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
3371 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
3372 default: assert(0);
3373 }
3374 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
3375 {
3376 std::string desc5 = desc4;
3377
3378 switch(subscriptionModeIndex)
3379 {
3380 case 0:
3381 desc5 += " Subscription_66%";
3382 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
3383 break;
3384 case 1:
3385 desc5 += " Subscription_133%";
3386 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
3387 break;
3388 case 2:
3389 desc5 += " Subscription_100%";
3390 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
3391 break;
3392 case 3:
3393 desc5 += " Subscription_33%";
3394 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
3395 break;
3396 case 4:
3397 desc5 += " Subscription_166%";
3398 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
3399 break;
3400 default:
3401 assert(0);
3402 }
3403
3404 config.TotalItemCount = config.UsedItemCountMax * 5;
3405 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3406
3407 const char* testDescription = desc5.c_str();
3408
3409 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3410 {
3411 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3412
3413 PoolTestResult result{};
3414 g_MemoryAliasingWarningEnabled = false;
3415 TestPool_Benchmark(result, config);
3416 g_MemoryAliasingWarningEnabled = true;
3417 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3418 }
3419 }
3420 }
3421 }
3422 }
3423 }
3424}
3425
3426void Test()
3427{
3428 wprintf(L"TESTING:\n");
3429
Adam Sawicki212a4a62018-06-14 15:44:45 +02003430 // TEMP tests
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003431TestLinearAllocator();
3432return;
Adam Sawicki212a4a62018-06-14 15:44:45 +02003433
Adam Sawickib8333fb2018-03-13 16:15:53 +01003434 // # Simple tests
3435
3436 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02003437#if VMA_DEBUG_MARGIN
3438 TestDebugMargin();
3439#else
3440 TestPool_SameSize();
3441 TestHeapSizeLimit();
3442#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02003443#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
3444 TestAllocationsInitialization();
3445#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01003446 TestMapping();
3447 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003448 TestLinearAllocator();
Adam Sawickib8333fb2018-03-13 16:15:53 +01003449 TestDefragmentationSimple();
3450 TestDefragmentationFull();
3451
3452 // # Detailed tests
3453 FILE* file;
3454 fopen_s(&file, "Results.csv", "w");
3455 assert(file != NULL);
3456
3457 WriteMainTestResultHeader(file);
3458 PerformMainTests(file);
3459 //PerformCustomMainTest(file);
3460
3461 WritePoolTestResultHeader(file);
3462 PerformPoolTests(file);
3463 //PerformCustomPoolTest(file);
3464
3465 fclose(file);
3466
3467 wprintf(L"Done.\n");
3468}
3469
Adam Sawickif1a793c2018-03-13 15:42:22 +01003470#endif // #ifdef _WIN32