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