blob: 81382113b6b76c2324a310179ffd3e19402663c6 [file] [log] [blame]
Adam Sawickiae5c4662019-01-02 10:23:35 +01001//
Adam Sawicki50882502020-02-07 16:51:31 +01002// Copyright (c) 2017-2020 Advanced Micro Devices, Inc. All rights reserved.
Adam Sawickiae5c4662019-01-02 10:23:35 +01003//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21//
22
Adam Sawickif1a793c2018-03-13 15:42:22 +010023#include "Tests.h"
24#include "VmaUsage.h"
25#include "Common.h"
Adam Sawickib8333fb2018-03-13 16:15:53 +010026#include <atomic>
27#include <thread>
28#include <mutex>
Adam Sawicki94ce3d72019-04-17 14:59:25 +020029#include <functional>
Adam Sawickif1a793c2018-03-13 15:42:22 +010030
31#ifdef _WIN32
32
Adam Sawicki33d2ce72018-08-27 13:59:13 +020033static const char* CODE_DESCRIPTION = "Foo";
34
Adam Sawickif2975342018-10-16 13:49:02 +020035extern VkCommandBuffer g_hTemporaryCommandBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +020036extern const VkAllocationCallbacks* g_Allocs;
Adam Sawickie73e9882020-03-20 18:05:42 +010037extern bool g_BufferDeviceAddressEnabled;
38extern PFN_vkGetBufferDeviceAddressEXT g_vkGetBufferDeviceAddressEXT;
Adam Sawickif2975342018-10-16 13:49:02 +020039void BeginSingleTimeCommands();
40void EndSingleTimeCommands();
41
Adam Sawickibdb89a92018-12-13 11:56:30 +010042#ifndef VMA_DEBUG_MARGIN
43 #define VMA_DEBUG_MARGIN 0
44#endif
45
Adam Sawicki0a607132018-08-24 11:18:41 +020046enum CONFIG_TYPE {
47 CONFIG_TYPE_MINIMUM,
48 CONFIG_TYPE_SMALL,
49 CONFIG_TYPE_AVERAGE,
50 CONFIG_TYPE_LARGE,
51 CONFIG_TYPE_MAXIMUM,
52 CONFIG_TYPE_COUNT
53};
54
Adam Sawickif2975342018-10-16 13:49:02 +020055static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
56//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
Adam Sawicki0a607132018-08-24 11:18:41 +020057
Adam Sawickib8333fb2018-03-13 16:15:53 +010058enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
59
Adam Sawicki0667e332018-08-24 17:26:44 +020060static const char* FREE_ORDER_NAMES[] = {
61 "FORWARD",
62 "BACKWARD",
63 "RANDOM",
Adam Sawicki0a607132018-08-24 11:18:41 +020064};
65
Adam Sawicki80927152018-09-07 17:27:23 +020066// Copy of internal VmaAlgorithmToStr.
67static const char* AlgorithmToStr(uint32_t algorithm)
68{
69 switch(algorithm)
70 {
71 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
72 return "Linear";
73 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
74 return "Buddy";
75 case 0:
76 return "Default";
77 default:
78 assert(0);
79 return "";
80 }
81}
82
Adam Sawickib8333fb2018-03-13 16:15:53 +010083struct AllocationSize
84{
85 uint32_t Probability;
86 VkDeviceSize BufferSizeMin, BufferSizeMax;
87 uint32_t ImageSizeMin, ImageSizeMax;
88};
89
90struct Config
91{
92 uint32_t RandSeed;
93 VkDeviceSize BeginBytesToAllocate;
94 uint32_t AdditionalOperationCount;
95 VkDeviceSize MaxBytesToAllocate;
96 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
97 std::vector<AllocationSize> AllocationSizes;
98 uint32_t ThreadCount;
99 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
100 FREE_ORDER FreeOrder;
Adam Sawicki0667e332018-08-24 17:26:44 +0200101 VmaAllocationCreateFlags AllocationStrategy; // For VMA_ALLOCATION_CREATE_STRATEGY_*
Adam Sawickib8333fb2018-03-13 16:15:53 +0100102};
103
104struct Result
105{
106 duration TotalTime;
107 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
108 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
109 VkDeviceSize TotalMemoryAllocated;
110 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
111};
112
113void TestDefragmentationSimple();
114void TestDefragmentationFull();
115
116struct PoolTestConfig
117{
118 uint32_t RandSeed;
119 uint32_t ThreadCount;
120 VkDeviceSize PoolSize;
121 uint32_t FrameCount;
122 uint32_t TotalItemCount;
123 // Range for number of items used in each frame.
124 uint32_t UsedItemCountMin, UsedItemCountMax;
125 // Percent of items to make unused, and possibly make some others used in each frame.
126 uint32_t ItemsToMakeUnusedPercent;
127 std::vector<AllocationSize> AllocationSizes;
128
129 VkDeviceSize CalcAvgResourceSize() const
130 {
131 uint32_t probabilitySum = 0;
132 VkDeviceSize sizeSum = 0;
133 for(size_t i = 0; i < AllocationSizes.size(); ++i)
134 {
135 const AllocationSize& allocSize = AllocationSizes[i];
136 if(allocSize.BufferSizeMax > 0)
137 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
138 else
139 {
140 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
141 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
142 }
143 probabilitySum += allocSize.Probability;
144 }
145 return sizeSum / probabilitySum;
146 }
147
148 bool UsesBuffers() const
149 {
150 for(size_t i = 0; i < AllocationSizes.size(); ++i)
151 if(AllocationSizes[i].BufferSizeMax > 0)
152 return true;
153 return false;
154 }
155
156 bool UsesImages() const
157 {
158 for(size_t i = 0; i < AllocationSizes.size(); ++i)
159 if(AllocationSizes[i].ImageSizeMax > 0)
160 return true;
161 return false;
162 }
163};
164
165struct PoolTestResult
166{
167 duration TotalTime;
168 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
169 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
170 size_t LostAllocationCount, LostAllocationTotalSize;
171 size_t FailedAllocationCount, FailedAllocationTotalSize;
172};
173
174static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
175
Adam Sawicki51fa9662018-10-03 13:44:29 +0200176uint32_t g_FrameIndex = 0;
Adam Sawicki8cfe05f2018-08-22 16:48:17 +0200177
Adam Sawickib8333fb2018-03-13 16:15:53 +0100178struct BufferInfo
179{
180 VkBuffer Buffer = VK_NULL_HANDLE;
181 VmaAllocation Allocation = VK_NULL_HANDLE;
182};
183
Adam Sawicki40ffe982019-10-11 15:56:02 +0200184static uint32_t MemoryTypeToHeap(uint32_t memoryTypeIndex)
185{
186 const VkPhysicalDeviceMemoryProperties* props;
187 vmaGetMemoryProperties(g_hAllocator, &props);
188 return props->memoryTypes[memoryTypeIndex].heapIndex;
189}
190
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +0200191static uint32_t GetAllocationStrategyCount()
192{
193 uint32_t strategyCount = 0;
194 switch(ConfigType)
195 {
196 case CONFIG_TYPE_MINIMUM: strategyCount = 1; break;
197 case CONFIG_TYPE_SMALL: strategyCount = 1; break;
198 case CONFIG_TYPE_AVERAGE: strategyCount = 2; break;
199 case CONFIG_TYPE_LARGE: strategyCount = 2; break;
200 case CONFIG_TYPE_MAXIMUM: strategyCount = 3; break;
201 default: assert(0);
202 }
203 return strategyCount;
204}
205
206static const char* GetAllocationStrategyName(VmaAllocationCreateFlags allocStrategy)
207{
208 switch(allocStrategy)
209 {
210 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT: return "BEST_FIT"; break;
211 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT: return "WORST_FIT"; break;
212 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT: return "FIRST_FIT"; break;
213 case 0: return "Default"; break;
214 default: assert(0); return "";
215 }
216}
217
Adam Sawickib8333fb2018-03-13 16:15:53 +0100218static void InitResult(Result& outResult)
219{
220 outResult.TotalTime = duration::zero();
221 outResult.AllocationTimeMin = duration::max();
222 outResult.AllocationTimeAvg = duration::zero();
223 outResult.AllocationTimeMax = duration::min();
224 outResult.DeallocationTimeMin = duration::max();
225 outResult.DeallocationTimeAvg = duration::zero();
226 outResult.DeallocationTimeMax = duration::min();
227 outResult.TotalMemoryAllocated = 0;
228 outResult.FreeRangeSizeAvg = 0;
229 outResult.FreeRangeSizeMax = 0;
230}
231
232class TimeRegisterObj
233{
234public:
235 TimeRegisterObj(duration& min, duration& sum, duration& max) :
236 m_Min(min),
237 m_Sum(sum),
238 m_Max(max),
239 m_TimeBeg(std::chrono::high_resolution_clock::now())
240 {
241 }
242
243 ~TimeRegisterObj()
244 {
245 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
246 m_Sum += d;
247 if(d < m_Min) m_Min = d;
248 if(d > m_Max) m_Max = d;
249 }
250
251private:
252 duration& m_Min;
253 duration& m_Sum;
254 duration& m_Max;
255 time_point m_TimeBeg;
256};
257
258struct PoolTestThreadResult
259{
260 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
261 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
262 size_t AllocationCount, DeallocationCount;
263 size_t LostAllocationCount, LostAllocationTotalSize;
264 size_t FailedAllocationCount, FailedAllocationTotalSize;
265};
266
267class AllocationTimeRegisterObj : public TimeRegisterObj
268{
269public:
270 AllocationTimeRegisterObj(Result& result) :
271 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
272 {
273 }
274};
275
276class DeallocationTimeRegisterObj : public TimeRegisterObj
277{
278public:
279 DeallocationTimeRegisterObj(Result& result) :
280 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
281 {
282 }
283};
284
285class PoolAllocationTimeRegisterObj : public TimeRegisterObj
286{
287public:
288 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
289 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
290 {
291 }
292};
293
294class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
295{
296public:
297 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
298 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
299 {
300 }
301};
302
Adam Sawicki33d2ce72018-08-27 13:59:13 +0200303static void CurrentTimeToStr(std::string& out)
304{
305 time_t rawTime; time(&rawTime);
306 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
307 char timeStr[128];
308 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
309 out = timeStr;
310}
311
Adam Sawickib8333fb2018-03-13 16:15:53 +0100312VkResult MainTest(Result& outResult, const Config& config)
313{
314 assert(config.ThreadCount > 0);
315
316 InitResult(outResult);
317
318 RandomNumberGenerator mainRand{config.RandSeed};
319
320 time_point timeBeg = std::chrono::high_resolution_clock::now();
321
322 std::atomic<size_t> allocationCount = 0;
323 VkResult res = VK_SUCCESS;
324
325 uint32_t memUsageProbabilitySum =
326 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
327 config.MemUsageProbability[2] + config.MemUsageProbability[3];
328 assert(memUsageProbabilitySum > 0);
329
330 uint32_t allocationSizeProbabilitySum = std::accumulate(
331 config.AllocationSizes.begin(),
332 config.AllocationSizes.end(),
333 0u,
334 [](uint32_t sum, const AllocationSize& allocSize) {
335 return sum + allocSize.Probability;
336 });
337
338 struct Allocation
339 {
340 VkBuffer Buffer;
341 VkImage Image;
342 VmaAllocation Alloc;
343 };
344
345 std::vector<Allocation> commonAllocations;
346 std::mutex commonAllocationsMutex;
347
348 auto Allocate = [&](
349 VkDeviceSize bufferSize,
350 const VkExtent2D imageExtent,
351 RandomNumberGenerator& localRand,
352 VkDeviceSize& totalAllocatedBytes,
353 std::vector<Allocation>& allocations) -> VkResult
354 {
355 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
356
357 uint32_t memUsageIndex = 0;
358 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
359 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
360 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
361
362 VmaAllocationCreateInfo memReq = {};
363 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
Adam Sawicki0667e332018-08-24 17:26:44 +0200364 memReq.flags |= config.AllocationStrategy;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100365
366 Allocation allocation = {};
367 VmaAllocationInfo allocationInfo;
368
369 // Buffer
370 if(bufferSize > 0)
371 {
372 assert(imageExtent.width == 0);
373 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
374 bufferInfo.size = bufferSize;
375 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
376
377 {
378 AllocationTimeRegisterObj timeRegisterObj{outResult};
379 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
380 }
381 }
382 // Image
383 else
384 {
385 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
386 imageInfo.imageType = VK_IMAGE_TYPE_2D;
387 imageInfo.extent.width = imageExtent.width;
388 imageInfo.extent.height = imageExtent.height;
389 imageInfo.extent.depth = 1;
390 imageInfo.mipLevels = 1;
391 imageInfo.arrayLayers = 1;
392 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
393 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
394 VK_IMAGE_TILING_OPTIMAL :
395 VK_IMAGE_TILING_LINEAR;
396 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
397 switch(memReq.usage)
398 {
399 case VMA_MEMORY_USAGE_GPU_ONLY:
400 switch(localRand.Generate() % 3)
401 {
402 case 0:
403 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
404 break;
405 case 1:
406 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
407 break;
408 case 2:
409 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
410 break;
411 }
412 break;
413 case VMA_MEMORY_USAGE_CPU_ONLY:
414 case VMA_MEMORY_USAGE_CPU_TO_GPU:
415 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
416 break;
417 case VMA_MEMORY_USAGE_GPU_TO_CPU:
418 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
419 break;
420 }
421 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
422 imageInfo.flags = 0;
423
424 {
425 AllocationTimeRegisterObj timeRegisterObj{outResult};
426 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
427 }
428 }
429
430 if(res == VK_SUCCESS)
431 {
432 ++allocationCount;
433 totalAllocatedBytes += allocationInfo.size;
434 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
435 if(useCommonAllocations)
436 {
437 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
438 commonAllocations.push_back(allocation);
439 }
440 else
441 allocations.push_back(allocation);
442 }
443 else
444 {
Adam Sawickib8d34d52018-10-03 17:41:20 +0200445 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100446 }
447 return res;
448 };
449
450 auto GetNextAllocationSize = [&](
451 VkDeviceSize& outBufSize,
452 VkExtent2D& outImageSize,
453 RandomNumberGenerator& localRand)
454 {
455 outBufSize = 0;
456 outImageSize = {0, 0};
457
458 uint32_t allocSizeIndex = 0;
459 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
460 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
461 r -= config.AllocationSizes[allocSizeIndex++].Probability;
462
463 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
464 if(allocSize.BufferSizeMax > 0)
465 {
466 assert(allocSize.ImageSizeMax == 0);
467 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
468 outBufSize = allocSize.BufferSizeMin;
469 else
470 {
471 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
472 outBufSize = outBufSize / 16 * 16;
473 }
474 }
475 else
476 {
477 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
478 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
479 else
480 {
481 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
482 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
483 }
484 }
485 };
486
487 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
488 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
489
490 auto ThreadProc = [&](uint32_t randSeed) -> void
491 {
492 RandomNumberGenerator threadRand(randSeed);
493 VkDeviceSize threadTotalAllocatedBytes = 0;
494 std::vector<Allocation> threadAllocations;
495 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
496 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
497 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
498
499 // BEGIN ALLOCATIONS
500 for(;;)
501 {
502 VkDeviceSize bufferSize = 0;
503 VkExtent2D imageExtent = {};
504 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
505 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
506 threadBeginBytesToAllocate)
507 {
508 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
509 break;
510 }
511 else
512 break;
513 }
514
515 // ADDITIONAL ALLOCATIONS AND FREES
516 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
517 {
518 VkDeviceSize bufferSize = 0;
519 VkExtent2D imageExtent = {};
520 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
521
522 // true = allocate, false = free
523 bool allocate = threadRand.Generate() % 2 != 0;
524
525 if(allocate)
526 {
527 if(threadTotalAllocatedBytes +
528 bufferSize +
529 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
530 threadMaxBytesToAllocate)
531 {
532 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
533 break;
534 }
535 }
536 else
537 {
538 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
539 if(useCommonAllocations)
540 {
541 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
542 if(!commonAllocations.empty())
543 {
544 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
545 VmaAllocationInfo allocationInfo;
546 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
547 if(threadTotalAllocatedBytes >= allocationInfo.size)
548 {
549 DeallocationTimeRegisterObj timeRegisterObj{outResult};
550 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
551 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
552 else
553 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
554 threadTotalAllocatedBytes -= allocationInfo.size;
555 commonAllocations.erase(commonAllocations.begin() + indexToFree);
556 }
557 }
558 }
559 else
560 {
561 if(!threadAllocations.empty())
562 {
563 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
564 VmaAllocationInfo allocationInfo;
565 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
566 if(threadTotalAllocatedBytes >= allocationInfo.size)
567 {
568 DeallocationTimeRegisterObj timeRegisterObj{outResult};
569 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
570 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
571 else
572 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
573 threadTotalAllocatedBytes -= allocationInfo.size;
574 threadAllocations.erase(threadAllocations.begin() + indexToFree);
575 }
576 }
577 }
578 }
579 }
580
581 ++numThreadsReachedMaxAllocations;
582
583 WaitForSingleObject(threadsFinishEvent, INFINITE);
584
585 // DEALLOCATION
586 while(!threadAllocations.empty())
587 {
588 size_t indexToFree = 0;
589 switch(config.FreeOrder)
590 {
591 case FREE_ORDER::FORWARD:
592 indexToFree = 0;
593 break;
594 case FREE_ORDER::BACKWARD:
595 indexToFree = threadAllocations.size() - 1;
596 break;
597 case FREE_ORDER::RANDOM:
598 indexToFree = mainRand.Generate() % threadAllocations.size();
599 break;
600 }
601
602 {
603 DeallocationTimeRegisterObj timeRegisterObj{outResult};
604 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
605 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
606 else
607 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
608 }
609 threadAllocations.erase(threadAllocations.begin() + indexToFree);
610 }
611 };
612
613 uint32_t threadRandSeed = mainRand.Generate();
614 std::vector<std::thread> bkgThreads;
615 for(size_t i = 0; i < config.ThreadCount; ++i)
616 {
617 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
618 }
619
620 // Wait for threads reached max allocations
621 while(numThreadsReachedMaxAllocations < config.ThreadCount)
622 Sleep(0);
623
624 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
625 VmaStats vmaStats = {};
626 vmaCalculateStats(g_hAllocator, &vmaStats);
627 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
628 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
629 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
630
631 // Signal threads to deallocate
632 SetEvent(threadsFinishEvent);
633
634 // Wait for threads finished
635 for(size_t i = 0; i < bkgThreads.size(); ++i)
636 bkgThreads[i].join();
637 bkgThreads.clear();
638
639 CloseHandle(threadsFinishEvent);
640
641 // Deallocate remaining common resources
642 while(!commonAllocations.empty())
643 {
644 size_t indexToFree = 0;
645 switch(config.FreeOrder)
646 {
647 case FREE_ORDER::FORWARD:
648 indexToFree = 0;
649 break;
650 case FREE_ORDER::BACKWARD:
651 indexToFree = commonAllocations.size() - 1;
652 break;
653 case FREE_ORDER::RANDOM:
654 indexToFree = mainRand.Generate() % commonAllocations.size();
655 break;
656 }
657
658 {
659 DeallocationTimeRegisterObj timeRegisterObj{outResult};
660 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
661 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
662 else
663 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
664 }
665 commonAllocations.erase(commonAllocations.begin() + indexToFree);
666 }
667
668 if(allocationCount)
669 {
670 outResult.AllocationTimeAvg /= allocationCount;
671 outResult.DeallocationTimeAvg /= allocationCount;
672 }
673
674 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
675
676 return res;
677}
678
Adam Sawicki51fa9662018-10-03 13:44:29 +0200679void SaveAllocatorStatsToFile(const wchar_t* filePath)
Adam Sawickib8333fb2018-03-13 16:15:53 +0100680{
Adam Sawicki4d844e22019-01-24 16:21:05 +0100681 wprintf(L"Saving JSON dump to file \"%s\"\n", filePath);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100682 char* stats;
Adam Sawickie44c6262018-06-15 14:30:39 +0200683 vmaBuildStatsString(g_hAllocator, &stats, VK_TRUE);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100684 SaveFile(filePath, stats, strlen(stats));
Adam Sawickie44c6262018-06-15 14:30:39 +0200685 vmaFreeStatsString(g_hAllocator, stats);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100686}
687
688struct AllocInfo
689{
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200690 VmaAllocation m_Allocation = VK_NULL_HANDLE;
691 VkBuffer m_Buffer = VK_NULL_HANDLE;
692 VkImage m_Image = VK_NULL_HANDLE;
Adam Sawickia52012d2019-12-23 15:28:51 +0100693 VkImageLayout m_ImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200694 uint32_t m_StartValue = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100695 union
696 {
697 VkBufferCreateInfo m_BufferInfo;
698 VkImageCreateInfo m_ImageInfo;
699 };
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200700
Adam Sawickic467e282019-12-23 16:38:31 +0100701 // After defragmentation.
702 VkBuffer m_NewBuffer = VK_NULL_HANDLE;
703 VkImage m_NewImage = VK_NULL_HANDLE;
704
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200705 void CreateBuffer(
706 const VkBufferCreateInfo& bufCreateInfo,
707 const VmaAllocationCreateInfo& allocCreateInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +0100708 void CreateImage(
709 const VkImageCreateInfo& imageCreateInfo,
710 const VmaAllocationCreateInfo& allocCreateInfo,
711 VkImageLayout layout);
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200712 void Destroy();
Adam Sawickib8333fb2018-03-13 16:15:53 +0100713};
714
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200715void AllocInfo::CreateBuffer(
716 const VkBufferCreateInfo& bufCreateInfo,
717 const VmaAllocationCreateInfo& allocCreateInfo)
718{
719 m_BufferInfo = bufCreateInfo;
720 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &m_Buffer, &m_Allocation, nullptr);
721 TEST(res == VK_SUCCESS);
722}
Adam Sawickia52012d2019-12-23 15:28:51 +0100723void AllocInfo::CreateImage(
724 const VkImageCreateInfo& imageCreateInfo,
725 const VmaAllocationCreateInfo& allocCreateInfo,
726 VkImageLayout layout)
727{
728 m_ImageInfo = imageCreateInfo;
729 m_ImageLayout = layout;
730 VkResult res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &m_Image, &m_Allocation, nullptr);
731 TEST(res == VK_SUCCESS);
732}
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200733
734void AllocInfo::Destroy()
735{
736 if(m_Image)
737 {
Adam Sawickic467e282019-12-23 16:38:31 +0100738 assert(!m_Buffer);
Adam Sawicki1f84f622019-07-02 13:40:01 +0200739 vkDestroyImage(g_hDevice, m_Image, g_Allocs);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100740 m_Image = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200741 }
742 if(m_Buffer)
743 {
Adam Sawickic467e282019-12-23 16:38:31 +0100744 assert(!m_Image);
Adam Sawicki1f84f622019-07-02 13:40:01 +0200745 vkDestroyBuffer(g_hDevice, m_Buffer, g_Allocs);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100746 m_Buffer = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200747 }
748 if(m_Allocation)
749 {
750 vmaFreeMemory(g_hAllocator, m_Allocation);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100751 m_Allocation = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200752 }
753}
754
Adam Sawickif2975342018-10-16 13:49:02 +0200755class StagingBufferCollection
756{
757public:
758 StagingBufferCollection() { }
759 ~StagingBufferCollection();
760 // Returns false if maximum total size of buffers would be exceeded.
761 bool AcquireBuffer(VkDeviceSize size, VkBuffer& outBuffer, void*& outMappedPtr);
762 void ReleaseAllBuffers();
763
764private:
765 static const VkDeviceSize MAX_TOTAL_SIZE = 256ull * 1024 * 1024;
766 struct BufInfo
767 {
768 VmaAllocation Allocation = VK_NULL_HANDLE;
769 VkBuffer Buffer = VK_NULL_HANDLE;
770 VkDeviceSize Size = VK_WHOLE_SIZE;
771 void* MappedPtr = nullptr;
772 bool Used = false;
773 };
774 std::vector<BufInfo> m_Bufs;
775 // Including both used and unused.
776 VkDeviceSize m_TotalSize = 0;
777};
778
779StagingBufferCollection::~StagingBufferCollection()
780{
781 for(size_t i = m_Bufs.size(); i--; )
782 {
783 vmaDestroyBuffer(g_hAllocator, m_Bufs[i].Buffer, m_Bufs[i].Allocation);
784 }
785}
786
787bool StagingBufferCollection::AcquireBuffer(VkDeviceSize size, VkBuffer& outBuffer, void*& outMappedPtr)
788{
789 assert(size <= MAX_TOTAL_SIZE);
790
791 // Try to find existing unused buffer with best size.
792 size_t bestIndex = SIZE_MAX;
793 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
794 {
795 BufInfo& currBufInfo = m_Bufs[i];
796 if(!currBufInfo.Used && currBufInfo.Size >= size &&
797 (bestIndex == SIZE_MAX || currBufInfo.Size < m_Bufs[bestIndex].Size))
798 {
799 bestIndex = i;
800 }
801 }
802
803 if(bestIndex != SIZE_MAX)
804 {
805 m_Bufs[bestIndex].Used = true;
806 outBuffer = m_Bufs[bestIndex].Buffer;
807 outMappedPtr = m_Bufs[bestIndex].MappedPtr;
808 return true;
809 }
810
811 // Allocate new buffer with requested size.
812 if(m_TotalSize + size <= MAX_TOTAL_SIZE)
813 {
814 BufInfo bufInfo;
815 bufInfo.Size = size;
816 bufInfo.Used = true;
817
818 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
819 bufCreateInfo.size = size;
820 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
821
822 VmaAllocationCreateInfo allocCreateInfo = {};
823 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
824 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
825
826 VmaAllocationInfo allocInfo;
827 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
828 bufInfo.MappedPtr = allocInfo.pMappedData;
829 TEST(res == VK_SUCCESS && bufInfo.MappedPtr);
830
831 outBuffer = bufInfo.Buffer;
832 outMappedPtr = bufInfo.MappedPtr;
833
834 m_Bufs.push_back(std::move(bufInfo));
835
836 m_TotalSize += size;
837
838 return true;
839 }
840
841 // There are some unused but smaller buffers: Free them and try again.
842 bool hasUnused = false;
843 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
844 {
845 if(!m_Bufs[i].Used)
846 {
847 hasUnused = true;
848 break;
849 }
850 }
851 if(hasUnused)
852 {
853 for(size_t i = m_Bufs.size(); i--; )
854 {
855 if(!m_Bufs[i].Used)
856 {
857 m_TotalSize -= m_Bufs[i].Size;
858 vmaDestroyBuffer(g_hAllocator, m_Bufs[i].Buffer, m_Bufs[i].Allocation);
859 m_Bufs.erase(m_Bufs.begin() + i);
860 }
861 }
862
863 return AcquireBuffer(size, outBuffer, outMappedPtr);
864 }
865
866 return false;
867}
868
869void StagingBufferCollection::ReleaseAllBuffers()
870{
871 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
872 {
873 m_Bufs[i].Used = false;
874 }
875}
876
877static void UploadGpuData(const AllocInfo* allocInfo, size_t allocInfoCount)
878{
879 StagingBufferCollection stagingBufs;
880
881 bool cmdBufferStarted = false;
882 for(size_t allocInfoIndex = 0; allocInfoIndex < allocInfoCount; ++allocInfoIndex)
883 {
884 const AllocInfo& currAllocInfo = allocInfo[allocInfoIndex];
885 if(currAllocInfo.m_Buffer)
886 {
887 const VkDeviceSize size = currAllocInfo.m_BufferInfo.size;
888
889 VkBuffer stagingBuf = VK_NULL_HANDLE;
890 void* stagingBufMappedPtr = nullptr;
891 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
892 {
893 TEST(cmdBufferStarted);
894 EndSingleTimeCommands();
895 stagingBufs.ReleaseAllBuffers();
896 cmdBufferStarted = false;
897
898 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
899 TEST(ok);
900 }
901
902 // Fill staging buffer.
903 {
904 assert(size % sizeof(uint32_t) == 0);
905 uint32_t* stagingValPtr = (uint32_t*)stagingBufMappedPtr;
906 uint32_t val = currAllocInfo.m_StartValue;
907 for(size_t i = 0; i < size / sizeof(uint32_t); ++i)
908 {
909 *stagingValPtr = val;
910 ++stagingValPtr;
911 ++val;
912 }
913 }
914
915 // Issue copy command from staging buffer to destination buffer.
916 if(!cmdBufferStarted)
917 {
918 cmdBufferStarted = true;
919 BeginSingleTimeCommands();
920 }
921
922 VkBufferCopy copy = {};
923 copy.srcOffset = 0;
924 copy.dstOffset = 0;
925 copy.size = size;
926 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingBuf, currAllocInfo.m_Buffer, 1, &copy);
927 }
928 else
929 {
Adam Sawickia52012d2019-12-23 15:28:51 +0100930 TEST(currAllocInfo.m_ImageInfo.format == VK_FORMAT_R8G8B8A8_UNORM && "Only RGBA8 images are currently supported.");
931 TEST(currAllocInfo.m_ImageInfo.mipLevels == 1 && "Only single mip images are currently supported.");
932
Adam Sawickic467e282019-12-23 16:38:31 +0100933 const VkDeviceSize size = (VkDeviceSize)currAllocInfo.m_ImageInfo.extent.width * currAllocInfo.m_ImageInfo.extent.height * sizeof(uint32_t);
Adam Sawickia52012d2019-12-23 15:28:51 +0100934
935 VkBuffer stagingBuf = VK_NULL_HANDLE;
936 void* stagingBufMappedPtr = nullptr;
937 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
938 {
939 TEST(cmdBufferStarted);
940 EndSingleTimeCommands();
941 stagingBufs.ReleaseAllBuffers();
942 cmdBufferStarted = false;
943
944 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
945 TEST(ok);
946 }
947
948 // Fill staging buffer.
949 {
950 assert(size % sizeof(uint32_t) == 0);
951 uint32_t *stagingValPtr = (uint32_t *)stagingBufMappedPtr;
952 uint32_t val = currAllocInfo.m_StartValue;
953 for(size_t i = 0; i < size / sizeof(uint32_t); ++i)
954 {
955 *stagingValPtr = val;
956 ++stagingValPtr;
957 ++val;
958 }
959 }
960
961 // Issue copy command from staging buffer to destination buffer.
962 if(!cmdBufferStarted)
963 {
964 cmdBufferStarted = true;
965 BeginSingleTimeCommands();
966 }
967
968
969 // Transfer to transfer dst layout
970 VkImageSubresourceRange subresourceRange = {
971 VK_IMAGE_ASPECT_COLOR_BIT,
972 0, VK_REMAINING_MIP_LEVELS,
973 0, VK_REMAINING_ARRAY_LAYERS
974 };
975
976 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
977 barrier.srcAccessMask = 0;
978 barrier.dstAccessMask = 0;
979 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
980 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
981 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
982 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
983 barrier.image = currAllocInfo.m_Image;
984 barrier.subresourceRange = subresourceRange;
985
986 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
987 0, nullptr,
988 0, nullptr,
989 1, &barrier);
990
991 // Copy image date
992 VkBufferImageCopy copy = {};
993 copy.bufferOffset = 0;
994 copy.bufferRowLength = 0;
995 copy.bufferImageHeight = 0;
996 copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
997 copy.imageSubresource.layerCount = 1;
998 copy.imageExtent = currAllocInfo.m_ImageInfo.extent;
999
1000 vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, stagingBuf, currAllocInfo.m_Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
1001
1002 // Transfer to desired layout
1003 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1004 barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1005 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1006 barrier.newLayout = currAllocInfo.m_ImageLayout;
1007
1008 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
1009 0, nullptr,
1010 0, nullptr,
1011 1, &barrier);
Adam Sawickif2975342018-10-16 13:49:02 +02001012 }
1013 }
1014
1015 if(cmdBufferStarted)
1016 {
1017 EndSingleTimeCommands();
1018 stagingBufs.ReleaseAllBuffers();
1019 }
1020}
1021
1022static void ValidateGpuData(const AllocInfo* allocInfo, size_t allocInfoCount)
1023{
1024 StagingBufferCollection stagingBufs;
1025
1026 bool cmdBufferStarted = false;
1027 size_t validateAllocIndexOffset = 0;
1028 std::vector<void*> validateStagingBuffers;
1029 for(size_t allocInfoIndex = 0; allocInfoIndex < allocInfoCount; ++allocInfoIndex)
1030 {
1031 const AllocInfo& currAllocInfo = allocInfo[allocInfoIndex];
1032 if(currAllocInfo.m_Buffer)
1033 {
1034 const VkDeviceSize size = currAllocInfo.m_BufferInfo.size;
1035
1036 VkBuffer stagingBuf = VK_NULL_HANDLE;
1037 void* stagingBufMappedPtr = nullptr;
1038 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
1039 {
1040 TEST(cmdBufferStarted);
1041 EndSingleTimeCommands();
1042 cmdBufferStarted = false;
1043
1044 for(size_t validateIndex = 0;
1045 validateIndex < validateStagingBuffers.size();
1046 ++validateIndex)
1047 {
1048 const size_t validateAllocIndex = validateIndex + validateAllocIndexOffset;
1049 const VkDeviceSize validateSize = allocInfo[validateAllocIndex].m_BufferInfo.size;
1050 TEST(validateSize % sizeof(uint32_t) == 0);
1051 const uint32_t* stagingValPtr = (const uint32_t*)validateStagingBuffers[validateIndex];
1052 uint32_t val = allocInfo[validateAllocIndex].m_StartValue;
1053 bool valid = true;
1054 for(size_t i = 0; i < validateSize / sizeof(uint32_t); ++i)
1055 {
1056 if(*stagingValPtr != val)
1057 {
1058 valid = false;
1059 break;
1060 }
1061 ++stagingValPtr;
1062 ++val;
1063 }
1064 TEST(valid);
1065 }
1066
1067 stagingBufs.ReleaseAllBuffers();
1068
1069 validateAllocIndexOffset = allocInfoIndex;
1070 validateStagingBuffers.clear();
1071
1072 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
1073 TEST(ok);
1074 }
1075
1076 // Issue copy command from staging buffer to destination buffer.
1077 if(!cmdBufferStarted)
1078 {
1079 cmdBufferStarted = true;
1080 BeginSingleTimeCommands();
1081 }
1082
1083 VkBufferCopy copy = {};
1084 copy.srcOffset = 0;
1085 copy.dstOffset = 0;
1086 copy.size = size;
1087 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, currAllocInfo.m_Buffer, stagingBuf, 1, &copy);
1088
1089 // Sava mapped pointer for later validation.
1090 validateStagingBuffers.push_back(stagingBufMappedPtr);
1091 }
1092 else
1093 {
1094 TEST(0 && "Images not currently supported.");
1095 }
1096 }
1097
1098 if(cmdBufferStarted)
1099 {
1100 EndSingleTimeCommands();
1101
1102 for(size_t validateIndex = 0;
1103 validateIndex < validateStagingBuffers.size();
1104 ++validateIndex)
1105 {
1106 const size_t validateAllocIndex = validateIndex + validateAllocIndexOffset;
1107 const VkDeviceSize validateSize = allocInfo[validateAllocIndex].m_BufferInfo.size;
1108 TEST(validateSize % sizeof(uint32_t) == 0);
1109 const uint32_t* stagingValPtr = (const uint32_t*)validateStagingBuffers[validateIndex];
1110 uint32_t val = allocInfo[validateAllocIndex].m_StartValue;
1111 bool valid = true;
1112 for(size_t i = 0; i < validateSize / sizeof(uint32_t); ++i)
1113 {
1114 if(*stagingValPtr != val)
1115 {
1116 valid = false;
1117 break;
1118 }
1119 ++stagingValPtr;
1120 ++val;
1121 }
1122 TEST(valid);
1123 }
1124
1125 stagingBufs.ReleaseAllBuffers();
1126 }
1127}
1128
Adam Sawickib8333fb2018-03-13 16:15:53 +01001129static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
1130{
1131 outMemReq = {};
1132 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
1133 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
1134}
1135
1136static void CreateBuffer(
1137 VmaPool pool,
1138 const VkBufferCreateInfo& bufCreateInfo,
1139 bool persistentlyMapped,
1140 AllocInfo& outAllocInfo)
1141{
1142 outAllocInfo = {};
1143 outAllocInfo.m_BufferInfo = bufCreateInfo;
1144
1145 VmaAllocationCreateInfo allocCreateInfo = {};
1146 allocCreateInfo.pool = pool;
1147 if(persistentlyMapped)
1148 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1149
1150 VmaAllocationInfo vmaAllocInfo = {};
1151 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
1152
1153 // Setup StartValue and fill.
1154 {
1155 outAllocInfo.m_StartValue = (uint32_t)rand();
1156 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001157 TEST((data != nullptr) == persistentlyMapped);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001158 if(!persistentlyMapped)
1159 {
1160 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
1161 }
1162
1163 uint32_t value = outAllocInfo.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001164 TEST(bufCreateInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001165 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
1166 data[i] = value++;
1167
1168 if(!persistentlyMapped)
1169 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
1170 }
1171}
1172
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001173static void CreateAllocation(AllocInfo& outAllocation)
Adam Sawickib8333fb2018-03-13 16:15:53 +01001174{
1175 outAllocation.m_Allocation = nullptr;
1176 outAllocation.m_Buffer = nullptr;
1177 outAllocation.m_Image = nullptr;
1178 outAllocation.m_StartValue = (uint32_t)rand();
1179
1180 VmaAllocationCreateInfo vmaMemReq;
1181 GetMemReq(vmaMemReq);
1182
1183 VmaAllocationInfo allocInfo;
1184
1185 const bool isBuffer = true;//(rand() & 0x1) != 0;
1186 const bool isLarge = (rand() % 16) == 0;
1187 if(isBuffer)
1188 {
1189 const uint32_t bufferSize = isLarge ?
1190 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
1191 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
1192
1193 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1194 bufferInfo.size = bufferSize;
1195 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1196
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001197 VkResult res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001198 outAllocation.m_BufferInfo = bufferInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001199 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001200 }
1201 else
1202 {
1203 const uint32_t imageSizeX = isLarge ?
1204 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
1205 rand() % 1024 + 1; // 1 ... 1024
1206 const uint32_t imageSizeY = isLarge ?
1207 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
1208 rand() % 1024 + 1; // 1 ... 1024
1209
1210 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1211 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1212 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
1213 imageInfo.extent.width = imageSizeX;
1214 imageInfo.extent.height = imageSizeY;
1215 imageInfo.extent.depth = 1;
1216 imageInfo.mipLevels = 1;
1217 imageInfo.arrayLayers = 1;
1218 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1219 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1220 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
1221 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1222
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001223 VkResult res = vmaCreateImage(g_hAllocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001224 outAllocation.m_ImageInfo = imageInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001225 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001226 }
1227
1228 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
1229 if(allocInfo.pMappedData == nullptr)
1230 {
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001231 VkResult res = vmaMapMemory(g_hAllocator, outAllocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001232 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001233 }
1234
1235 uint32_t value = outAllocation.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001236 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001237 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
1238 data[i] = value++;
1239
1240 if(allocInfo.pMappedData == nullptr)
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001241 vmaUnmapMemory(g_hAllocator, outAllocation.m_Allocation);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001242}
1243
1244static void DestroyAllocation(const AllocInfo& allocation)
1245{
1246 if(allocation.m_Buffer)
1247 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
1248 else
1249 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
1250}
1251
1252static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
1253{
1254 for(size_t i = allocations.size(); i--; )
1255 DestroyAllocation(allocations[i]);
1256 allocations.clear();
1257}
1258
1259static void ValidateAllocationData(const AllocInfo& allocation)
1260{
1261 VmaAllocationInfo allocInfo;
1262 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
1263
1264 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
1265 if(allocInfo.pMappedData == nullptr)
1266 {
1267 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001268 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001269 }
1270
1271 uint32_t value = allocation.m_StartValue;
1272 bool ok = true;
1273 size_t i;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001274 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001275 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
1276 {
1277 if(data[i] != value++)
1278 {
1279 ok = false;
1280 break;
1281 }
1282 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02001283 TEST(ok);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001284
1285 if(allocInfo.pMappedData == nullptr)
1286 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
1287}
1288
1289static void RecreateAllocationResource(AllocInfo& allocation)
1290{
1291 VmaAllocationInfo allocInfo;
1292 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
1293
1294 if(allocation.m_Buffer)
1295 {
Adam Sawicki1f84f622019-07-02 13:40:01 +02001296 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001297
Adam Sawicki1f84f622019-07-02 13:40:01 +02001298 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, g_Allocs, &allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001299 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001300
1301 // Just to silence validation layer warnings.
1302 VkMemoryRequirements vkMemReq;
1303 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
Adam Sawicki2af57d72018-12-06 15:35:05 +01001304 TEST(vkMemReq.size >= allocation.m_BufferInfo.size);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001305
Adam Sawickiaf88c1b2019-07-02 12:34:26 +02001306 res = vmaBindBufferMemory(g_hAllocator, allocation.m_Allocation, allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001307 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001308 }
1309 else
1310 {
Adam Sawicki1f84f622019-07-02 13:40:01 +02001311 vkDestroyImage(g_hDevice, allocation.m_Image, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001312
Adam Sawicki1f84f622019-07-02 13:40:01 +02001313 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, g_Allocs, &allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001314 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001315
1316 // Just to silence validation layer warnings.
1317 VkMemoryRequirements vkMemReq;
1318 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
1319
Adam Sawickiaf88c1b2019-07-02 12:34:26 +02001320 res = vmaBindImageMemory(g_hAllocator, allocation.m_Allocation, allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001321 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001322 }
1323}
1324
1325static void Defragment(AllocInfo* allocs, size_t allocCount,
1326 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
1327 VmaDefragmentationStats* defragmentationStats = nullptr)
1328{
1329 std::vector<VmaAllocation> vmaAllocs(allocCount);
1330 for(size_t i = 0; i < allocCount; ++i)
1331 vmaAllocs[i] = allocs[i].m_Allocation;
1332
1333 std::vector<VkBool32> allocChanged(allocCount);
1334
1335 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
1336 defragmentationInfo, defragmentationStats) );
1337
1338 for(size_t i = 0; i < allocCount; ++i)
1339 {
1340 if(allocChanged[i])
1341 {
1342 RecreateAllocationResource(allocs[i]);
1343 }
1344 }
1345}
1346
1347static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
1348{
1349 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
1350 ValidateAllocationData(allocInfo);
1351 });
1352}
1353
1354void TestDefragmentationSimple()
1355{
1356 wprintf(L"Test defragmentation simple\n");
1357
1358 RandomNumberGenerator rand(667);
1359
1360 const VkDeviceSize BUF_SIZE = 0x10000;
1361 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
1362
1363 const VkDeviceSize MIN_BUF_SIZE = 32;
1364 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
1365 auto RandomBufSize = [&]() -> VkDeviceSize {
1366 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
1367 };
1368
1369 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1370 bufCreateInfo.size = BUF_SIZE;
1371 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1372
1373 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
1374 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1375
1376 uint32_t memTypeIndex = UINT32_MAX;
1377 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
1378
1379 VmaPoolCreateInfo poolCreateInfo = {};
1380 poolCreateInfo.blockSize = BLOCK_SIZE;
1381 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1382
1383 VmaPool pool;
1384 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
1385
Adam Sawickie1681912018-11-23 17:50:12 +01001386 // Defragmentation of empty pool.
1387 {
1388 VmaDefragmentationInfo2 defragInfo = {};
1389 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
1390 defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
1391 defragInfo.poolCount = 1;
1392 defragInfo.pPools = &pool;
1393
1394 VmaDefragmentationStats defragStats = {};
1395 VmaDefragmentationContext defragCtx = nullptr;
1396 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &defragStats, &defragCtx);
1397 TEST(res >= VK_SUCCESS);
1398 vmaDefragmentationEnd(g_hAllocator, defragCtx);
1399 TEST(defragStats.allocationsMoved == 0 && defragStats.bytesFreed == 0 &&
1400 defragStats.bytesMoved == 0 && defragStats.deviceMemoryBlocksFreed == 0);
1401 }
1402
Adam Sawickib8333fb2018-03-13 16:15:53 +01001403 std::vector<AllocInfo> allocations;
1404
1405 // persistentlyMappedOption = 0 - not persistently mapped.
1406 // persistentlyMappedOption = 1 - persistently mapped.
1407 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
1408 {
1409 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
1410 const bool persistentlyMapped = persistentlyMappedOption != 0;
1411
1412 // # Test 1
1413 // Buffers of fixed size.
1414 // Fill 2 blocks. Remove odd buffers. Defragment everything.
1415 // Expected result: at least 1 block freed.
1416 {
1417 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1418 {
1419 AllocInfo allocInfo;
1420 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1421 allocations.push_back(allocInfo);
1422 }
1423
1424 for(size_t i = 1; i < allocations.size(); ++i)
1425 {
1426 DestroyAllocation(allocations[i]);
1427 allocations.erase(allocations.begin() + i);
1428 }
1429
1430 VmaDefragmentationStats defragStats;
1431 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001432 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
1433 TEST(defragStats.deviceMemoryBlocksFreed >= 1);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001434
1435 ValidateAllocationsData(allocations.data(), allocations.size());
1436
1437 DestroyAllAllocations(allocations);
1438 }
1439
1440 // # Test 2
1441 // Buffers of fixed size.
1442 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
1443 // Expected result: Each of 4 interations makes some progress.
1444 {
1445 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1446 {
1447 AllocInfo allocInfo;
1448 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1449 allocations.push_back(allocInfo);
1450 }
1451
1452 for(size_t i = 1; i < allocations.size(); ++i)
1453 {
1454 DestroyAllocation(allocations[i]);
1455 allocations.erase(allocations.begin() + i);
1456 }
1457
1458 VmaDefragmentationInfo defragInfo = {};
1459 defragInfo.maxAllocationsToMove = 1;
1460 defragInfo.maxBytesToMove = BUF_SIZE;
1461
1462 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
1463 {
1464 VmaDefragmentationStats defragStats;
1465 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001466 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001467 }
1468
1469 ValidateAllocationsData(allocations.data(), allocations.size());
1470
1471 DestroyAllAllocations(allocations);
1472 }
1473
1474 // # Test 3
1475 // Buffers of variable size.
1476 // Create a number of buffers. Remove some percent of them.
1477 // Defragment while having some percent of them unmovable.
1478 // Expected result: Just simple validation.
1479 {
1480 for(size_t i = 0; i < 100; ++i)
1481 {
1482 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
1483 localBufCreateInfo.size = RandomBufSize();
1484
1485 AllocInfo allocInfo;
1486 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1487 allocations.push_back(allocInfo);
1488 }
1489
1490 const uint32_t percentToDelete = 60;
1491 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
1492 for(size_t i = 0; i < numberToDelete; ++i)
1493 {
1494 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
1495 DestroyAllocation(allocations[indexToDelete]);
1496 allocations.erase(allocations.begin() + indexToDelete);
1497 }
1498
1499 // Non-movable allocations will be at the beginning of allocations array.
1500 const uint32_t percentNonMovable = 20;
1501 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
1502 for(size_t i = 0; i < numberNonMovable; ++i)
1503 {
1504 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
1505 if(indexNonMovable != i)
1506 std::swap(allocations[i], allocations[indexNonMovable]);
1507 }
1508
1509 VmaDefragmentationStats defragStats;
1510 Defragment(
1511 allocations.data() + numberNonMovable,
1512 allocations.size() - numberNonMovable,
1513 nullptr, &defragStats);
1514
1515 ValidateAllocationsData(allocations.data(), allocations.size());
1516
1517 DestroyAllAllocations(allocations);
1518 }
1519 }
1520
Adam Sawicki647cf242018-11-23 17:58:00 +01001521 /*
1522 Allocation that must be move to an overlapping place using memmove().
1523 Create 2 buffers, second slightly bigger than the first. Delete first. Then defragment.
1524 */
Adam Sawickibdb89a92018-12-13 11:56:30 +01001525 if(VMA_DEBUG_MARGIN == 0) // FAST algorithm works only when DEBUG_MARGIN disabled.
Adam Sawicki647cf242018-11-23 17:58:00 +01001526 {
1527 AllocInfo allocInfo[2];
1528
1529 bufCreateInfo.size = BUF_SIZE;
1530 CreateBuffer(pool, bufCreateInfo, false, allocInfo[0]);
1531 const VkDeviceSize biggerBufSize = BUF_SIZE + BUF_SIZE / 256;
1532 bufCreateInfo.size = biggerBufSize;
1533 CreateBuffer(pool, bufCreateInfo, false, allocInfo[1]);
1534
1535 DestroyAllocation(allocInfo[0]);
1536
1537 VmaDefragmentationStats defragStats;
1538 Defragment(&allocInfo[1], 1, nullptr, &defragStats);
1539 // If this fails, it means we couldn't do memmove with overlapping regions.
1540 TEST(defragStats.allocationsMoved == 1 && defragStats.bytesMoved > 0);
1541
1542 ValidateAllocationsData(&allocInfo[1], 1);
1543 DestroyAllocation(allocInfo[1]);
1544 }
1545
Adam Sawickib8333fb2018-03-13 16:15:53 +01001546 vmaDestroyPool(g_hAllocator, pool);
1547}
1548
Adam Sawicki52076eb2018-11-22 16:14:50 +01001549void TestDefragmentationWholePool()
1550{
1551 wprintf(L"Test defragmentation whole pool\n");
1552
1553 RandomNumberGenerator rand(668);
1554
1555 const VkDeviceSize BUF_SIZE = 0x10000;
1556 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
1557
1558 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1559 bufCreateInfo.size = BUF_SIZE;
1560 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1561
1562 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
1563 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1564
1565 uint32_t memTypeIndex = UINT32_MAX;
1566 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
1567
1568 VmaPoolCreateInfo poolCreateInfo = {};
1569 poolCreateInfo.blockSize = BLOCK_SIZE;
1570 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1571
1572 VmaDefragmentationStats defragStats[2];
1573 for(size_t caseIndex = 0; caseIndex < 2; ++caseIndex)
1574 {
1575 VmaPool pool;
1576 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
1577
1578 std::vector<AllocInfo> allocations;
1579
1580 // Buffers of fixed size.
1581 // Fill 2 blocks. Remove odd buffers. Defragment all of them.
1582 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1583 {
1584 AllocInfo allocInfo;
1585 CreateBuffer(pool, bufCreateInfo, false, allocInfo);
1586 allocations.push_back(allocInfo);
1587 }
1588
1589 for(size_t i = 1; i < allocations.size(); ++i)
1590 {
1591 DestroyAllocation(allocations[i]);
1592 allocations.erase(allocations.begin() + i);
1593 }
1594
1595 VmaDefragmentationInfo2 defragInfo = {};
1596 defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
1597 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
1598 std::vector<VmaAllocation> allocationsToDefrag;
1599 if(caseIndex == 0)
1600 {
1601 defragInfo.poolCount = 1;
1602 defragInfo.pPools = &pool;
1603 }
1604 else
1605 {
1606 const size_t allocCount = allocations.size();
1607 allocationsToDefrag.resize(allocCount);
1608 std::transform(
1609 allocations.begin(), allocations.end(),
1610 allocationsToDefrag.begin(),
1611 [](const AllocInfo& allocInfo) { return allocInfo.m_Allocation; });
1612 defragInfo.allocationCount = (uint32_t)allocCount;
1613 defragInfo.pAllocations = allocationsToDefrag.data();
1614 }
1615
1616 VmaDefragmentationContext defragCtx = VK_NULL_HANDLE;
1617 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &defragStats[caseIndex], &defragCtx);
1618 TEST(res >= VK_SUCCESS);
1619 vmaDefragmentationEnd(g_hAllocator, defragCtx);
1620
1621 TEST(defragStats[caseIndex].allocationsMoved > 0 && defragStats[caseIndex].bytesMoved > 0);
1622
1623 ValidateAllocationsData(allocations.data(), allocations.size());
1624
1625 DestroyAllAllocations(allocations);
1626
1627 vmaDestroyPool(g_hAllocator, pool);
1628 }
1629
1630 TEST(defragStats[0].bytesMoved == defragStats[1].bytesMoved);
1631 TEST(defragStats[0].allocationsMoved == defragStats[1].allocationsMoved);
1632 TEST(defragStats[0].bytesFreed == defragStats[1].bytesFreed);
1633 TEST(defragStats[0].deviceMemoryBlocksFreed == defragStats[1].deviceMemoryBlocksFreed);
1634}
1635
Adam Sawickib8333fb2018-03-13 16:15:53 +01001636void TestDefragmentationFull()
1637{
1638 std::vector<AllocInfo> allocations;
1639
1640 // Create initial allocations.
1641 for(size_t i = 0; i < 400; ++i)
1642 {
1643 AllocInfo allocation;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001644 CreateAllocation(allocation);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001645 allocations.push_back(allocation);
1646 }
1647
1648 // Delete random allocations
1649 const size_t allocationsToDeletePercent = 80;
1650 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
1651 for(size_t i = 0; i < allocationsToDelete; ++i)
1652 {
1653 size_t index = (size_t)rand() % allocations.size();
1654 DestroyAllocation(allocations[index]);
1655 allocations.erase(allocations.begin() + index);
1656 }
1657
1658 for(size_t i = 0; i < allocations.size(); ++i)
1659 ValidateAllocationData(allocations[i]);
1660
Adam Sawicki0667e332018-08-24 17:26:44 +02001661 //SaveAllocatorStatsToFile(L"Before.csv");
Adam Sawickib8333fb2018-03-13 16:15:53 +01001662
1663 {
1664 std::vector<VmaAllocation> vmaAllocations(allocations.size());
1665 for(size_t i = 0; i < allocations.size(); ++i)
1666 vmaAllocations[i] = allocations[i].m_Allocation;
1667
1668 const size_t nonMovablePercent = 0;
1669 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
1670 for(size_t i = 0; i < nonMovableCount; ++i)
1671 {
1672 size_t index = (size_t)rand() % vmaAllocations.size();
1673 vmaAllocations.erase(vmaAllocations.begin() + index);
1674 }
1675
1676 const uint32_t defragCount = 1;
1677 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1678 {
1679 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1680
1681 VmaDefragmentationInfo defragmentationInfo;
1682 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1683 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1684
1685 wprintf(L"Defragmentation #%u\n", defragIndex);
1686
1687 time_point begTime = std::chrono::high_resolution_clock::now();
1688
1689 VmaDefragmentationStats stats;
1690 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001691 TEST(res >= 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001692
1693 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1694
1695 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1696 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1697 wprintf(L"Time: %.2f s\n", defragmentDuration);
1698
1699 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1700 {
1701 if(allocationsChanged[i])
1702 {
1703 RecreateAllocationResource(allocations[i]);
1704 }
1705 }
1706
1707 for(size_t i = 0; i < allocations.size(); ++i)
1708 ValidateAllocationData(allocations[i]);
1709
Adam Sawicki0667e332018-08-24 17:26:44 +02001710 //wchar_t fileName[MAX_PATH];
1711 //swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
1712 //SaveAllocatorStatsToFile(fileName);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001713 }
1714 }
1715
1716 // Destroy all remaining allocations.
1717 DestroyAllAllocations(allocations);
1718}
1719
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001720static void TestDefragmentationGpu()
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001721{
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001722 wprintf(L"Test defragmentation GPU\n");
Adam Sawicki05704002018-11-08 16:07:29 +01001723 g_MemoryAliasingWarningEnabled = false;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001724
1725 std::vector<AllocInfo> allocations;
1726
1727 // Create that many allocations to surely fill 3 new blocks of 256 MB.
Adam Sawickic6ede152018-11-16 17:04:14 +01001728 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
1729 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001730 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
Adam Sawickic6ede152018-11-16 17:04:14 +01001731 const size_t bufCount = (size_t)(totalSize / bufSizeMin);
1732 const size_t percentToLeave = 30;
1733 const size_t percentNonMovable = 3;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001734 RandomNumberGenerator rand = { 234522 };
1735
1736 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001737
1738 VmaAllocationCreateInfo allocCreateInfo = {};
1739 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
Adam Sawickic6ede152018-11-16 17:04:14 +01001740 allocCreateInfo.flags = 0;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001741
1742 // Create all intended buffers.
1743 for(size_t i = 0; i < bufCount; ++i)
1744 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001745 bufCreateInfo.size = align_up(rand.Generate() % (bufSizeMax - bufSizeMin) + bufSizeMin, 32ull);
1746
1747 if(rand.Generate() % 100 < percentNonMovable)
1748 {
1749 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
1750 VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1751 VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1752 allocCreateInfo.pUserData = (void*)(uintptr_t)2;
1753 }
1754 else
1755 {
1756 // Different usage just to see different color in output from VmaDumpVis.
1757 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
1758 VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1759 VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1760 // And in JSON dump.
1761 allocCreateInfo.pUserData = (void*)(uintptr_t)1;
1762 }
1763
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001764 AllocInfo alloc;
1765 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
1766 alloc.m_StartValue = rand.Generate();
1767 allocations.push_back(alloc);
1768 }
1769
1770 // Destroy some percentage of them.
1771 {
1772 const size_t buffersToDestroy = round_div<size_t>(bufCount * (100 - percentToLeave), 100);
1773 for(size_t i = 0; i < buffersToDestroy; ++i)
1774 {
1775 const size_t index = rand.Generate() % allocations.size();
1776 allocations[index].Destroy();
1777 allocations.erase(allocations.begin() + index);
1778 }
1779 }
1780
1781 // Fill them with meaningful data.
1782 UploadGpuData(allocations.data(), allocations.size());
1783
Adam Sawickic6ede152018-11-16 17:04:14 +01001784 wchar_t fileName[MAX_PATH];
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001785 swprintf_s(fileName, L"GPU_defragmentation_A_before.json");
Adam Sawickic6ede152018-11-16 17:04:14 +01001786 SaveAllocatorStatsToFile(fileName);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001787
1788 // Defragment using GPU only.
1789 {
1790 const size_t allocCount = allocations.size();
Adam Sawicki440307e2018-10-18 15:05:19 +02001791
Adam Sawickic6ede152018-11-16 17:04:14 +01001792 std::vector<VmaAllocation> allocationPtrs;
1793 std::vector<VkBool32> allocationChanged;
1794 std::vector<size_t> allocationOriginalIndex;
1795
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001796 for(size_t i = 0; i < allocCount; ++i)
1797 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001798 VmaAllocationInfo allocInfo = {};
1799 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
1800 if((uintptr_t)allocInfo.pUserData == 1) // Movable
1801 {
1802 allocationPtrs.push_back(allocations[i].m_Allocation);
1803 allocationChanged.push_back(VK_FALSE);
1804 allocationOriginalIndex.push_back(i);
1805 }
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001806 }
Adam Sawickic6ede152018-11-16 17:04:14 +01001807
1808 const size_t movableAllocCount = allocationPtrs.size();
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001809
1810 BeginSingleTimeCommands();
1811
1812 VmaDefragmentationInfo2 defragInfo = {};
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001813 defragInfo.flags = 0;
Adam Sawickic6ede152018-11-16 17:04:14 +01001814 defragInfo.allocationCount = (uint32_t)movableAllocCount;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001815 defragInfo.pAllocations = allocationPtrs.data();
Adam Sawicki440307e2018-10-18 15:05:19 +02001816 defragInfo.pAllocationsChanged = allocationChanged.data();
1817 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001818 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
1819 defragInfo.commandBuffer = g_hTemporaryCommandBuffer;
1820
1821 VmaDefragmentationStats stats = {};
1822 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
1823 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
1824 TEST(res >= VK_SUCCESS);
1825
1826 EndSingleTimeCommands();
1827
1828 vmaDefragmentationEnd(g_hAllocator, ctx);
1829
Adam Sawickic6ede152018-11-16 17:04:14 +01001830 for(size_t i = 0; i < movableAllocCount; ++i)
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001831 {
1832 if(allocationChanged[i])
1833 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001834 const size_t origAllocIndex = allocationOriginalIndex[i];
1835 RecreateAllocationResource(allocations[origAllocIndex]);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001836 }
1837 }
1838
Adam Sawicki4d844e22019-01-24 16:21:05 +01001839 // If corruption detection is enabled, GPU defragmentation may not work on
1840 // memory types that have this detection active, e.g. on Intel.
Adam Sawickia1f727c2019-01-24 16:25:11 +01001841 #if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
Adam Sawicki4d844e22019-01-24 16:21:05 +01001842 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
1843 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
Adam Sawickia1f727c2019-01-24 16:25:11 +01001844 #endif
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001845 }
1846
1847 ValidateGpuData(allocations.data(), allocations.size());
1848
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001849 swprintf_s(fileName, L"GPU_defragmentation_B_after.json");
Adam Sawickic6ede152018-11-16 17:04:14 +01001850 SaveAllocatorStatsToFile(fileName);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001851
1852 // Destroy all remaining buffers.
1853 for(size_t i = allocations.size(); i--; )
1854 {
1855 allocations[i].Destroy();
1856 }
Adam Sawicki05704002018-11-08 16:07:29 +01001857
1858 g_MemoryAliasingWarningEnabled = true;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001859}
1860
Adam Sawickic467e282019-12-23 16:38:31 +01001861static void ProcessDefragmentationStepInfo(VmaDefragmentationPassInfo &stepInfo)
Adam Sawickia52012d2019-12-23 15:28:51 +01001862{
1863 std::vector<VkImageMemoryBarrier> beginImageBarriers;
1864 std::vector<VkImageMemoryBarrier> finalizeImageBarriers;
1865
1866 VkPipelineStageFlags beginSrcStageMask = 0;
1867 VkPipelineStageFlags beginDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1868
1869 VkPipelineStageFlags finalizeSrcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1870 VkPipelineStageFlags finalizeDstStageMask = 0;
1871
1872 bool wantsMemoryBarrier = false;
1873
1874 VkMemoryBarrier beginMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
1875 VkMemoryBarrier finalizeMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
1876
Adam Sawickic467e282019-12-23 16:38:31 +01001877 for(uint32_t i = 0; i < stepInfo.moveCount; ++i)
Adam Sawickia52012d2019-12-23 15:28:51 +01001878 {
1879 VmaAllocationInfo info;
1880 vmaGetAllocationInfo(g_hAllocator, stepInfo.pMoves[i].allocation, &info);
1881
1882 AllocInfo *allocInfo = (AllocInfo *)info.pUserData;
1883
1884 if(allocInfo->m_Image)
1885 {
1886 VkImage newImage;
1887
1888 const VkResult result = vkCreateImage(g_hDevice, &allocInfo->m_ImageInfo, g_Allocs, &newImage);
1889 TEST(result >= VK_SUCCESS);
1890
1891 vkBindImageMemory(g_hDevice, newImage, stepInfo.pMoves[i].memory, stepInfo.pMoves[i].offset);
Adam Sawickic467e282019-12-23 16:38:31 +01001892 allocInfo->m_NewImage = newImage;
Adam Sawickia52012d2019-12-23 15:28:51 +01001893
1894 // Keep track of our pipeline stages that we need to wait/signal on
1895 beginSrcStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1896 finalizeDstStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1897
1898 // We need one pipeline barrier and two image layout transitions here
1899 // First we'll have to turn our newly created image into VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
1900 // And the second one is turning the old image into VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
1901
1902 VkImageSubresourceRange subresourceRange = {
1903 VK_IMAGE_ASPECT_COLOR_BIT,
1904 0, VK_REMAINING_MIP_LEVELS,
1905 0, VK_REMAINING_ARRAY_LAYERS
1906 };
1907
1908 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
1909 barrier.srcAccessMask = 0;
1910 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1911 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1912 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1913 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1914 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1915 barrier.image = newImage;
1916 barrier.subresourceRange = subresourceRange;
1917
1918 beginImageBarriers.push_back(barrier);
1919
1920 // Second barrier to convert the existing image. This one actually needs a real barrier
1921 barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
1922 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1923 barrier.oldLayout = allocInfo->m_ImageLayout;
1924 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1925 barrier.image = allocInfo->m_Image;
1926
1927 beginImageBarriers.push_back(barrier);
1928
1929 // And lastly we need a barrier that turns our new image into the layout of the old one
1930 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1931 barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1932 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1933 barrier.newLayout = allocInfo->m_ImageLayout;
1934 barrier.image = newImage;
1935
1936 finalizeImageBarriers.push_back(barrier);
1937 }
1938 else if(allocInfo->m_Buffer)
1939 {
1940 VkBuffer newBuffer;
1941
1942 const VkResult result = vkCreateBuffer(g_hDevice, &allocInfo->m_BufferInfo, g_Allocs, &newBuffer);
1943 TEST(result >= VK_SUCCESS);
1944
1945 vkBindBufferMemory(g_hDevice, newBuffer, stepInfo.pMoves[i].memory, stepInfo.pMoves[i].offset);
Adam Sawickic467e282019-12-23 16:38:31 +01001946 allocInfo->m_NewBuffer = newBuffer;
Adam Sawickia52012d2019-12-23 15:28:51 +01001947
1948 // Keep track of our pipeline stages that we need to wait/signal on
1949 beginSrcStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1950 finalizeDstStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1951
1952 beginMemoryBarrier.srcAccessMask |= VK_ACCESS_MEMORY_WRITE_BIT;
1953 beginMemoryBarrier.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
1954
1955 finalizeMemoryBarrier.srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
1956 finalizeMemoryBarrier.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT;
1957
1958 wantsMemoryBarrier = true;
1959 }
1960 }
1961
1962 if(!beginImageBarriers.empty() || wantsMemoryBarrier)
1963 {
1964 const uint32_t memoryBarrierCount = wantsMemoryBarrier ? 1 : 0;
1965
1966 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, beginSrcStageMask, beginDstStageMask, 0,
1967 memoryBarrierCount, &beginMemoryBarrier,
1968 0, nullptr,
1969 (uint32_t)beginImageBarriers.size(), beginImageBarriers.data());
1970 }
1971
1972 for(uint32_t i = 0; i < stepInfo.moveCount; ++ i)
1973 {
1974 VmaAllocationInfo info;
1975 vmaGetAllocationInfo(g_hAllocator, stepInfo.pMoves[i].allocation, &info);
1976
1977 AllocInfo *allocInfo = (AllocInfo *)info.pUserData;
1978
1979 if(allocInfo->m_Image)
1980 {
1981 std::vector<VkImageCopy> imageCopies;
1982
1983 // Copy all mips of the source image into the target image
1984 VkOffset3D offset = { 0, 0, 0 };
1985 VkExtent3D extent = allocInfo->m_ImageInfo.extent;
1986
1987 VkImageSubresourceLayers subresourceLayers = {
1988 VK_IMAGE_ASPECT_COLOR_BIT,
1989 0,
1990 0, 1
1991 };
1992
1993 for(uint32_t mip = 0; mip < allocInfo->m_ImageInfo.mipLevels; ++ mip)
1994 {
1995 subresourceLayers.mipLevel = mip;
1996
1997 VkImageCopy imageCopy{
1998 subresourceLayers,
1999 offset,
2000 subresourceLayers,
2001 offset,
2002 extent
2003 };
2004
2005 imageCopies.push_back(imageCopy);
2006
2007 extent.width = std::max(uint32_t(1), extent.width >> 1);
2008 extent.height = std::max(uint32_t(1), extent.height >> 1);
2009 extent.depth = std::max(uint32_t(1), extent.depth >> 1);
2010 }
2011
2012 vkCmdCopyImage(
2013 g_hTemporaryCommandBuffer,
2014 allocInfo->m_Image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
Adam Sawickic467e282019-12-23 16:38:31 +01002015 allocInfo->m_NewImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
Adam Sawickia52012d2019-12-23 15:28:51 +01002016 (uint32_t)imageCopies.size(), imageCopies.data());
Adam Sawickia52012d2019-12-23 15:28:51 +01002017 }
2018 else if(allocInfo->m_Buffer)
2019 {
2020 VkBufferCopy region = {
2021 0,
2022 0,
2023 allocInfo->m_BufferInfo.size };
2024
2025 vkCmdCopyBuffer(g_hTemporaryCommandBuffer,
Adam Sawickic467e282019-12-23 16:38:31 +01002026 allocInfo->m_Buffer, allocInfo->m_NewBuffer,
Adam Sawickia52012d2019-12-23 15:28:51 +01002027 1, &region);
Adam Sawickia52012d2019-12-23 15:28:51 +01002028 }
2029 }
2030
Adam Sawickia52012d2019-12-23 15:28:51 +01002031 if(!finalizeImageBarriers.empty() || wantsMemoryBarrier)
2032 {
2033 const uint32_t memoryBarrierCount = wantsMemoryBarrier ? 1 : 0;
2034
2035 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, finalizeSrcStageMask, finalizeDstStageMask, 0,
2036 memoryBarrierCount, &finalizeMemoryBarrier,
2037 0, nullptr,
2038 (uint32_t)finalizeImageBarriers.size(), finalizeImageBarriers.data());
2039 }
2040}
2041
2042
2043static void TestDefragmentationIncrementalBasic()
2044{
2045 wprintf(L"Test defragmentation incremental basic\n");
2046 g_MemoryAliasingWarningEnabled = false;
2047
2048 std::vector<AllocInfo> allocations;
2049
2050 // Create that many allocations to surely fill 3 new blocks of 256 MB.
2051 const std::array<uint32_t, 3> imageSizes = { 256, 512, 1024 };
2052 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
2053 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
2054 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
Adam Sawickic467e282019-12-23 16:38:31 +01002055 const size_t imageCount = totalSize / ((size_t)imageSizes[0] * imageSizes[0] * 4) / 2;
Adam Sawickia52012d2019-12-23 15:28:51 +01002056 const size_t bufCount = (size_t)(totalSize / bufSizeMin) / 2;
2057 const size_t percentToLeave = 30;
2058 RandomNumberGenerator rand = { 234522 };
2059
2060 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2061 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2062 imageInfo.extent.depth = 1;
2063 imageInfo.mipLevels = 1;
2064 imageInfo.arrayLayers = 1;
2065 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2066 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
2067 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2068 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2069 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2070
2071 VmaAllocationCreateInfo allocCreateInfo = {};
2072 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2073 allocCreateInfo.flags = 0;
2074
2075 // Create all intended images.
2076 for(size_t i = 0; i < imageCount; ++i)
2077 {
2078 const uint32_t size = imageSizes[rand.Generate() % 3];
2079
2080 imageInfo.extent.width = size;
2081 imageInfo.extent.height = size;
2082
2083 AllocInfo alloc;
2084 alloc.CreateImage(imageInfo, allocCreateInfo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
2085 alloc.m_StartValue = 0;
2086
2087 allocations.push_back(alloc);
2088 }
2089
2090 // And all buffers
2091 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2092
2093 for(size_t i = 0; i < bufCount; ++i)
2094 {
2095 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
2096 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2097
2098 AllocInfo alloc;
2099 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
2100 alloc.m_StartValue = 0;
2101
2102 allocations.push_back(alloc);
2103 }
2104
2105 // Destroy some percentage of them.
2106 {
2107 const size_t allocationsToDestroy = round_div<size_t>((imageCount + bufCount) * (100 - percentToLeave), 100);
2108 for(size_t i = 0; i < allocationsToDestroy; ++i)
2109 {
2110 const size_t index = rand.Generate() % allocations.size();
2111 allocations[index].Destroy();
2112 allocations.erase(allocations.begin() + index);
2113 }
2114 }
2115
2116 {
2117 // Set our user data pointers. A real application should probably be more clever here
2118 const size_t allocationCount = allocations.size();
2119 for(size_t i = 0; i < allocationCount; ++i)
2120 {
2121 AllocInfo &alloc = allocations[i];
2122 vmaSetAllocationUserData(g_hAllocator, alloc.m_Allocation, &alloc);
2123 }
2124 }
2125
2126 // Fill them with meaningful data.
2127 UploadGpuData(allocations.data(), allocations.size());
2128
2129 wchar_t fileName[MAX_PATH];
2130 swprintf_s(fileName, L"GPU_defragmentation_incremental_basic_A_before.json");
2131 SaveAllocatorStatsToFile(fileName);
2132
2133 // Defragment using GPU only.
2134 {
2135 const size_t allocCount = allocations.size();
2136
2137 std::vector<VmaAllocation> allocationPtrs;
2138
2139 for(size_t i = 0; i < allocCount; ++i)
2140 {
Adam Sawickia52012d2019-12-23 15:28:51 +01002141 allocationPtrs.push_back(allocations[i].m_Allocation);
2142 }
2143
2144 const size_t movableAllocCount = allocationPtrs.size();
2145
2146 VmaDefragmentationInfo2 defragInfo = {};
2147 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_INCREMENTAL;
2148 defragInfo.allocationCount = (uint32_t)movableAllocCount;
2149 defragInfo.pAllocations = allocationPtrs.data();
2150 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2151 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2152
2153 VmaDefragmentationStats stats = {};
2154 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
2155 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
2156 TEST(res >= VK_SUCCESS);
2157
2158 res = VK_NOT_READY;
2159
Adam Sawickic467e282019-12-23 16:38:31 +01002160 std::vector<VmaDefragmentationPassMoveInfo> moveInfo;
Adam Sawickia52012d2019-12-23 15:28:51 +01002161 moveInfo.resize(movableAllocCount);
2162
2163 while(res == VK_NOT_READY)
2164 {
Adam Sawickic467e282019-12-23 16:38:31 +01002165 VmaDefragmentationPassInfo stepInfo = {};
Adam Sawickia52012d2019-12-23 15:28:51 +01002166 stepInfo.pMoves = moveInfo.data();
2167 stepInfo.moveCount = (uint32_t)moveInfo.size();
2168
Adam Sawickic467e282019-12-23 16:38:31 +01002169 res = vmaBeginDefragmentationPass(g_hAllocator, ctx, &stepInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +01002170 TEST(res >= VK_SUCCESS);
2171
2172 BeginSingleTimeCommands();
Adam Sawickic467e282019-12-23 16:38:31 +01002173 std::vector<void*> newHandles;
Adam Sawickia52012d2019-12-23 15:28:51 +01002174 ProcessDefragmentationStepInfo(stepInfo);
2175 EndSingleTimeCommands();
2176
Adam Sawickic467e282019-12-23 16:38:31 +01002177 res = vmaEndDefragmentationPass(g_hAllocator, ctx);
2178
2179 // Destroy old buffers/images and replace them with new handles.
2180 for(size_t i = 0; i < stepInfo.moveCount; ++i)
2181 {
2182 VmaAllocation const alloc = stepInfo.pMoves[i].allocation;
2183 VmaAllocationInfo vmaAllocInfo;
2184 vmaGetAllocationInfo(g_hAllocator, alloc, &vmaAllocInfo);
2185 AllocInfo* allocInfo = (AllocInfo*)vmaAllocInfo.pUserData;
2186 if(allocInfo->m_Buffer)
2187 {
2188 assert(allocInfo->m_NewBuffer && !allocInfo->m_Image && !allocInfo->m_NewImage);
2189 vkDestroyBuffer(g_hDevice, allocInfo->m_Buffer, g_Allocs);
2190 allocInfo->m_Buffer = allocInfo->m_NewBuffer;
2191 allocInfo->m_NewBuffer = VK_NULL_HANDLE;
2192 }
2193 else if(allocInfo->m_Image)
2194 {
2195 assert(allocInfo->m_NewImage && !allocInfo->m_Buffer && !allocInfo->m_NewBuffer);
2196 vkDestroyImage(g_hDevice, allocInfo->m_Image, g_Allocs);
2197 allocInfo->m_Image = allocInfo->m_NewImage;
2198 allocInfo->m_NewImage = VK_NULL_HANDLE;
2199 }
2200 else
2201 assert(0);
2202 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002203 }
2204
2205 TEST(res >= VK_SUCCESS);
2206 vmaDefragmentationEnd(g_hAllocator, ctx);
2207
2208 // If corruption detection is enabled, GPU defragmentation may not work on
2209 // memory types that have this detection active, e.g. on Intel.
2210#if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
2211 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
2212 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
2213#endif
2214 }
2215
2216 //ValidateGpuData(allocations.data(), allocations.size());
2217
2218 swprintf_s(fileName, L"GPU_defragmentation_incremental_basic_B_after.json");
2219 SaveAllocatorStatsToFile(fileName);
2220
Adam Sawickic467e282019-12-23 16:38:31 +01002221 // Destroy all remaining buffers and images.
Adam Sawickia52012d2019-12-23 15:28:51 +01002222 for(size_t i = allocations.size(); i--; )
2223 {
2224 allocations[i].Destroy();
2225 }
2226
2227 g_MemoryAliasingWarningEnabled = true;
2228}
2229
2230void TestDefragmentationIncrementalComplex()
2231{
2232 wprintf(L"Test defragmentation incremental complex\n");
2233 g_MemoryAliasingWarningEnabled = false;
2234
2235 std::vector<AllocInfo> allocations;
2236
2237 // Create that many allocations to surely fill 3 new blocks of 256 MB.
2238 const std::array<uint32_t, 3> imageSizes = { 256, 512, 1024 };
2239 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
2240 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
2241 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
2242 const size_t imageCount = (size_t)(totalSize / (imageSizes[0] * imageSizes[0] * 4)) / 2;
2243 const size_t bufCount = (size_t)(totalSize / bufSizeMin) / 2;
2244 const size_t percentToLeave = 30;
2245 RandomNumberGenerator rand = { 234522 };
2246
2247 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2248 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2249 imageInfo.extent.depth = 1;
2250 imageInfo.mipLevels = 1;
2251 imageInfo.arrayLayers = 1;
2252 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2253 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
2254 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2255 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2256 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2257
2258 VmaAllocationCreateInfo allocCreateInfo = {};
2259 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2260 allocCreateInfo.flags = 0;
2261
2262 // Create all intended images.
2263 for(size_t i = 0; i < imageCount; ++i)
2264 {
2265 const uint32_t size = imageSizes[rand.Generate() % 3];
2266
2267 imageInfo.extent.width = size;
2268 imageInfo.extent.height = size;
2269
2270 AllocInfo alloc;
2271 alloc.CreateImage(imageInfo, allocCreateInfo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
2272 alloc.m_StartValue = 0;
2273
2274 allocations.push_back(alloc);
2275 }
2276
2277 // And all buffers
2278 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2279
2280 for(size_t i = 0; i < bufCount; ++i)
2281 {
2282 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
2283 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2284
2285 AllocInfo alloc;
2286 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
2287 alloc.m_StartValue = 0;
2288
2289 allocations.push_back(alloc);
2290 }
2291
2292 // Destroy some percentage of them.
2293 {
2294 const size_t allocationsToDestroy = round_div<size_t>((imageCount + bufCount) * (100 - percentToLeave), 100);
2295 for(size_t i = 0; i < allocationsToDestroy; ++i)
2296 {
2297 const size_t index = rand.Generate() % allocations.size();
2298 allocations[index].Destroy();
2299 allocations.erase(allocations.begin() + index);
2300 }
2301 }
2302
2303 {
2304 // Set our user data pointers. A real application should probably be more clever here
2305 const size_t allocationCount = allocations.size();
2306 for(size_t i = 0; i < allocationCount; ++i)
2307 {
2308 AllocInfo &alloc = allocations[i];
2309 vmaSetAllocationUserData(g_hAllocator, alloc.m_Allocation, &alloc);
2310 }
2311 }
2312
2313 // Fill them with meaningful data.
2314 UploadGpuData(allocations.data(), allocations.size());
2315
2316 wchar_t fileName[MAX_PATH];
2317 swprintf_s(fileName, L"GPU_defragmentation_incremental_complex_A_before.json");
2318 SaveAllocatorStatsToFile(fileName);
2319
2320 std::vector<AllocInfo> additionalAllocations;
2321
2322#define MakeAdditionalAllocation() \
2323 do { \
2324 { \
2325 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16); \
2326 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; \
2327 \
2328 AllocInfo alloc; \
2329 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo); \
2330 \
2331 additionalAllocations.push_back(alloc); \
2332 } \
2333 } while(0)
2334
2335 // Defragment using GPU only.
2336 {
2337 const size_t allocCount = allocations.size();
2338
2339 std::vector<VmaAllocation> allocationPtrs;
2340
2341 for(size_t i = 0; i < allocCount; ++i)
2342 {
2343 VmaAllocationInfo allocInfo = {};
2344 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
2345
2346 allocationPtrs.push_back(allocations[i].m_Allocation);
2347 }
2348
2349 const size_t movableAllocCount = allocationPtrs.size();
2350
2351 VmaDefragmentationInfo2 defragInfo = {};
2352 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_INCREMENTAL;
2353 defragInfo.allocationCount = (uint32_t)movableAllocCount;
2354 defragInfo.pAllocations = allocationPtrs.data();
2355 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2356 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2357
2358 VmaDefragmentationStats stats = {};
2359 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
2360 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
2361 TEST(res >= VK_SUCCESS);
2362
2363 res = VK_NOT_READY;
2364
Adam Sawickic467e282019-12-23 16:38:31 +01002365 std::vector<VmaDefragmentationPassMoveInfo> moveInfo;
Adam Sawickia52012d2019-12-23 15:28:51 +01002366 moveInfo.resize(movableAllocCount);
2367
2368 MakeAdditionalAllocation();
2369
2370 while(res == VK_NOT_READY)
2371 {
Adam Sawickic467e282019-12-23 16:38:31 +01002372 VmaDefragmentationPassInfo stepInfo = {};
Adam Sawickia52012d2019-12-23 15:28:51 +01002373 stepInfo.pMoves = moveInfo.data();
2374 stepInfo.moveCount = (uint32_t)moveInfo.size();
2375
Adam Sawickic467e282019-12-23 16:38:31 +01002376 res = vmaBeginDefragmentationPass(g_hAllocator, ctx, &stepInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +01002377 TEST(res >= VK_SUCCESS);
2378
2379 MakeAdditionalAllocation();
2380
2381 BeginSingleTimeCommands();
2382 ProcessDefragmentationStepInfo(stepInfo);
2383 EndSingleTimeCommands();
2384
Adam Sawickic467e282019-12-23 16:38:31 +01002385 res = vmaEndDefragmentationPass(g_hAllocator, ctx);
2386
2387 // Destroy old buffers/images and replace them with new handles.
2388 for(size_t i = 0; i < stepInfo.moveCount; ++i)
2389 {
2390 VmaAllocation const alloc = stepInfo.pMoves[i].allocation;
2391 VmaAllocationInfo vmaAllocInfo;
2392 vmaGetAllocationInfo(g_hAllocator, alloc, &vmaAllocInfo);
2393 AllocInfo* allocInfo = (AllocInfo*)vmaAllocInfo.pUserData;
2394 if(allocInfo->m_Buffer)
2395 {
2396 assert(allocInfo->m_NewBuffer && !allocInfo->m_Image && !allocInfo->m_NewImage);
2397 vkDestroyBuffer(g_hDevice, allocInfo->m_Buffer, g_Allocs);
2398 allocInfo->m_Buffer = allocInfo->m_NewBuffer;
2399 allocInfo->m_NewBuffer = VK_NULL_HANDLE;
2400 }
2401 else if(allocInfo->m_Image)
2402 {
2403 assert(allocInfo->m_NewImage && !allocInfo->m_Buffer && !allocInfo->m_NewBuffer);
2404 vkDestroyImage(g_hDevice, allocInfo->m_Image, g_Allocs);
2405 allocInfo->m_Image = allocInfo->m_NewImage;
2406 allocInfo->m_NewImage = VK_NULL_HANDLE;
2407 }
2408 else
2409 assert(0);
2410 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002411
2412 MakeAdditionalAllocation();
2413 }
2414
2415 TEST(res >= VK_SUCCESS);
2416 vmaDefragmentationEnd(g_hAllocator, ctx);
2417
2418 // If corruption detection is enabled, GPU defragmentation may not work on
2419 // memory types that have this detection active, e.g. on Intel.
2420#if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
2421 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
2422 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
2423#endif
2424 }
2425
2426 //ValidateGpuData(allocations.data(), allocations.size());
2427
2428 swprintf_s(fileName, L"GPU_defragmentation_incremental_complex_B_after.json");
2429 SaveAllocatorStatsToFile(fileName);
2430
2431 // Destroy all remaining buffers.
2432 for(size_t i = allocations.size(); i--; )
2433 {
2434 allocations[i].Destroy();
2435 }
2436
2437 for(size_t i = additionalAllocations.size(); i--; )
2438 {
2439 additionalAllocations[i].Destroy();
2440 }
2441
2442 g_MemoryAliasingWarningEnabled = true;
2443}
2444
2445
Adam Sawickib8333fb2018-03-13 16:15:53 +01002446static void TestUserData()
2447{
2448 VkResult res;
2449
2450 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2451 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
2452 bufCreateInfo.size = 0x10000;
2453
2454 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
2455 {
2456 // Opaque pointer
2457 {
2458
2459 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
2460 void* pointerToSomething = &res;
2461
2462 VmaAllocationCreateInfo allocCreateInfo = {};
2463 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2464 allocCreateInfo.pUserData = numberAsPointer;
2465 if(testIndex == 1)
2466 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2467
2468 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2469 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002470 TEST(res == VK_SUCCESS);
2471 TEST(allocInfo.pUserData = numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002472
2473 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002474 TEST(allocInfo.pUserData == numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002475
2476 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
2477 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002478 TEST(allocInfo.pUserData == pointerToSomething);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002479
2480 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2481 }
2482
2483 // String
2484 {
2485 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
2486 const char* name2 = "2";
2487 const size_t name1Len = strlen(name1);
2488
2489 char* name1Buf = new char[name1Len + 1];
2490 strcpy_s(name1Buf, name1Len + 1, name1);
2491
2492 VmaAllocationCreateInfo allocCreateInfo = {};
2493 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2494 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
2495 allocCreateInfo.pUserData = name1Buf;
2496 if(testIndex == 1)
2497 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2498
2499 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2500 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002501 TEST(res == VK_SUCCESS);
2502 TEST(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
2503 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002504
2505 delete[] name1Buf;
2506
2507 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002508 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002509
2510 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
2511 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002512 TEST(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002513
2514 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
2515 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002516 TEST(allocInfo.pUserData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002517
2518 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2519 }
2520 }
2521}
2522
Adam Sawicki370ab182018-11-08 16:31:00 +01002523static void TestInvalidAllocations()
2524{
2525 VkResult res;
2526
2527 VmaAllocationCreateInfo allocCreateInfo = {};
2528 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2529
2530 // Try to allocate 0 bytes.
2531 {
2532 VkMemoryRequirements memReq = {};
2533 memReq.size = 0; // !!!
2534 memReq.alignment = 4;
2535 memReq.memoryTypeBits = UINT32_MAX;
2536 VmaAllocation alloc = VK_NULL_HANDLE;
2537 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2538 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && alloc == VK_NULL_HANDLE);
2539 }
2540
2541 // Try to create buffer with size = 0.
2542 {
2543 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2544 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2545 bufCreateInfo.size = 0; // !!!
2546 VkBuffer buf = VK_NULL_HANDLE;
2547 VmaAllocation alloc = VK_NULL_HANDLE;
2548 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
2549 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && buf == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
2550 }
2551
2552 // Try to create image with one dimension = 0.
2553 {
2554 VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2555 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
2556 imageCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
2557 imageCreateInfo.extent.width = 128;
2558 imageCreateInfo.extent.height = 0; // !!!
2559 imageCreateInfo.extent.depth = 1;
2560 imageCreateInfo.mipLevels = 1;
2561 imageCreateInfo.arrayLayers = 1;
2562 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2563 imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
2564 imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
2565 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2566 VkImage image = VK_NULL_HANDLE;
2567 VmaAllocation alloc = VK_NULL_HANDLE;
2568 res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &image, &alloc, nullptr);
2569 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && image == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
2570 }
2571}
2572
Adam Sawickib8333fb2018-03-13 16:15:53 +01002573static void TestMemoryRequirements()
2574{
2575 VkResult res;
2576 VkBuffer buf;
2577 VmaAllocation alloc;
2578 VmaAllocationInfo allocInfo;
2579
2580 const VkPhysicalDeviceMemoryProperties* memProps;
2581 vmaGetMemoryProperties(g_hAllocator, &memProps);
2582
2583 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2584 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2585 bufInfo.size = 128;
2586
2587 VmaAllocationCreateInfo allocCreateInfo = {};
2588
2589 // No requirements.
2590 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002591 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002592 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2593
2594 // Usage.
2595 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2596 allocCreateInfo.requiredFlags = 0;
2597 allocCreateInfo.preferredFlags = 0;
2598 allocCreateInfo.memoryTypeBits = UINT32_MAX;
2599
2600 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002601 TEST(res == VK_SUCCESS);
2602 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002603 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2604
2605 // Required flags, preferred flags.
2606 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
2607 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2608 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
2609 allocCreateInfo.memoryTypeBits = 0;
2610
2611 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002612 TEST(res == VK_SUCCESS);
2613 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
2614 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002615 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2616
2617 // memoryTypeBits.
2618 const uint32_t memType = allocInfo.memoryType;
2619 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2620 allocCreateInfo.requiredFlags = 0;
2621 allocCreateInfo.preferredFlags = 0;
2622 allocCreateInfo.memoryTypeBits = 1u << memType;
2623
2624 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002625 TEST(res == VK_SUCCESS);
2626 TEST(allocInfo.memoryType == memType);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002627 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2628
2629}
2630
Adam Sawickia1d992f2020-03-02 15:32:10 +01002631static void TestGetAllocatorInfo()
2632{
2633 wprintf(L"Test vnaGetAllocatorInfo\n");
2634
2635 VmaAllocatorInfo allocInfo = {};
2636 vmaGetAllocatorInfo(g_hAllocator, &allocInfo);
2637 TEST(allocInfo.instance == g_hVulkanInstance);
2638 TEST(allocInfo.physicalDevice == g_hPhysicalDevice);
2639 TEST(allocInfo.device == g_hDevice);
2640}
2641
Adam Sawickib8333fb2018-03-13 16:15:53 +01002642static void TestBasics()
2643{
Adam Sawickiaaa1a562020-06-24 17:41:09 +02002644 wprintf(L"Test basics\n");
2645
Adam Sawickib8333fb2018-03-13 16:15:53 +01002646 VkResult res;
2647
Adam Sawickia1d992f2020-03-02 15:32:10 +01002648 TestGetAllocatorInfo();
2649
Adam Sawickib8333fb2018-03-13 16:15:53 +01002650 TestMemoryRequirements();
2651
2652 // Lost allocation
2653 {
2654 VmaAllocation alloc = VK_NULL_HANDLE;
2655 vmaCreateLostAllocation(g_hAllocator, &alloc);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002656 TEST(alloc != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002657
2658 VmaAllocationInfo allocInfo;
2659 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002660 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
2661 TEST(allocInfo.size == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002662
2663 vmaFreeMemory(g_hAllocator, alloc);
2664 }
2665
2666 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
2667 {
2668 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2669 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
2670 bufCreateInfo.size = 128;
2671
2672 VmaAllocationCreateInfo allocCreateInfo = {};
2673 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2674 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2675
2676 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2677 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002678 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002679
2680 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2681
2682 // Same with OWN_MEMORY.
2683 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2684
2685 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002686 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002687
2688 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2689 }
2690
2691 TestUserData();
Adam Sawicki370ab182018-11-08 16:31:00 +01002692
2693 TestInvalidAllocations();
Adam Sawickib8333fb2018-03-13 16:15:53 +01002694}
2695
Adam Sawickiaaa1a562020-06-24 17:41:09 +02002696static void TestAllocationVersusResourceSize()
2697{
2698 wprintf(L"Test allocation versus resource size\n");
2699
2700 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2701 bufCreateInfo.size = 22921; // Prime number
2702 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2703
2704 VmaAllocationCreateInfo allocCreateInfo = {};
2705 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2706
2707 for(uint32_t i = 0; i < 2; ++i)
2708 {
2709 allocCreateInfo.flags = (i == 1) ? VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT : 0;
2710
2711 AllocInfo info;
2712 info.CreateBuffer(bufCreateInfo, allocCreateInfo);
2713
2714 VmaAllocationInfo allocInfo = {};
2715 vmaGetAllocationInfo(g_hAllocator, info.m_Allocation, &allocInfo);
2716 //wprintf(L" Buffer size = %llu, allocation size = %llu\n", bufCreateInfo.size, allocInfo.size);
2717
2718 // Map and test accessing entire area of the allocation, not only the buffer.
2719 void* mappedPtr = nullptr;
2720 VkResult res = vmaMapMemory(g_hAllocator, info.m_Allocation, &mappedPtr);
2721 TEST(res == VK_SUCCESS);
2722
2723 memset(mappedPtr, 0xCC, (size_t)allocInfo.size);
2724
2725 vmaUnmapMemory(g_hAllocator, info.m_Allocation);
2726
2727 info.Destroy();
2728 }
2729}
2730
Adam Sawickiddcbf8c2019-11-22 15:22:42 +01002731static void TestPool_MinBlockCount()
2732{
2733#if defined(VMA_DEBUG_MARGIN) && VMA_DEBUG_MARGIN > 0
2734 return;
2735#endif
2736
2737 wprintf(L"Test Pool MinBlockCount\n");
2738 VkResult res;
2739
2740 static const VkDeviceSize ALLOC_SIZE = 512ull * 1024;
2741 static const VkDeviceSize BLOCK_SIZE = ALLOC_SIZE * 2; // Each block can fit 2 allocations.
2742
2743 VmaAllocationCreateInfo allocCreateInfo = {};
2744 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_COPY;
2745
2746 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2747 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2748 bufCreateInfo.size = ALLOC_SIZE;
2749
2750 VmaPoolCreateInfo poolCreateInfo = {};
2751 poolCreateInfo.blockSize = BLOCK_SIZE;
2752 poolCreateInfo.minBlockCount = 2; // At least 2 blocks always present.
2753 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2754 TEST(res == VK_SUCCESS);
2755
2756 VmaPool pool = VK_NULL_HANDLE;
2757 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2758 TEST(res == VK_SUCCESS && pool != VK_NULL_HANDLE);
2759
2760 // Check that there are 2 blocks preallocated as requested.
2761 VmaPoolStats begPoolStats = {};
2762 vmaGetPoolStats(g_hAllocator, pool, &begPoolStats);
2763 TEST(begPoolStats.blockCount == 2 && begPoolStats.allocationCount == 0 && begPoolStats.size == BLOCK_SIZE * 2);
2764
2765 // Allocate 5 buffers to create 3 blocks.
2766 static const uint32_t BUF_COUNT = 5;
2767 allocCreateInfo.pool = pool;
2768 std::vector<AllocInfo> allocs(BUF_COUNT);
2769 for(uint32_t i = 0; i < BUF_COUNT; ++i)
2770 {
2771 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &allocs[i].m_Buffer, &allocs[i].m_Allocation, nullptr);
2772 TEST(res == VK_SUCCESS && allocs[i].m_Buffer != VK_NULL_HANDLE && allocs[i].m_Allocation != VK_NULL_HANDLE);
2773 }
2774
2775 // Check that there are really 3 blocks.
2776 VmaPoolStats poolStats2 = {};
2777 vmaGetPoolStats(g_hAllocator, pool, &poolStats2);
2778 TEST(poolStats2.blockCount == 3 && poolStats2.allocationCount == BUF_COUNT && poolStats2.size == BLOCK_SIZE * 3);
2779
2780 // Free two first allocations to make one block empty.
2781 allocs[0].Destroy();
2782 allocs[1].Destroy();
2783
2784 // Check that there are still 3 blocks due to hysteresis.
2785 VmaPoolStats poolStats3 = {};
2786 vmaGetPoolStats(g_hAllocator, pool, &poolStats3);
2787 TEST(poolStats3.blockCount == 3 && poolStats3.allocationCount == BUF_COUNT - 2 && poolStats2.size == BLOCK_SIZE * 3);
2788
2789 // Free the last allocation to make second block empty.
2790 allocs[BUF_COUNT - 1].Destroy();
2791
2792 // Check that there are now 2 blocks only.
2793 VmaPoolStats poolStats4 = {};
2794 vmaGetPoolStats(g_hAllocator, pool, &poolStats4);
2795 TEST(poolStats4.blockCount == 2 && poolStats4.allocationCount == BUF_COUNT - 3 && poolStats4.size == BLOCK_SIZE * 2);
2796
2797 // Cleanup.
2798 for(size_t i = allocs.size(); i--; )
2799 {
2800 allocs[i].Destroy();
2801 }
2802 vmaDestroyPool(g_hAllocator, pool);
2803}
2804
Adam Sawickib8333fb2018-03-13 16:15:53 +01002805void TestHeapSizeLimit()
2806{
Adam Sawickifbaccff2020-03-09 17:09:23 +01002807 const VkDeviceSize HEAP_SIZE_LIMIT = 100ull * 1024 * 1024; // 100 MB
2808 const VkDeviceSize BLOCK_SIZE = 10ull * 1024 * 1024; // 10 MB
Adam Sawickib8333fb2018-03-13 16:15:53 +01002809
2810 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
2811 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
2812 {
2813 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
2814 }
2815
2816 VmaAllocatorCreateInfo allocatorCreateInfo = {};
2817 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
2818 allocatorCreateInfo.device = g_hDevice;
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002819 allocatorCreateInfo.instance = g_hVulkanInstance;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002820 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
2821
2822 VmaAllocator hAllocator;
2823 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002824 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002825
2826 struct Item
2827 {
2828 VkBuffer hBuf;
2829 VmaAllocation hAlloc;
2830 };
2831 std::vector<Item> items;
2832
2833 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2834 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2835
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002836 // 1. Allocate two blocks of dedicated memory, half the size of BLOCK_SIZE.
2837 VmaAllocationInfo dedicatedAllocInfo;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002838 {
2839 VmaAllocationCreateInfo allocCreateInfo = {};
2840 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2841 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2842
2843 bufCreateInfo.size = BLOCK_SIZE / 2;
2844
2845 for(size_t i = 0; i < 2; ++i)
2846 {
2847 Item item;
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002848 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &dedicatedAllocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002849 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002850 items.push_back(item);
2851 }
2852 }
2853
2854 // Create pool to make sure allocations must be out of this memory type.
2855 VmaPoolCreateInfo poolCreateInfo = {};
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002856 poolCreateInfo.memoryTypeIndex = dedicatedAllocInfo.memoryType;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002857 poolCreateInfo.blockSize = BLOCK_SIZE;
2858
2859 VmaPool hPool;
2860 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002861 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002862
2863 // 2. Allocate normal buffers from all the remaining memory.
2864 {
2865 VmaAllocationCreateInfo allocCreateInfo = {};
2866 allocCreateInfo.pool = hPool;
2867
2868 bufCreateInfo.size = BLOCK_SIZE / 2;
2869
2870 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
2871 for(size_t i = 0; i < bufCount; ++i)
2872 {
2873 Item item;
2874 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002875 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002876 items.push_back(item);
2877 }
2878 }
2879
2880 // 3. Allocation of one more (even small) buffer should fail.
2881 {
2882 VmaAllocationCreateInfo allocCreateInfo = {};
2883 allocCreateInfo.pool = hPool;
2884
2885 bufCreateInfo.size = 128;
2886
2887 VkBuffer hBuf;
2888 VmaAllocation hAlloc;
2889 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002890 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002891 }
2892
2893 // Destroy everything.
2894 for(size_t i = items.size(); i--; )
2895 {
2896 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
2897 }
2898
2899 vmaDestroyPool(hAllocator, hPool);
2900
2901 vmaDestroyAllocator(hAllocator);
2902}
2903
Adam Sawicki212a4a62018-06-14 15:44:45 +02002904#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02002905static void TestDebugMargin()
2906{
2907 if(VMA_DEBUG_MARGIN == 0)
2908 {
2909 return;
2910 }
2911
2912 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02002913 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02002914
2915 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02002916 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02002917
2918 // Create few buffers of different size.
2919 const size_t BUF_COUNT = 10;
2920 BufferInfo buffers[BUF_COUNT];
2921 VmaAllocationInfo allocInfo[BUF_COUNT];
2922 for(size_t i = 0; i < 10; ++i)
2923 {
2924 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02002925 // Last one will be mapped.
2926 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02002927
2928 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002929 TEST(res == VK_SUCCESS);
Adam Sawicki73b16652018-06-11 16:39:25 +02002930 // Margin is preserved also at the beginning of a block.
Adam Sawickib8d34d52018-10-03 17:41:20 +02002931 TEST(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002932
2933 if(i == BUF_COUNT - 1)
2934 {
2935 // Fill with data.
Adam Sawickib8d34d52018-10-03 17:41:20 +02002936 TEST(allocInfo[i].pMappedData != nullptr);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002937 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
2938 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
2939 }
Adam Sawicki73b16652018-06-11 16:39:25 +02002940 }
2941
2942 // Check if their offsets preserve margin between them.
2943 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
2944 {
2945 if(lhs.deviceMemory != rhs.deviceMemory)
2946 {
2947 return lhs.deviceMemory < rhs.deviceMemory;
2948 }
2949 return lhs.offset < rhs.offset;
2950 });
2951 for(size_t i = 1; i < BUF_COUNT; ++i)
2952 {
2953 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
2954 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02002955 TEST(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
Adam Sawicki73b16652018-06-11 16:39:25 +02002956 }
2957 }
2958
Adam Sawicki212a4a62018-06-14 15:44:45 +02002959 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002960 TEST(res == VK_SUCCESS);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002961
Adam Sawicki73b16652018-06-11 16:39:25 +02002962 // Destroy all buffers.
2963 for(size_t i = BUF_COUNT; i--; )
2964 {
2965 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
2966 }
2967}
Adam Sawicki212a4a62018-06-14 15:44:45 +02002968#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02002969
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002970static void TestLinearAllocator()
2971{
2972 wprintf(L"Test linear allocator\n");
2973
2974 RandomNumberGenerator rand{645332};
2975
2976 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2977 sampleBufCreateInfo.size = 1024; // Whatever.
2978 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2979
2980 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2981 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2982
2983 VmaPoolCreateInfo poolCreateInfo = {};
2984 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002985 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002986
Adam Sawickiee082772018-06-20 17:45:49 +02002987 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002988 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2989 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2990
2991 VmaPool pool = nullptr;
2992 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002993 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002994
2995 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
2996
2997 VmaAllocationCreateInfo allocCreateInfo = {};
2998 allocCreateInfo.pool = pool;
2999
3000 constexpr size_t maxBufCount = 100;
3001 std::vector<BufferInfo> bufInfo;
3002
3003 constexpr VkDeviceSize bufSizeMin = 16;
3004 constexpr VkDeviceSize bufSizeMax = 1024;
3005 VmaAllocationInfo allocInfo;
3006 VkDeviceSize prevOffset = 0;
3007
3008 // Test one-time free.
3009 for(size_t i = 0; i < 2; ++i)
3010 {
3011 // Allocate number of buffers of varying size that surely fit into this block.
3012 VkDeviceSize bufSumSize = 0;
3013 for(size_t i = 0; i < maxBufCount; ++i)
3014 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003015 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003016 BufferInfo newBufInfo;
3017 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3018 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003019 TEST(res == VK_SUCCESS);
3020 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003021 bufInfo.push_back(newBufInfo);
3022 prevOffset = allocInfo.offset;
3023 bufSumSize += bufCreateInfo.size;
3024 }
3025
3026 // Validate pool stats.
3027 VmaPoolStats stats;
3028 vmaGetPoolStats(g_hAllocator, pool, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003029 TEST(stats.size == poolCreateInfo.blockSize);
3030 TEST(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
3031 TEST(stats.allocationCount == bufInfo.size());
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003032
3033 // Destroy the buffers in random order.
3034 while(!bufInfo.empty())
3035 {
3036 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3037 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3038 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3039 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3040 }
3041 }
3042
3043 // Test stack.
3044 {
3045 // Allocate number of buffers of varying size that surely fit into this block.
3046 for(size_t i = 0; i < maxBufCount; ++i)
3047 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003048 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003049 BufferInfo newBufInfo;
3050 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3051 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003052 TEST(res == VK_SUCCESS);
3053 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003054 bufInfo.push_back(newBufInfo);
3055 prevOffset = allocInfo.offset;
3056 }
3057
3058 // Destroy few buffers from top of the stack.
3059 for(size_t i = 0; i < maxBufCount / 5; ++i)
3060 {
3061 const BufferInfo& currBufInfo = bufInfo.back();
3062 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3063 bufInfo.pop_back();
3064 }
3065
3066 // Create some more
3067 for(size_t i = 0; i < maxBufCount / 5; ++i)
3068 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003069 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003070 BufferInfo newBufInfo;
3071 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3072 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003073 TEST(res == VK_SUCCESS);
3074 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003075 bufInfo.push_back(newBufInfo);
3076 prevOffset = allocInfo.offset;
3077 }
3078
3079 // Destroy the buffers in reverse order.
3080 while(!bufInfo.empty())
3081 {
3082 const BufferInfo& currBufInfo = bufInfo.back();
3083 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3084 bufInfo.pop_back();
3085 }
3086 }
3087
Adam Sawickiee082772018-06-20 17:45:49 +02003088 // Test ring buffer.
3089 {
3090 // Allocate number of buffers that surely fit into this block.
3091 bufCreateInfo.size = bufSizeMax;
3092 for(size_t i = 0; i < maxBufCount; ++i)
3093 {
3094 BufferInfo newBufInfo;
3095 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3096 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003097 TEST(res == VK_SUCCESS);
3098 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawickiee082772018-06-20 17:45:49 +02003099 bufInfo.push_back(newBufInfo);
3100 prevOffset = allocInfo.offset;
3101 }
3102
3103 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
3104 const size_t buffersPerIter = maxBufCount / 10 - 1;
3105 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
3106 for(size_t iter = 0; iter < iterCount; ++iter)
3107 {
3108 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
3109 {
3110 const BufferInfo& currBufInfo = bufInfo.front();
3111 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3112 bufInfo.erase(bufInfo.begin());
3113 }
3114 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
3115 {
3116 BufferInfo newBufInfo;
3117 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3118 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003119 TEST(res == VK_SUCCESS);
Adam Sawickiee082772018-06-20 17:45:49 +02003120 bufInfo.push_back(newBufInfo);
3121 }
3122 }
3123
3124 // Allocate buffers until we reach out-of-memory.
3125 uint32_t debugIndex = 0;
3126 while(res == VK_SUCCESS)
3127 {
3128 BufferInfo newBufInfo;
3129 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3130 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3131 if(res == VK_SUCCESS)
3132 {
3133 bufInfo.push_back(newBufInfo);
3134 }
3135 else
3136 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003137 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickiee082772018-06-20 17:45:49 +02003138 }
3139 ++debugIndex;
3140 }
3141
3142 // Destroy the buffers in random order.
3143 while(!bufInfo.empty())
3144 {
3145 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3146 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3147 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3148 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3149 }
3150 }
3151
Adam Sawicki680b2252018-08-22 14:47:32 +02003152 // Test double stack.
3153 {
3154 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
3155 VkDeviceSize prevOffsetLower = 0;
3156 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
3157 for(size_t i = 0; i < maxBufCount; ++i)
3158 {
3159 const bool upperAddress = (i % 2) != 0;
3160 if(upperAddress)
3161 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3162 else
3163 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003164 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003165 BufferInfo newBufInfo;
3166 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3167 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003168 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02003169 if(upperAddress)
3170 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003171 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003172 prevOffsetUpper = allocInfo.offset;
3173 }
3174 else
3175 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003176 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02003177 prevOffsetLower = allocInfo.offset;
3178 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003179 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003180 bufInfo.push_back(newBufInfo);
3181 }
3182
3183 // Destroy few buffers from top of the stack.
3184 for(size_t i = 0; i < maxBufCount / 5; ++i)
3185 {
3186 const BufferInfo& currBufInfo = bufInfo.back();
3187 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3188 bufInfo.pop_back();
3189 }
3190
3191 // Create some more
3192 for(size_t i = 0; i < maxBufCount / 5; ++i)
3193 {
3194 const bool upperAddress = (i % 2) != 0;
3195 if(upperAddress)
3196 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3197 else
3198 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003199 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003200 BufferInfo newBufInfo;
3201 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3202 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003203 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02003204 bufInfo.push_back(newBufInfo);
3205 }
3206
3207 // Destroy the buffers in reverse order.
3208 while(!bufInfo.empty())
3209 {
3210 const BufferInfo& currBufInfo = bufInfo.back();
3211 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3212 bufInfo.pop_back();
3213 }
3214
3215 // Create buffers on both sides until we reach out of memory.
3216 prevOffsetLower = 0;
3217 prevOffsetUpper = poolCreateInfo.blockSize;
3218 res = VK_SUCCESS;
3219 for(size_t i = 0; res == VK_SUCCESS; ++i)
3220 {
3221 const bool upperAddress = (i % 2) != 0;
3222 if(upperAddress)
3223 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3224 else
3225 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003226 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003227 BufferInfo newBufInfo;
3228 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3229 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3230 if(res == VK_SUCCESS)
3231 {
3232 if(upperAddress)
3233 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003234 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003235 prevOffsetUpper = allocInfo.offset;
3236 }
3237 else
3238 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003239 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02003240 prevOffsetLower = allocInfo.offset;
3241 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003242 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003243 bufInfo.push_back(newBufInfo);
3244 }
3245 }
3246
3247 // Destroy the buffers in random order.
3248 while(!bufInfo.empty())
3249 {
3250 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3251 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3252 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3253 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3254 }
3255
3256 // Create buffers on upper side only, constant size, until we reach out of memory.
3257 prevOffsetUpper = poolCreateInfo.blockSize;
3258 res = VK_SUCCESS;
3259 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3260 bufCreateInfo.size = bufSizeMax;
3261 for(size_t i = 0; res == VK_SUCCESS; ++i)
3262 {
3263 BufferInfo newBufInfo;
3264 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3265 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3266 if(res == VK_SUCCESS)
3267 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003268 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003269 prevOffsetUpper = allocInfo.offset;
3270 bufInfo.push_back(newBufInfo);
3271 }
3272 }
3273
3274 // Destroy the buffers in reverse order.
3275 while(!bufInfo.empty())
3276 {
3277 const BufferInfo& currBufInfo = bufInfo.back();
3278 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3279 bufInfo.pop_back();
3280 }
3281 }
3282
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003283 // Test ring buffer with lost allocations.
3284 {
3285 // Allocate number of buffers until pool is full.
3286 // Notice CAN_BECOME_LOST flag and call to vmaSetCurrentFrameIndex.
3287 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT;
3288 res = VK_SUCCESS;
3289 for(size_t i = 0; res == VK_SUCCESS; ++i)
3290 {
3291 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3292
Adam Sawickifd366b62019-01-24 15:26:43 +01003293 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003294
3295 BufferInfo newBufInfo;
3296 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3297 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3298 if(res == VK_SUCCESS)
3299 bufInfo.push_back(newBufInfo);
3300 }
3301
3302 // Free first half of it.
3303 {
3304 const size_t buffersToDelete = bufInfo.size() / 2;
3305 for(size_t i = 0; i < buffersToDelete; ++i)
3306 {
3307 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3308 }
3309 bufInfo.erase(bufInfo.begin(), bufInfo.begin() + buffersToDelete);
3310 }
3311
3312 // Allocate number of buffers until pool is full again.
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003313 // This way we make sure ring buffers wraps around, front in in the middle.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003314 res = VK_SUCCESS;
3315 for(size_t i = 0; res == VK_SUCCESS; ++i)
3316 {
3317 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3318
Adam Sawickifd366b62019-01-24 15:26:43 +01003319 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003320
3321 BufferInfo newBufInfo;
3322 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3323 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3324 if(res == VK_SUCCESS)
3325 bufInfo.push_back(newBufInfo);
3326 }
3327
3328 VkDeviceSize firstNewOffset;
3329 {
3330 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3331
3332 // Allocate a large buffer with CAN_MAKE_OTHER_LOST.
3333 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
3334 bufCreateInfo.size = bufSizeMax;
3335
3336 BufferInfo newBufInfo;
3337 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3338 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003339 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003340 bufInfo.push_back(newBufInfo);
3341 firstNewOffset = allocInfo.offset;
3342
3343 // Make sure at least one buffer from the beginning became lost.
3344 vmaGetAllocationInfo(g_hAllocator, bufInfo[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003345 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003346 }
3347
Adam Sawickifd366b62019-01-24 15:26:43 +01003348#if 0 // TODO Fix and uncomment. Failing on Intel.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003349 // Allocate more buffers that CAN_MAKE_OTHER_LOST until we wrap-around with this.
3350 size_t newCount = 1;
3351 for(;;)
3352 {
3353 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3354
Adam Sawickifd366b62019-01-24 15:26:43 +01003355 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003356
3357 BufferInfo newBufInfo;
3358 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3359 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickifd366b62019-01-24 15:26:43 +01003360
Adam Sawickib8d34d52018-10-03 17:41:20 +02003361 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003362 bufInfo.push_back(newBufInfo);
3363 ++newCount;
3364 if(allocInfo.offset < firstNewOffset)
3365 break;
3366 }
Adam Sawickifd366b62019-01-24 15:26:43 +01003367#endif
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003368
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003369 // Delete buffers that are lost.
3370 for(size_t i = bufInfo.size(); i--; )
3371 {
3372 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
3373 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3374 {
3375 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3376 bufInfo.erase(bufInfo.begin() + i);
3377 }
3378 }
3379
3380 // Test vmaMakePoolAllocationsLost
3381 {
3382 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3383
Adam Sawicki4d35a5d2019-01-24 15:51:59 +01003384 size_t lostAllocCount = 0;
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003385 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostAllocCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003386 TEST(lostAllocCount > 0);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003387
3388 size_t realLostAllocCount = 0;
3389 for(size_t i = 0; i < bufInfo.size(); ++i)
3390 {
3391 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
3392 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3393 ++realLostAllocCount;
3394 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003395 TEST(realLostAllocCount == lostAllocCount);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003396 }
3397
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003398 // Destroy all the buffers in forward order.
3399 for(size_t i = 0; i < bufInfo.size(); ++i)
3400 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3401 bufInfo.clear();
3402 }
3403
Adam Sawicki70a683e2018-08-24 15:36:32 +02003404 vmaDestroyPool(g_hAllocator, pool);
3405}
Adam Sawickif799c4f2018-08-23 10:40:30 +02003406
Adam Sawicki70a683e2018-08-24 15:36:32 +02003407static void TestLinearAllocatorMultiBlock()
3408{
3409 wprintf(L"Test linear allocator multi block\n");
3410
3411 RandomNumberGenerator rand{345673};
3412
3413 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3414 sampleBufCreateInfo.size = 1024 * 1024;
3415 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3416
3417 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3418 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3419
3420 VmaPoolCreateInfo poolCreateInfo = {};
3421 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3422 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003423 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003424
3425 VmaPool pool = nullptr;
3426 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003427 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003428
3429 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
3430
3431 VmaAllocationCreateInfo allocCreateInfo = {};
3432 allocCreateInfo.pool = pool;
3433
3434 std::vector<BufferInfo> bufInfo;
3435 VmaAllocationInfo allocInfo;
3436
3437 // Test one-time free.
3438 {
3439 // Allocate buffers until we move to a second block.
3440 VkDeviceMemory lastMem = VK_NULL_HANDLE;
3441 for(uint32_t i = 0; ; ++i)
3442 {
3443 BufferInfo newBufInfo;
3444 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3445 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003446 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003447 bufInfo.push_back(newBufInfo);
3448 if(lastMem && allocInfo.deviceMemory != lastMem)
3449 {
3450 break;
3451 }
3452 lastMem = allocInfo.deviceMemory;
3453 }
3454
Adam Sawickib8d34d52018-10-03 17:41:20 +02003455 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003456
3457 // Make sure that pool has now two blocks.
3458 VmaPoolStats poolStats = {};
3459 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003460 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003461
3462 // Destroy all the buffers in random order.
3463 while(!bufInfo.empty())
3464 {
3465 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3466 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3467 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3468 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3469 }
3470
3471 // Make sure that pool has now at most one block.
3472 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003473 TEST(poolStats.blockCount <= 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003474 }
3475
3476 // Test stack.
3477 {
3478 // Allocate buffers until we move to a second block.
3479 VkDeviceMemory lastMem = VK_NULL_HANDLE;
3480 for(uint32_t i = 0; ; ++i)
3481 {
3482 BufferInfo newBufInfo;
3483 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3484 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003485 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003486 bufInfo.push_back(newBufInfo);
3487 if(lastMem && allocInfo.deviceMemory != lastMem)
3488 {
3489 break;
3490 }
3491 lastMem = allocInfo.deviceMemory;
3492 }
3493
Adam Sawickib8d34d52018-10-03 17:41:20 +02003494 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003495
3496 // Add few more buffers.
3497 for(uint32_t i = 0; i < 5; ++i)
3498 {
3499 BufferInfo newBufInfo;
3500 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3501 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003502 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003503 bufInfo.push_back(newBufInfo);
3504 }
3505
3506 // Make sure that pool has now two blocks.
3507 VmaPoolStats poolStats = {};
3508 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003509 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003510
3511 // Delete half of buffers, LIFO.
3512 for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
3513 {
3514 const BufferInfo& currBufInfo = bufInfo.back();
3515 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3516 bufInfo.pop_back();
3517 }
3518
3519 // Add one more buffer.
3520 BufferInfo newBufInfo;
3521 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3522 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003523 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003524 bufInfo.push_back(newBufInfo);
3525
3526 // Make sure that pool has now one block.
3527 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003528 TEST(poolStats.blockCount == 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003529
3530 // Delete all the remaining buffers, LIFO.
3531 while(!bufInfo.empty())
3532 {
3533 const BufferInfo& currBufInfo = bufInfo.back();
3534 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3535 bufInfo.pop_back();
3536 }
Adam Sawickif799c4f2018-08-23 10:40:30 +02003537 }
3538
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003539 vmaDestroyPool(g_hAllocator, pool);
3540}
3541
Adam Sawickifd11d752018-08-22 15:02:10 +02003542static void ManuallyTestLinearAllocator()
3543{
3544 VmaStats origStats;
3545 vmaCalculateStats(g_hAllocator, &origStats);
3546
3547 wprintf(L"Manually test linear allocator\n");
3548
3549 RandomNumberGenerator rand{645332};
3550
3551 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3552 sampleBufCreateInfo.size = 1024; // Whatever.
3553 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3554
3555 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3556 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3557
3558 VmaPoolCreateInfo poolCreateInfo = {};
3559 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003560 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003561
3562 poolCreateInfo.blockSize = 10 * 1024;
3563 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3564 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
3565
3566 VmaPool pool = nullptr;
3567 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003568 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003569
3570 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
3571
3572 VmaAllocationCreateInfo allocCreateInfo = {};
3573 allocCreateInfo.pool = pool;
3574
3575 std::vector<BufferInfo> bufInfo;
3576 VmaAllocationInfo allocInfo;
3577 BufferInfo newBufInfo;
3578
3579 // Test double stack.
3580 {
3581 /*
3582 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
3583 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
3584
3585 Totally:
3586 1 block allocated
3587 10240 Vulkan bytes
3588 6 new allocations
3589 2256 bytes in allocations
3590 */
3591
3592 bufCreateInfo.size = 32;
3593 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3594 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003595 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003596 bufInfo.push_back(newBufInfo);
3597
3598 bufCreateInfo.size = 1024;
3599 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3600 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003601 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003602 bufInfo.push_back(newBufInfo);
3603
3604 bufCreateInfo.size = 32;
3605 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3606 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003607 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003608 bufInfo.push_back(newBufInfo);
3609
3610 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3611
3612 bufCreateInfo.size = 128;
3613 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3614 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003615 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003616 bufInfo.push_back(newBufInfo);
3617
3618 bufCreateInfo.size = 1024;
3619 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3620 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003621 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003622 bufInfo.push_back(newBufInfo);
3623
3624 bufCreateInfo.size = 16;
3625 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3626 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003627 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003628 bufInfo.push_back(newBufInfo);
3629
3630 VmaStats currStats;
3631 vmaCalculateStats(g_hAllocator, &currStats);
3632 VmaPoolStats poolStats;
3633 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
3634
3635 char* statsStr = nullptr;
3636 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
3637
3638 // PUT BREAKPOINT HERE TO CHECK.
3639 // Inspect: currStats versus origStats, poolStats, statsStr.
3640 int I = 0;
3641
3642 vmaFreeStatsString(g_hAllocator, statsStr);
3643
3644 // Destroy the buffers in reverse order.
3645 while(!bufInfo.empty())
3646 {
3647 const BufferInfo& currBufInfo = bufInfo.back();
3648 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3649 bufInfo.pop_back();
3650 }
3651 }
3652
3653 vmaDestroyPool(g_hAllocator, pool);
3654}
3655
Adam Sawicki80927152018-09-07 17:27:23 +02003656static void BenchmarkAlgorithmsCase(FILE* file,
3657 uint32_t algorithm,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003658 bool empty,
3659 VmaAllocationCreateFlags allocStrategy,
3660 FREE_ORDER freeOrder)
Adam Sawicki0a607132018-08-24 11:18:41 +02003661{
3662 RandomNumberGenerator rand{16223};
3663
3664 const VkDeviceSize bufSizeMin = 32;
3665 const VkDeviceSize bufSizeMax = 1024;
3666 const size_t maxBufCapacity = 10000;
3667 const uint32_t iterationCount = 10;
3668
3669 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3670 sampleBufCreateInfo.size = bufSizeMax;
3671 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3672
3673 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3674 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3675
3676 VmaPoolCreateInfo poolCreateInfo = {};
3677 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003678 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003679
3680 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
Adam Sawicki80927152018-09-07 17:27:23 +02003681 poolCreateInfo.flags |= algorithm;
Adam Sawicki0a607132018-08-24 11:18:41 +02003682 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
3683
3684 VmaPool pool = nullptr;
3685 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003686 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003687
3688 // Buffer created just to get memory requirements. Never bound to any memory.
3689 VkBuffer dummyBuffer = VK_NULL_HANDLE;
Adam Sawicki1f84f622019-07-02 13:40:01 +02003690 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003691 TEST(res == VK_SUCCESS && dummyBuffer);
Adam Sawicki0a607132018-08-24 11:18:41 +02003692
3693 VkMemoryRequirements memReq = {};
3694 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3695
Adam Sawicki1f84f622019-07-02 13:40:01 +02003696 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawicki0a607132018-08-24 11:18:41 +02003697
3698 VmaAllocationCreateInfo allocCreateInfo = {};
3699 allocCreateInfo.pool = pool;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003700 allocCreateInfo.flags = allocStrategy;
Adam Sawicki0a607132018-08-24 11:18:41 +02003701
3702 VmaAllocation alloc;
3703 std::vector<VmaAllocation> baseAllocations;
3704
3705 if(!empty)
3706 {
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003707 // Make allocations up to 1/3 of pool size.
Adam Sawicki0a607132018-08-24 11:18:41 +02003708 VkDeviceSize totalSize = 0;
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003709 while(totalSize < poolCreateInfo.blockSize / 3)
Adam Sawicki0a607132018-08-24 11:18:41 +02003710 {
Adam Sawicki4d844e22019-01-24 16:21:05 +01003711 // This test intentionally allows sizes that are aligned to 4 or 16 bytes.
3712 // This is theoretically allowed and already uncovered one bug.
Adam Sawicki0a607132018-08-24 11:18:41 +02003713 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
3714 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003715 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003716 baseAllocations.push_back(alloc);
3717 totalSize += memReq.size;
3718 }
3719
3720 // Delete half of them, choose randomly.
3721 size_t allocsToDelete = baseAllocations.size() / 2;
3722 for(size_t i = 0; i < allocsToDelete; ++i)
3723 {
3724 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
3725 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
3726 baseAllocations.erase(baseAllocations.begin() + index);
3727 }
3728 }
3729
3730 // BENCHMARK
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003731 const size_t allocCount = maxBufCapacity / 3;
Adam Sawicki0a607132018-08-24 11:18:41 +02003732 std::vector<VmaAllocation> testAllocations;
3733 testAllocations.reserve(allocCount);
3734 duration allocTotalDuration = duration::zero();
3735 duration freeTotalDuration = duration::zero();
3736 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
3737 {
3738 // Allocations
3739 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
3740 for(size_t i = 0; i < allocCount; ++i)
3741 {
3742 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
3743 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003744 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003745 testAllocations.push_back(alloc);
3746 }
3747 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
3748
3749 // Deallocations
3750 switch(freeOrder)
3751 {
3752 case FREE_ORDER::FORWARD:
3753 // Leave testAllocations unchanged.
3754 break;
3755 case FREE_ORDER::BACKWARD:
3756 std::reverse(testAllocations.begin(), testAllocations.end());
3757 break;
3758 case FREE_ORDER::RANDOM:
3759 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
3760 break;
3761 default: assert(0);
3762 }
3763
3764 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
3765 for(size_t i = 0; i < allocCount; ++i)
3766 vmaFreeMemory(g_hAllocator, testAllocations[i]);
3767 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
3768
3769 testAllocations.clear();
3770 }
3771
3772 // Delete baseAllocations
3773 while(!baseAllocations.empty())
3774 {
3775 vmaFreeMemory(g_hAllocator, baseAllocations.back());
3776 baseAllocations.pop_back();
3777 }
3778
3779 vmaDestroyPool(g_hAllocator, pool);
3780
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003781 const float allocTotalSeconds = ToFloatSeconds(allocTotalDuration);
3782 const float freeTotalSeconds = ToFloatSeconds(freeTotalDuration);
3783
Adam Sawicki80927152018-09-07 17:27:23 +02003784 printf(" Algorithm=%s %s Allocation=%s FreeOrder=%s: allocations %g s, free %g s\n",
3785 AlgorithmToStr(algorithm),
Adam Sawicki0667e332018-08-24 17:26:44 +02003786 empty ? "Empty" : "Not empty",
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003787 GetAllocationStrategyName(allocStrategy),
Adam Sawicki0a607132018-08-24 11:18:41 +02003788 FREE_ORDER_NAMES[(size_t)freeOrder],
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003789 allocTotalSeconds,
3790 freeTotalSeconds);
3791
3792 if(file)
3793 {
3794 std::string currTime;
3795 CurrentTimeToStr(currTime);
3796
Adam Sawicki80927152018-09-07 17:27:23 +02003797 fprintf(file, "%s,%s,%s,%u,%s,%s,%g,%g\n",
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003798 CODE_DESCRIPTION, currTime.c_str(),
Adam Sawicki80927152018-09-07 17:27:23 +02003799 AlgorithmToStr(algorithm),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003800 empty ? 1 : 0,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003801 GetAllocationStrategyName(allocStrategy),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003802 FREE_ORDER_NAMES[(uint32_t)freeOrder],
3803 allocTotalSeconds,
3804 freeTotalSeconds);
3805 }
Adam Sawicki0a607132018-08-24 11:18:41 +02003806}
3807
Adam Sawickie73e9882020-03-20 18:05:42 +01003808static void TestBufferDeviceAddress()
3809{
3810 wprintf(L"Test buffer device address\n");
3811
3812 assert(g_BufferDeviceAddressEnabled);
3813
3814 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3815 bufCreateInfo.size = 0x10000;
3816 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
3817 VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; // !!!
3818
3819 VmaAllocationCreateInfo allocCreateInfo = {};
3820 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3821
3822 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
3823 {
3824 // 1st is placed, 2nd is dedicated.
3825 if(testIndex == 1)
3826 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3827
3828 BufferInfo bufInfo = {};
3829 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3830 &bufInfo.Buffer, &bufInfo.Allocation, nullptr);
3831 TEST(res == VK_SUCCESS);
3832
3833 VkBufferDeviceAddressInfoEXT bufferDeviceAddressInfo = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT };
3834 bufferDeviceAddressInfo.buffer = bufInfo.Buffer;
3835 //assert(g_vkGetBufferDeviceAddressEXT != nullptr);
3836 if(g_vkGetBufferDeviceAddressEXT != nullptr)
3837 {
3838 VkDeviceAddress addr = g_vkGetBufferDeviceAddressEXT(g_hDevice, &bufferDeviceAddressInfo);
3839 TEST(addr != 0);
3840 }
3841
3842 vmaDestroyBuffer(g_hAllocator, bufInfo.Buffer, bufInfo.Allocation);
3843 }
3844}
3845
Adam Sawicki80927152018-09-07 17:27:23 +02003846static void BenchmarkAlgorithms(FILE* file)
Adam Sawicki0a607132018-08-24 11:18:41 +02003847{
Adam Sawicki80927152018-09-07 17:27:23 +02003848 wprintf(L"Benchmark algorithms\n");
Adam Sawicki0a607132018-08-24 11:18:41 +02003849
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003850 if(file)
3851 {
3852 fprintf(file,
3853 "Code,Time,"
Adam Sawicki80927152018-09-07 17:27:23 +02003854 "Algorithm,Empty,Allocation strategy,Free order,"
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003855 "Allocation time (s),Deallocation time (s)\n");
3856 }
3857
Adam Sawicki0a607132018-08-24 11:18:41 +02003858 uint32_t freeOrderCount = 1;
3859 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
3860 freeOrderCount = 3;
3861 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
3862 freeOrderCount = 2;
3863
3864 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003865 const uint32_t allocStrategyCount = GetAllocationStrategyCount();
Adam Sawicki0a607132018-08-24 11:18:41 +02003866
3867 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
3868 {
3869 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
3870 switch(freeOrderIndex)
3871 {
3872 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
3873 case 1: freeOrder = FREE_ORDER::FORWARD; break;
3874 case 2: freeOrder = FREE_ORDER::RANDOM; break;
3875 default: assert(0);
3876 }
3877
3878 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
3879 {
Adam Sawicki80927152018-09-07 17:27:23 +02003880 for(uint32_t algorithmIndex = 0; algorithmIndex < 3; ++algorithmIndex)
Adam Sawicki0a607132018-08-24 11:18:41 +02003881 {
Adam Sawicki80927152018-09-07 17:27:23 +02003882 uint32_t algorithm = 0;
3883 switch(algorithmIndex)
3884 {
3885 case 0:
3886 break;
3887 case 1:
3888 algorithm = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
3889 break;
3890 case 2:
3891 algorithm = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3892 break;
3893 default:
3894 assert(0);
3895 }
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003896
Adam Sawicki80927152018-09-07 17:27:23 +02003897 uint32_t currAllocStrategyCount = algorithm != 0 ? 1 : allocStrategyCount;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003898 for(uint32_t allocStrategyIndex = 0; allocStrategyIndex < currAllocStrategyCount; ++allocStrategyIndex)
3899 {
3900 VmaAllocatorCreateFlags strategy = 0;
Adam Sawicki80927152018-09-07 17:27:23 +02003901 if(currAllocStrategyCount > 1)
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003902 {
3903 switch(allocStrategyIndex)
3904 {
3905 case 0: strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; break;
3906 case 1: strategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT; break;
3907 case 2: strategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT; break;
3908 default: assert(0);
3909 }
3910 }
3911
Adam Sawicki80927152018-09-07 17:27:23 +02003912 BenchmarkAlgorithmsCase(
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003913 file,
Adam Sawicki80927152018-09-07 17:27:23 +02003914 algorithm,
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003915 (emptyIndex == 0), // empty
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003916 strategy,
3917 freeOrder); // freeOrder
3918 }
Adam Sawicki0a607132018-08-24 11:18:41 +02003919 }
3920 }
3921 }
3922}
3923
Adam Sawickib8333fb2018-03-13 16:15:53 +01003924static void TestPool_SameSize()
3925{
3926 const VkDeviceSize BUF_SIZE = 1024 * 1024;
3927 const size_t BUF_COUNT = 100;
3928 VkResult res;
3929
3930 RandomNumberGenerator rand{123};
3931
3932 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3933 bufferInfo.size = BUF_SIZE;
3934 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3935
3936 uint32_t memoryTypeBits = UINT32_MAX;
3937 {
3938 VkBuffer dummyBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +02003939 res = vkCreateBuffer(g_hDevice, &bufferInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003940 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003941
3942 VkMemoryRequirements memReq;
3943 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3944 memoryTypeBits = memReq.memoryTypeBits;
3945
Adam Sawicki1f84f622019-07-02 13:40:01 +02003946 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003947 }
3948
3949 VmaAllocationCreateInfo poolAllocInfo = {};
3950 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3951 uint32_t memTypeIndex;
3952 res = vmaFindMemoryTypeIndex(
3953 g_hAllocator,
3954 memoryTypeBits,
3955 &poolAllocInfo,
3956 &memTypeIndex);
3957
3958 VmaPoolCreateInfo poolCreateInfo = {};
3959 poolCreateInfo.memoryTypeIndex = memTypeIndex;
3960 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
3961 poolCreateInfo.minBlockCount = 1;
3962 poolCreateInfo.maxBlockCount = 4;
3963 poolCreateInfo.frameInUseCount = 0;
3964
3965 VmaPool pool;
3966 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003967 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003968
Adam Sawickia020fb82019-11-02 14:43:06 +01003969 // Test pool name
3970 {
3971 static const char* const POOL_NAME = "Pool name";
3972 vmaSetPoolName(g_hAllocator, pool, POOL_NAME);
3973
3974 const char* fetchedPoolName = nullptr;
3975 vmaGetPoolName(g_hAllocator, pool, &fetchedPoolName);
3976 TEST(strcmp(fetchedPoolName, POOL_NAME) == 0);
3977
Adam Sawickia020fb82019-11-02 14:43:06 +01003978 vmaSetPoolName(g_hAllocator, pool, nullptr);
3979 }
3980
Adam Sawickib8333fb2018-03-13 16:15:53 +01003981 vmaSetCurrentFrameIndex(g_hAllocator, 1);
3982
3983 VmaAllocationCreateInfo allocInfo = {};
3984 allocInfo.pool = pool;
3985 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
3986 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
3987
3988 struct BufItem
3989 {
3990 VkBuffer Buf;
3991 VmaAllocation Alloc;
3992 };
3993 std::vector<BufItem> items;
3994
3995 // Fill entire pool.
3996 for(size_t i = 0; i < BUF_COUNT; ++i)
3997 {
3998 BufItem item;
3999 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004000 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004001 items.push_back(item);
4002 }
4003
4004 // Make sure that another allocation would fail.
4005 {
4006 BufItem item;
4007 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004008 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004009 }
4010
4011 // Validate that no buffer is lost. Also check that they are not mapped.
4012 for(size_t i = 0; i < items.size(); ++i)
4013 {
4014 VmaAllocationInfo allocInfo;
4015 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004016 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
4017 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004018 }
4019
4020 // Free some percent of random items.
4021 {
4022 const size_t PERCENT_TO_FREE = 10;
4023 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
4024 for(size_t i = 0; i < itemsToFree; ++i)
4025 {
4026 size_t index = (size_t)rand.Generate() % items.size();
4027 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
4028 items.erase(items.begin() + index);
4029 }
4030 }
4031
4032 // Randomly allocate and free items.
4033 {
4034 const size_t OPERATION_COUNT = BUF_COUNT;
4035 for(size_t i = 0; i < OPERATION_COUNT; ++i)
4036 {
4037 bool allocate = rand.Generate() % 2 != 0;
4038 if(allocate)
4039 {
4040 if(items.size() < BUF_COUNT)
4041 {
4042 BufItem item;
4043 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004044 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004045 items.push_back(item);
4046 }
4047 }
4048 else // Free
4049 {
4050 if(!items.empty())
4051 {
4052 size_t index = (size_t)rand.Generate() % items.size();
4053 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
4054 items.erase(items.begin() + index);
4055 }
4056 }
4057 }
4058 }
4059
4060 // Allocate up to maximum.
4061 while(items.size() < BUF_COUNT)
4062 {
4063 BufItem item;
4064 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004065 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004066 items.push_back(item);
4067 }
4068
4069 // Validate that no buffer is lost.
4070 for(size_t i = 0; i < items.size(); ++i)
4071 {
4072 VmaAllocationInfo allocInfo;
4073 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004074 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004075 }
4076
4077 // Next frame.
4078 vmaSetCurrentFrameIndex(g_hAllocator, 2);
4079
4080 // Allocate another BUF_COUNT buffers.
4081 for(size_t i = 0; i < BUF_COUNT; ++i)
4082 {
4083 BufItem item;
4084 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004085 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004086 items.push_back(item);
4087 }
4088
4089 // Make sure the first BUF_COUNT is lost. Delete them.
4090 for(size_t i = 0; i < BUF_COUNT; ++i)
4091 {
4092 VmaAllocationInfo allocInfo;
4093 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004094 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004095 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4096 }
4097 items.erase(items.begin(), items.begin() + BUF_COUNT);
4098
4099 // Validate that no buffer is lost.
4100 for(size_t i = 0; i < items.size(); ++i)
4101 {
4102 VmaAllocationInfo allocInfo;
4103 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004104 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004105 }
4106
4107 // Free one item.
4108 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
4109 items.pop_back();
4110
4111 // Validate statistics.
4112 {
4113 VmaPoolStats poolStats = {};
4114 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004115 TEST(poolStats.allocationCount == items.size());
4116 TEST(poolStats.size = BUF_COUNT * BUF_SIZE);
4117 TEST(poolStats.unusedRangeCount == 1);
4118 TEST(poolStats.unusedRangeSizeMax == BUF_SIZE);
4119 TEST(poolStats.unusedSize == BUF_SIZE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004120 }
4121
4122 // Free all remaining items.
4123 for(size_t i = items.size(); i--; )
4124 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4125 items.clear();
4126
4127 // Allocate maximum items again.
4128 for(size_t i = 0; i < BUF_COUNT; ++i)
4129 {
4130 BufItem item;
4131 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004132 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004133 items.push_back(item);
4134 }
4135
4136 // Delete every other item.
4137 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
4138 {
4139 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4140 items.erase(items.begin() + i);
4141 }
4142
4143 // Defragment!
4144 {
4145 std::vector<VmaAllocation> allocationsToDefragment(items.size());
4146 for(size_t i = 0; i < items.size(); ++i)
4147 allocationsToDefragment[i] = items[i].Alloc;
4148
4149 VmaDefragmentationStats defragmentationStats;
4150 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004151 TEST(res == VK_SUCCESS);
4152 TEST(defragmentationStats.deviceMemoryBlocksFreed == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004153 }
4154
4155 // Free all remaining items.
4156 for(size_t i = items.size(); i--; )
4157 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4158 items.clear();
4159
4160 ////////////////////////////////////////////////////////////////////////////////
4161 // Test for vmaMakePoolAllocationsLost
4162
4163 // Allocate 4 buffers on frame 10.
4164 vmaSetCurrentFrameIndex(g_hAllocator, 10);
4165 for(size_t i = 0; i < 4; ++i)
4166 {
4167 BufItem item;
4168 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004169 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004170 items.push_back(item);
4171 }
4172
4173 // Touch first 2 of them on frame 11.
4174 vmaSetCurrentFrameIndex(g_hAllocator, 11);
4175 for(size_t i = 0; i < 2; ++i)
4176 {
4177 VmaAllocationInfo allocInfo;
4178 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
4179 }
4180
4181 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
4182 size_t lostCount = 0xDEADC0DE;
4183 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004184 TEST(lostCount == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004185
4186 // Make another call. Now 0 should be lost.
4187 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004188 TEST(lostCount == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004189
4190 // Make another call, with null count. Should not crash.
4191 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
4192
4193 // END: Free all remaining items.
4194 for(size_t i = items.size(); i--; )
4195 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4196
4197 items.clear();
4198
Adam Sawickid2924172018-06-11 12:48:46 +02004199 ////////////////////////////////////////////////////////////////////////////////
4200 // Test for allocation too large for pool
4201
4202 {
4203 VmaAllocationCreateInfo allocCreateInfo = {};
4204 allocCreateInfo.pool = pool;
4205
4206 VkMemoryRequirements memReq;
4207 memReq.memoryTypeBits = UINT32_MAX;
4208 memReq.alignment = 1;
4209 memReq.size = poolCreateInfo.blockSize + 4;
4210
4211 VmaAllocation alloc = nullptr;
4212 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004213 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
Adam Sawickid2924172018-06-11 12:48:46 +02004214 }
4215
Adam Sawickib8333fb2018-03-13 16:15:53 +01004216 vmaDestroyPool(g_hAllocator, pool);
4217}
4218
Adam Sawickie44c6262018-06-15 14:30:39 +02004219static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
4220{
4221 const uint8_t* pBytes = (const uint8_t*)pMemory;
4222 for(size_t i = 0; i < size; ++i)
4223 {
4224 if(pBytes[i] != pattern)
4225 {
4226 return false;
4227 }
4228 }
4229 return true;
4230}
4231
4232static void TestAllocationsInitialization()
4233{
4234 VkResult res;
4235
4236 const size_t BUF_SIZE = 1024;
4237
4238 // Create pool.
4239
4240 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4241 bufInfo.size = BUF_SIZE;
4242 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4243
4244 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
4245 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
4246
4247 VmaPoolCreateInfo poolCreateInfo = {};
4248 poolCreateInfo.blockSize = BUF_SIZE * 10;
4249 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
4250 poolCreateInfo.maxBlockCount = 1;
4251 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004252 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004253
4254 VmaAllocationCreateInfo bufAllocCreateInfo = {};
4255 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004256 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004257
4258 // Create one persistently mapped buffer to keep memory of this block mapped,
4259 // so that pointer to mapped data will remain (more or less...) valid even
4260 // after destruction of other allocations.
4261
4262 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
4263 VkBuffer firstBuf;
4264 VmaAllocation firstAlloc;
4265 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004266 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004267
4268 // Test buffers.
4269
4270 for(uint32_t i = 0; i < 2; ++i)
4271 {
4272 const bool persistentlyMapped = i == 0;
4273 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
4274 VkBuffer buf;
4275 VmaAllocation alloc;
4276 VmaAllocationInfo allocInfo;
4277 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004278 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004279
4280 void* pMappedData;
4281 if(!persistentlyMapped)
4282 {
4283 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004284 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004285 }
4286 else
4287 {
4288 pMappedData = allocInfo.pMappedData;
4289 }
4290
4291 // Validate initialized content
4292 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004293 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02004294
4295 if(!persistentlyMapped)
4296 {
4297 vmaUnmapMemory(g_hAllocator, alloc);
4298 }
4299
4300 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4301
4302 // Validate freed content
4303 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004304 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02004305 }
4306
4307 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
4308 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
4309}
4310
Adam Sawickib8333fb2018-03-13 16:15:53 +01004311static void TestPool_Benchmark(
4312 PoolTestResult& outResult,
4313 const PoolTestConfig& config)
4314{
Adam Sawickib8d34d52018-10-03 17:41:20 +02004315 TEST(config.ThreadCount > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004316
4317 RandomNumberGenerator mainRand{config.RandSeed};
4318
4319 uint32_t allocationSizeProbabilitySum = std::accumulate(
4320 config.AllocationSizes.begin(),
4321 config.AllocationSizes.end(),
4322 0u,
4323 [](uint32_t sum, const AllocationSize& allocSize) {
4324 return sum + allocSize.Probability;
4325 });
4326
4327 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4328 bufferInfo.size = 256; // Whatever.
4329 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4330
4331 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4332 imageInfo.imageType = VK_IMAGE_TYPE_2D;
4333 imageInfo.extent.width = 256; // Whatever.
4334 imageInfo.extent.height = 256; // Whatever.
4335 imageInfo.extent.depth = 1;
4336 imageInfo.mipLevels = 1;
4337 imageInfo.arrayLayers = 1;
4338 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4339 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
4340 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
4341 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
4342 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4343
4344 uint32_t bufferMemoryTypeBits = UINT32_MAX;
4345 {
4346 VkBuffer dummyBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +02004347 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004348 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004349
4350 VkMemoryRequirements memReq;
4351 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
4352 bufferMemoryTypeBits = memReq.memoryTypeBits;
4353
Adam Sawicki1f84f622019-07-02 13:40:01 +02004354 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004355 }
4356
4357 uint32_t imageMemoryTypeBits = UINT32_MAX;
4358 {
4359 VkImage dummyImage;
Adam Sawicki1f84f622019-07-02 13:40:01 +02004360 VkResult res = vkCreateImage(g_hDevice, &imageInfo, g_Allocs, &dummyImage);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004361 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004362
4363 VkMemoryRequirements memReq;
4364 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
4365 imageMemoryTypeBits = memReq.memoryTypeBits;
4366
Adam Sawicki1f84f622019-07-02 13:40:01 +02004367 vkDestroyImage(g_hDevice, dummyImage, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004368 }
4369
4370 uint32_t memoryTypeBits = 0;
4371 if(config.UsesBuffers() && config.UsesImages())
4372 {
4373 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
4374 if(memoryTypeBits == 0)
4375 {
4376 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
4377 return;
4378 }
4379 }
4380 else if(config.UsesBuffers())
4381 memoryTypeBits = bufferMemoryTypeBits;
4382 else if(config.UsesImages())
4383 memoryTypeBits = imageMemoryTypeBits;
4384 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02004385 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004386
4387 VmaPoolCreateInfo poolCreateInfo = {};
4388 poolCreateInfo.memoryTypeIndex = 0;
4389 poolCreateInfo.minBlockCount = 1;
4390 poolCreateInfo.maxBlockCount = 1;
4391 poolCreateInfo.blockSize = config.PoolSize;
4392 poolCreateInfo.frameInUseCount = 1;
4393
4394 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
4395 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4396 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
4397
4398 VmaPool pool;
4399 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004400 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004401
4402 // Start time measurement - after creating pool and initializing data structures.
4403 time_point timeBeg = std::chrono::high_resolution_clock::now();
4404
4405 ////////////////////////////////////////////////////////////////////////////////
4406 // ThreadProc
4407 auto ThreadProc = [&](
4408 PoolTestThreadResult* outThreadResult,
4409 uint32_t randSeed,
4410 HANDLE frameStartEvent,
4411 HANDLE frameEndEvent) -> void
4412 {
4413 RandomNumberGenerator threadRand{randSeed};
4414
4415 outThreadResult->AllocationTimeMin = duration::max();
4416 outThreadResult->AllocationTimeSum = duration::zero();
4417 outThreadResult->AllocationTimeMax = duration::min();
4418 outThreadResult->DeallocationTimeMin = duration::max();
4419 outThreadResult->DeallocationTimeSum = duration::zero();
4420 outThreadResult->DeallocationTimeMax = duration::min();
4421 outThreadResult->AllocationCount = 0;
4422 outThreadResult->DeallocationCount = 0;
4423 outThreadResult->LostAllocationCount = 0;
4424 outThreadResult->LostAllocationTotalSize = 0;
4425 outThreadResult->FailedAllocationCount = 0;
4426 outThreadResult->FailedAllocationTotalSize = 0;
4427
4428 struct Item
4429 {
4430 VkDeviceSize BufferSize;
4431 VkExtent2D ImageSize;
4432 VkBuffer Buf;
4433 VkImage Image;
4434 VmaAllocation Alloc;
4435
4436 VkDeviceSize CalcSizeBytes() const
4437 {
4438 return BufferSize +
4439 ImageSize.width * ImageSize.height * 4;
4440 }
4441 };
4442 std::vector<Item> unusedItems, usedItems;
4443
4444 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
4445
4446 // Create all items - all unused, not yet allocated.
4447 for(size_t i = 0; i < threadTotalItemCount; ++i)
4448 {
4449 Item item = {};
4450
4451 uint32_t allocSizeIndex = 0;
4452 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
4453 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
4454 r -= config.AllocationSizes[allocSizeIndex++].Probability;
4455
4456 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
4457 if(allocSize.BufferSizeMax > 0)
4458 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004459 TEST(allocSize.BufferSizeMin > 0);
4460 TEST(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004461 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
4462 item.BufferSize = allocSize.BufferSizeMin;
4463 else
4464 {
4465 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
4466 item.BufferSize = item.BufferSize / 16 * 16;
4467 }
4468 }
4469 else
4470 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004471 TEST(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004472 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
4473 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
4474 else
4475 {
4476 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
4477 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
4478 }
4479 }
4480
4481 unusedItems.push_back(item);
4482 }
4483
4484 auto Allocate = [&](Item& item) -> VkResult
4485 {
4486 VmaAllocationCreateInfo allocCreateInfo = {};
4487 allocCreateInfo.pool = pool;
4488 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
4489 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
4490
4491 if(item.BufferSize)
4492 {
4493 bufferInfo.size = item.BufferSize;
4494 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4495 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
4496 }
4497 else
4498 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004499 TEST(item.ImageSize.width && item.ImageSize.height);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004500
4501 imageInfo.extent.width = item.ImageSize.width;
4502 imageInfo.extent.height = item.ImageSize.height;
4503 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4504 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
4505 }
4506 };
4507
4508 ////////////////////////////////////////////////////////////////////////////////
4509 // Frames
4510 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
4511 {
4512 WaitForSingleObject(frameStartEvent, INFINITE);
4513
4514 // Always make some percent of used bufs unused, to choose different used ones.
4515 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
4516 for(size_t i = 0; i < bufsToMakeUnused; ++i)
4517 {
4518 size_t index = threadRand.Generate() % usedItems.size();
4519 unusedItems.push_back(usedItems[index]);
4520 usedItems.erase(usedItems.begin() + index);
4521 }
4522
4523 // Determine which bufs we want to use in this frame.
4524 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
4525 / config.ThreadCount;
Adam Sawickib8d34d52018-10-03 17:41:20 +02004526 TEST(usedBufCount < usedItems.size() + unusedItems.size());
Adam Sawickib8333fb2018-03-13 16:15:53 +01004527 // Move some used to unused.
4528 while(usedBufCount < usedItems.size())
4529 {
4530 size_t index = threadRand.Generate() % usedItems.size();
4531 unusedItems.push_back(usedItems[index]);
4532 usedItems.erase(usedItems.begin() + index);
4533 }
4534 // Move some unused to used.
4535 while(usedBufCount > usedItems.size())
4536 {
4537 size_t index = threadRand.Generate() % unusedItems.size();
4538 usedItems.push_back(unusedItems[index]);
4539 unusedItems.erase(unusedItems.begin() + index);
4540 }
4541
4542 uint32_t touchExistingCount = 0;
4543 uint32_t touchLostCount = 0;
4544 uint32_t createSucceededCount = 0;
4545 uint32_t createFailedCount = 0;
4546
4547 // Touch all used bufs. If not created or lost, allocate.
4548 for(size_t i = 0; i < usedItems.size(); ++i)
4549 {
4550 Item& item = usedItems[i];
4551 // Not yet created.
4552 if(item.Alloc == VK_NULL_HANDLE)
4553 {
4554 res = Allocate(item);
4555 ++outThreadResult->AllocationCount;
4556 if(res != VK_SUCCESS)
4557 {
4558 item.Alloc = VK_NULL_HANDLE;
4559 item.Buf = VK_NULL_HANDLE;
4560 ++outThreadResult->FailedAllocationCount;
4561 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
4562 ++createFailedCount;
4563 }
4564 else
4565 ++createSucceededCount;
4566 }
4567 else
4568 {
4569 // Touch.
4570 VmaAllocationInfo allocInfo;
4571 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
4572 // Lost.
4573 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
4574 {
4575 ++touchLostCount;
4576
4577 // Destroy.
4578 {
4579 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4580 if(item.Buf)
4581 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
4582 else
4583 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
4584 ++outThreadResult->DeallocationCount;
4585 }
4586 item.Alloc = VK_NULL_HANDLE;
4587 item.Buf = VK_NULL_HANDLE;
4588
4589 ++outThreadResult->LostAllocationCount;
4590 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
4591
4592 // Recreate.
4593 res = Allocate(item);
4594 ++outThreadResult->AllocationCount;
4595 // Creation failed.
4596 if(res != VK_SUCCESS)
4597 {
4598 ++outThreadResult->FailedAllocationCount;
4599 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
4600 ++createFailedCount;
4601 }
4602 else
4603 ++createSucceededCount;
4604 }
4605 else
4606 ++touchExistingCount;
4607 }
4608 }
4609
4610 /*
4611 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
4612 randSeed, frameIndex,
4613 touchExistingCount, touchLostCount,
4614 createSucceededCount, createFailedCount);
4615 */
4616
4617 SetEvent(frameEndEvent);
4618 }
4619
4620 // Free all remaining items.
4621 for(size_t i = usedItems.size(); i--; )
4622 {
4623 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4624 if(usedItems[i].Buf)
4625 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
4626 else
4627 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
4628 ++outThreadResult->DeallocationCount;
4629 }
4630 for(size_t i = unusedItems.size(); i--; )
4631 {
4632 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
4633 if(unusedItems[i].Buf)
4634 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
4635 else
4636 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
4637 ++outThreadResult->DeallocationCount;
4638 }
4639 };
4640
4641 // Launch threads.
4642 uint32_t threadRandSeed = mainRand.Generate();
4643 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
4644 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
4645 std::vector<std::thread> bkgThreads;
4646 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
4647 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4648 {
4649 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
4650 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
4651 bkgThreads.emplace_back(std::bind(
4652 ThreadProc,
4653 &threadResults[threadIndex],
4654 threadRandSeed + threadIndex,
4655 frameStartEvents[threadIndex],
4656 frameEndEvents[threadIndex]));
4657 }
4658
4659 // Execute frames.
Adam Sawickib8d34d52018-10-03 17:41:20 +02004660 TEST(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004661 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
4662 {
4663 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
4664 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4665 SetEvent(frameStartEvents[threadIndex]);
4666 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
4667 }
4668
4669 // Wait for threads finished
4670 for(size_t i = 0; i < bkgThreads.size(); ++i)
4671 {
4672 bkgThreads[i].join();
4673 CloseHandle(frameEndEvents[i]);
4674 CloseHandle(frameStartEvents[i]);
4675 }
4676 bkgThreads.clear();
4677
4678 // Finish time measurement - before destroying pool.
4679 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
4680
4681 vmaDestroyPool(g_hAllocator, pool);
4682
4683 outResult.AllocationTimeMin = duration::max();
4684 outResult.AllocationTimeAvg = duration::zero();
4685 outResult.AllocationTimeMax = duration::min();
4686 outResult.DeallocationTimeMin = duration::max();
4687 outResult.DeallocationTimeAvg = duration::zero();
4688 outResult.DeallocationTimeMax = duration::min();
4689 outResult.LostAllocationCount = 0;
4690 outResult.LostAllocationTotalSize = 0;
4691 outResult.FailedAllocationCount = 0;
4692 outResult.FailedAllocationTotalSize = 0;
4693 size_t allocationCount = 0;
4694 size_t deallocationCount = 0;
4695 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4696 {
4697 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
4698 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
4699 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
4700 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
4701 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
4702 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
4703 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
4704 allocationCount += threadResult.AllocationCount;
4705 deallocationCount += threadResult.DeallocationCount;
4706 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
4707 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
4708 outResult.LostAllocationCount += threadResult.LostAllocationCount;
4709 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
4710 }
4711 if(allocationCount)
4712 outResult.AllocationTimeAvg /= allocationCount;
4713 if(deallocationCount)
4714 outResult.DeallocationTimeAvg /= deallocationCount;
4715}
4716
4717static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
4718{
4719 if(ptr1 < ptr2)
4720 return ptr1 + size1 > ptr2;
4721 else if(ptr2 < ptr1)
4722 return ptr2 + size2 > ptr1;
4723 else
4724 return true;
4725}
4726
Adam Sawickiefa88c42019-11-18 16:33:56 +01004727static void TestMemoryUsage()
4728{
4729 wprintf(L"Testing memory usage:\n");
4730
Adam Sawicki69185552019-11-18 17:03:34 +01004731 static const VmaMemoryUsage lastUsage = VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED;
Adam Sawickiefa88c42019-11-18 16:33:56 +01004732 for(uint32_t usage = 0; usage <= lastUsage; ++usage)
4733 {
4734 switch(usage)
4735 {
4736 case VMA_MEMORY_USAGE_UNKNOWN: printf(" VMA_MEMORY_USAGE_UNKNOWN:\n"); break;
4737 case VMA_MEMORY_USAGE_GPU_ONLY: printf(" VMA_MEMORY_USAGE_GPU_ONLY:\n"); break;
4738 case VMA_MEMORY_USAGE_CPU_ONLY: printf(" VMA_MEMORY_USAGE_CPU_ONLY:\n"); break;
4739 case VMA_MEMORY_USAGE_CPU_TO_GPU: printf(" VMA_MEMORY_USAGE_CPU_TO_GPU:\n"); break;
4740 case VMA_MEMORY_USAGE_GPU_TO_CPU: printf(" VMA_MEMORY_USAGE_GPU_TO_CPU:\n"); break;
4741 case VMA_MEMORY_USAGE_CPU_COPY: printf(" VMA_MEMORY_USAGE_CPU_COPY:\n"); break;
Adam Sawicki69185552019-11-18 17:03:34 +01004742 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED: printf(" VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:\n"); break;
Adam Sawickiefa88c42019-11-18 16:33:56 +01004743 default: assert(0);
4744 }
4745
4746 auto printResult = [](const char* testName, VkResult res, uint32_t memoryTypeBits, uint32_t memoryTypeIndex)
4747 {
4748 if(res == VK_SUCCESS)
4749 printf(" %s: memoryTypeBits=0x%X, memoryTypeIndex=%u\n", testName, memoryTypeBits, memoryTypeIndex);
4750 else
4751 printf(" %s: memoryTypeBits=0x%X, FAILED with res=%d\n", testName, memoryTypeBits, (int32_t)res);
4752 };
4753
4754 // 1: Buffer for copy
4755 {
4756 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4757 bufCreateInfo.size = 65536;
4758 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4759
4760 VkBuffer buf = VK_NULL_HANDLE;
4761 VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
4762 TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
4763
4764 VkMemoryRequirements memReq = {};
4765 vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
4766
4767 VmaAllocationCreateInfo allocCreateInfo = {};
4768 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4769 VmaAllocation alloc = VK_NULL_HANDLE;
4770 VmaAllocationInfo allocInfo = {};
4771 res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
4772 if(res == VK_SUCCESS)
4773 {
4774 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4775 res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
4776 TEST(res == VK_SUCCESS);
4777 }
4778 printResult("Buffer TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
4779 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4780 }
4781
4782 // 2: Vertex buffer
4783 {
4784 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4785 bufCreateInfo.size = 65536;
4786 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4787
4788 VkBuffer buf = VK_NULL_HANDLE;
4789 VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
4790 TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
4791
4792 VkMemoryRequirements memReq = {};
4793 vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
4794
4795 VmaAllocationCreateInfo allocCreateInfo = {};
4796 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4797 VmaAllocation alloc = VK_NULL_HANDLE;
4798 VmaAllocationInfo allocInfo = {};
4799 res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
4800 if(res == VK_SUCCESS)
4801 {
4802 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4803 res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
4804 TEST(res == VK_SUCCESS);
4805 }
4806 printResult("Buffer TRANSFER_DST + VERTEX_BUFFER", res, memReq.memoryTypeBits, allocInfo.memoryType);
4807 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4808 }
4809
4810 // 3: Image for copy, OPTIMAL
4811 {
4812 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4813 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4814 imgCreateInfo.extent.width = 256;
4815 imgCreateInfo.extent.height = 256;
4816 imgCreateInfo.extent.depth = 1;
4817 imgCreateInfo.mipLevels = 1;
4818 imgCreateInfo.arrayLayers = 1;
4819 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4820 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4821 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4822 imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
4823 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4824
4825 VkImage img = VK_NULL_HANDLE;
4826 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4827 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4828
4829 VkMemoryRequirements memReq = {};
4830 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4831
4832 VmaAllocationCreateInfo allocCreateInfo = {};
4833 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4834 VmaAllocation alloc = VK_NULL_HANDLE;
4835 VmaAllocationInfo allocInfo = {};
4836 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4837 if(res == VK_SUCCESS)
4838 {
4839 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4840 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4841 TEST(res == VK_SUCCESS);
4842 }
4843 printResult("Image OPTIMAL TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
4844
4845 vmaDestroyImage(g_hAllocator, img, alloc);
4846 }
4847
4848 // 4: Image SAMPLED, OPTIMAL
4849 {
4850 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4851 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4852 imgCreateInfo.extent.width = 256;
4853 imgCreateInfo.extent.height = 256;
4854 imgCreateInfo.extent.depth = 1;
4855 imgCreateInfo.mipLevels = 1;
4856 imgCreateInfo.arrayLayers = 1;
4857 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4858 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4859 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4860 imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
4861 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4862
4863 VkImage img = VK_NULL_HANDLE;
4864 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4865 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4866
4867 VkMemoryRequirements memReq = {};
4868 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4869
4870 VmaAllocationCreateInfo allocCreateInfo = {};
4871 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4872 VmaAllocation alloc = VK_NULL_HANDLE;
4873 VmaAllocationInfo allocInfo = {};
4874 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4875 if(res == VK_SUCCESS)
4876 {
4877 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4878 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4879 TEST(res == VK_SUCCESS);
4880 }
4881 printResult("Image OPTIMAL TRANSFER_DST + SAMPLED", res, memReq.memoryTypeBits, allocInfo.memoryType);
4882 vmaDestroyImage(g_hAllocator, img, alloc);
4883 }
4884
4885 // 5: Image COLOR_ATTACHMENT, OPTIMAL
4886 {
4887 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4888 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4889 imgCreateInfo.extent.width = 256;
4890 imgCreateInfo.extent.height = 256;
4891 imgCreateInfo.extent.depth = 1;
4892 imgCreateInfo.mipLevels = 1;
4893 imgCreateInfo.arrayLayers = 1;
4894 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4895 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4896 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4897 imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
4898 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4899
4900 VkImage img = VK_NULL_HANDLE;
4901 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4902 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4903
4904 VkMemoryRequirements memReq = {};
4905 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4906
4907 VmaAllocationCreateInfo allocCreateInfo = {};
4908 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4909 VmaAllocation alloc = VK_NULL_HANDLE;
4910 VmaAllocationInfo allocInfo = {};
4911 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4912 if(res == VK_SUCCESS)
4913 {
4914 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4915 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4916 TEST(res == VK_SUCCESS);
4917 }
4918 printResult("Image OPTIMAL SAMPLED + COLOR_ATTACHMENT", res, memReq.memoryTypeBits, allocInfo.memoryType);
4919 vmaDestroyImage(g_hAllocator, img, alloc);
4920 }
4921 }
4922}
4923
Adam Sawicki50882502020-02-07 16:51:31 +01004924static uint32_t FindDeviceCoherentMemoryTypeBits()
4925{
4926 VkPhysicalDeviceMemoryProperties memProps;
4927 vkGetPhysicalDeviceMemoryProperties(g_hPhysicalDevice, &memProps);
4928
4929 uint32_t memTypeBits = 0;
4930 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
4931 {
4932 if(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD)
4933 memTypeBits |= 1u << i;
4934 }
4935 return memTypeBits;
4936}
4937
4938static void TestDeviceCoherentMemory()
4939{
4940 if(!VK_AMD_device_coherent_memory_enabled)
4941 return;
4942
4943 uint32_t deviceCoherentMemoryTypeBits = FindDeviceCoherentMemoryTypeBits();
4944 // Extension is enabled, feature is enabled, and the device still doesn't support any such memory type?
4945 // OK then, so it's just fake!
4946 if(deviceCoherentMemoryTypeBits == 0)
4947 return;
4948
4949 wprintf(L"Testing device coherent memory...\n");
4950
4951 // 1. Try to allocate buffer from a memory type that is DEVICE_COHERENT.
4952
4953 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4954 bufCreateInfo.size = 0x10000;
4955 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4956
4957 VmaAllocationCreateInfo allocCreateInfo = {};
4958 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
4959 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD;
4960
4961 AllocInfo alloc = {};
4962 VmaAllocationInfo allocInfo = {};
4963 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &alloc.m_Buffer, &alloc.m_Allocation, &allocInfo);
4964
4965 // Make sure it succeeded and was really created in such memory type.
4966 TEST(res == VK_SUCCESS);
4967 TEST((1u << allocInfo.memoryType) & deviceCoherentMemoryTypeBits);
4968
4969 alloc.Destroy();
4970
4971 // 2. Try to create a pool in such memory type.
4972 {
4973 VmaPoolCreateInfo poolCreateInfo = {};
4974
4975 res = vmaFindMemoryTypeIndex(g_hAllocator, UINT32_MAX, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
4976 TEST(res == VK_SUCCESS);
4977 TEST((1u << poolCreateInfo.memoryTypeIndex) & deviceCoherentMemoryTypeBits);
4978
4979 VmaPool pool = VK_NULL_HANDLE;
4980 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
4981 TEST(res == VK_SUCCESS);
4982
4983 vmaDestroyPool(g_hAllocator, pool);
4984 }
4985
4986 // 3. Try the same with a local allocator created without VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT.
4987
4988 VmaAllocatorCreateInfo allocatorCreateInfo = {};
4989 SetAllocatorCreateInfo(allocatorCreateInfo);
4990 allocatorCreateInfo.flags &= ~VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT;
4991
4992 VmaAllocator localAllocator = VK_NULL_HANDLE;
4993 res = vmaCreateAllocator(&allocatorCreateInfo, &localAllocator);
4994 TEST(res == VK_SUCCESS && localAllocator);
4995
4996 res = vmaCreateBuffer(localAllocator, &bufCreateInfo, &allocCreateInfo, &alloc.m_Buffer, &alloc.m_Allocation, &allocInfo);
4997
4998 // Make sure it failed.
4999 TEST(res != VK_SUCCESS && !alloc.m_Buffer && !alloc.m_Allocation);
5000
5001 // 4. Try to find memory type.
5002 {
5003 uint32_t memTypeIndex = UINT_MAX;
5004 res = vmaFindMemoryTypeIndex(localAllocator, UINT32_MAX, &allocCreateInfo, &memTypeIndex);
5005 TEST(res != VK_SUCCESS);
5006 }
5007
5008 vmaDestroyAllocator(localAllocator);
5009}
5010
Adam Sawicki40ffe982019-10-11 15:56:02 +02005011static void TestBudget()
5012{
5013 wprintf(L"Testing budget...\n");
5014
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005015 static const VkDeviceSize BUF_SIZE = 10ull * 1024 * 1024;
Adam Sawicki353e3672019-11-02 14:12:05 +01005016 static const uint32_t BUF_COUNT = 4;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005017
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005018 const VkPhysicalDeviceMemoryProperties* memProps = {};
5019 vmaGetMemoryProperties(g_hAllocator, &memProps);
5020
Adam Sawicki40ffe982019-10-11 15:56:02 +02005021 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
5022 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005023 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
5024
5025 VmaBudget budgetBeg[VK_MAX_MEMORY_HEAPS] = {};
5026 vmaGetBudget(g_hAllocator, budgetBeg);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005027
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005028 for(uint32_t i = 0; i < memProps->memoryHeapCount; ++i)
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01005029 {
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005030 TEST(budgetBeg[i].budget > 0);
5031 TEST(budgetBeg[i].budget <= memProps->memoryHeaps[i].size);
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01005032 TEST(budgetBeg[i].allocationBytes <= budgetBeg[i].blockBytes);
5033 }
5034
Adam Sawicki40ffe982019-10-11 15:56:02 +02005035 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5036 bufInfo.size = BUF_SIZE;
5037 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5038
5039 VmaAllocationCreateInfo allocCreateInfo = {};
5040 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
5041 if(testIndex == 0)
5042 {
5043 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5044 }
5045
5046 // CREATE BUFFERS
5047 uint32_t heapIndex = 0;
5048 BufferInfo bufInfos[BUF_COUNT] = {};
5049 for(uint32_t bufIndex = 0; bufIndex < BUF_COUNT; ++bufIndex)
5050 {
5051 VmaAllocationInfo allocInfo;
5052 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5053 &bufInfos[bufIndex].Buffer, &bufInfos[bufIndex].Allocation, &allocInfo);
5054 TEST(res == VK_SUCCESS);
5055 if(bufIndex == 0)
5056 {
5057 heapIndex = MemoryTypeToHeap(allocInfo.memoryType);
5058 }
5059 else
5060 {
5061 // All buffers need to fall into the same heap.
5062 TEST(MemoryTypeToHeap(allocInfo.memoryType) == heapIndex);
5063 }
5064 }
5065
Adam Sawicki353e3672019-11-02 14:12:05 +01005066 VmaBudget budgetWithBufs[VK_MAX_MEMORY_HEAPS] = {};
5067 vmaGetBudget(g_hAllocator, budgetWithBufs);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005068
5069 // DESTROY BUFFERS
5070 for(size_t bufIndex = BUF_COUNT; bufIndex--; )
5071 {
5072 vmaDestroyBuffer(g_hAllocator, bufInfos[bufIndex].Buffer, bufInfos[bufIndex].Allocation);
5073 }
5074
Adam Sawicki353e3672019-11-02 14:12:05 +01005075 VmaBudget budgetEnd[VK_MAX_MEMORY_HEAPS] = {};
5076 vmaGetBudget(g_hAllocator, budgetEnd);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005077
5078 // CHECK
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005079 for(uint32_t i = 0; i < memProps->memoryHeapCount; ++i)
Adam Sawicki40ffe982019-10-11 15:56:02 +02005080 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005081 TEST(budgetEnd[i].allocationBytes <= budgetEnd[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005082 if(i == heapIndex)
5083 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005084 TEST(budgetEnd[i].allocationBytes == budgetBeg[i].allocationBytes);
5085 TEST(budgetWithBufs[i].allocationBytes == budgetBeg[i].allocationBytes + BUF_SIZE * BUF_COUNT);
5086 TEST(budgetWithBufs[i].blockBytes >= budgetEnd[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005087 }
5088 else
5089 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005090 TEST(budgetEnd[i].allocationBytes == budgetEnd[i].allocationBytes &&
5091 budgetEnd[i].allocationBytes == budgetWithBufs[i].allocationBytes);
5092 TEST(budgetEnd[i].blockBytes == budgetEnd[i].blockBytes &&
5093 budgetEnd[i].blockBytes == budgetWithBufs[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005094 }
5095 }
5096 }
5097}
5098
Adam Sawickib8333fb2018-03-13 16:15:53 +01005099static void TestMapping()
5100{
5101 wprintf(L"Testing mapping...\n");
5102
5103 VkResult res;
5104 uint32_t memTypeIndex = UINT32_MAX;
5105
5106 enum TEST
5107 {
5108 TEST_NORMAL,
5109 TEST_POOL,
5110 TEST_DEDICATED,
5111 TEST_COUNT
5112 };
5113 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
5114 {
5115 VmaPool pool = nullptr;
5116 if(testIndex == TEST_POOL)
5117 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005118 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005119 VmaPoolCreateInfo poolInfo = {};
5120 poolInfo.memoryTypeIndex = memTypeIndex;
5121 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005122 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005123 }
5124
5125 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5126 bufInfo.size = 0x10000;
5127 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005128
Adam Sawickib8333fb2018-03-13 16:15:53 +01005129 VmaAllocationCreateInfo allocCreateInfo = {};
5130 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
5131 allocCreateInfo.pool = pool;
5132 if(testIndex == TEST_DEDICATED)
5133 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005134
Adam Sawickib8333fb2018-03-13 16:15:53 +01005135 VmaAllocationInfo allocInfo;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005136
Adam Sawickib8333fb2018-03-13 16:15:53 +01005137 // Mapped manually
5138
5139 // Create 2 buffers.
5140 BufferInfo bufferInfos[3];
5141 for(size_t i = 0; i < 2; ++i)
5142 {
5143 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5144 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005145 TEST(res == VK_SUCCESS);
5146 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005147 memTypeIndex = allocInfo.memoryType;
5148 }
Adam Sawicki40ffe982019-10-11 15:56:02 +02005149
Adam Sawickib8333fb2018-03-13 16:15:53 +01005150 // Map buffer 0.
5151 char* data00 = nullptr;
5152 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005153 TEST(res == VK_SUCCESS && data00 != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005154 data00[0xFFFF] = data00[0];
5155
5156 // Map buffer 0 second time.
5157 char* data01 = nullptr;
5158 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005159 TEST(res == VK_SUCCESS && data01 == data00);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005160
5161 // Map buffer 1.
5162 char* data1 = nullptr;
5163 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005164 TEST(res == VK_SUCCESS && data1 != nullptr);
5165 TEST(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
Adam Sawickib8333fb2018-03-13 16:15:53 +01005166 data1[0xFFFF] = data1[0];
5167
5168 // Unmap buffer 0 two times.
5169 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
5170 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
5171 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005172 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005173
5174 // Unmap buffer 1.
5175 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
5176 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005177 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005178
5179 // Create 3rd buffer - persistently mapped.
5180 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
5181 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5182 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005183 TEST(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005184
5185 // Map buffer 2.
5186 char* data2 = nullptr;
5187 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005188 TEST(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005189 data2[0xFFFF] = data2[0];
5190
5191 // Unmap buffer 2.
5192 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
5193 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005194 TEST(allocInfo.pMappedData == data2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005195
5196 // Destroy all buffers.
5197 for(size_t i = 3; i--; )
5198 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
5199
5200 vmaDestroyPool(g_hAllocator, pool);
5201 }
5202}
5203
Adam Sawickidaa6a552019-06-25 15:26:37 +02005204// Test CREATE_MAPPED with required DEVICE_LOCAL. There was a bug with it.
5205static void TestDeviceLocalMapped()
5206{
5207 VkResult res;
5208
5209 for(uint32_t testIndex = 0; testIndex < 3; ++testIndex)
5210 {
5211 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5212 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5213 bufCreateInfo.size = 4096;
5214
5215 VmaPool pool = VK_NULL_HANDLE;
5216 VmaAllocationCreateInfo allocCreateInfo = {};
5217 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
5218 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
5219 if(testIndex == 2)
5220 {
5221 VmaPoolCreateInfo poolCreateInfo = {};
5222 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
5223 TEST(res == VK_SUCCESS);
5224 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
5225 TEST(res == VK_SUCCESS);
5226 allocCreateInfo.pool = pool;
5227 }
5228 else if(testIndex == 1)
5229 {
5230 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
5231 }
5232
5233 VkBuffer buf = VK_NULL_HANDLE;
5234 VmaAllocation alloc = VK_NULL_HANDLE;
5235 VmaAllocationInfo allocInfo = {};
5236 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
5237 TEST(res == VK_SUCCESS && alloc);
5238
5239 VkMemoryPropertyFlags memTypeFlags = 0;
5240 vmaGetMemoryTypeProperties(g_hAllocator, allocInfo.memoryType, &memTypeFlags);
5241 const bool shouldBeMapped = (memTypeFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
5242 TEST((allocInfo.pMappedData != nullptr) == shouldBeMapped);
5243
5244 vmaDestroyBuffer(g_hAllocator, buf, alloc);
5245 vmaDestroyPool(g_hAllocator, pool);
5246 }
5247}
5248
Adam Sawickib8333fb2018-03-13 16:15:53 +01005249static void TestMappingMultithreaded()
5250{
5251 wprintf(L"Testing mapping multithreaded...\n");
5252
5253 static const uint32_t threadCount = 16;
5254 static const uint32_t bufferCount = 1024;
5255 static const uint32_t threadBufferCount = bufferCount / threadCount;
5256
5257 VkResult res;
5258 volatile uint32_t memTypeIndex = UINT32_MAX;
5259
5260 enum TEST
5261 {
5262 TEST_NORMAL,
5263 TEST_POOL,
5264 TEST_DEDICATED,
5265 TEST_COUNT
5266 };
5267 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
5268 {
5269 VmaPool pool = nullptr;
5270 if(testIndex == TEST_POOL)
5271 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005272 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005273 VmaPoolCreateInfo poolInfo = {};
5274 poolInfo.memoryTypeIndex = memTypeIndex;
5275 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005276 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005277 }
5278
5279 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5280 bufCreateInfo.size = 0x10000;
5281 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
5282
5283 VmaAllocationCreateInfo allocCreateInfo = {};
5284 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
5285 allocCreateInfo.pool = pool;
5286 if(testIndex == TEST_DEDICATED)
5287 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5288
5289 std::thread threads[threadCount];
5290 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
5291 {
5292 threads[threadIndex] = std::thread([=, &memTypeIndex](){
5293 // ======== THREAD FUNCTION ========
5294
5295 RandomNumberGenerator rand{threadIndex};
5296
5297 enum class MODE
5298 {
5299 // Don't map this buffer at all.
5300 DONT_MAP,
5301 // Map and quickly unmap.
5302 MAP_FOR_MOMENT,
5303 // Map and unmap before destruction.
5304 MAP_FOR_LONGER,
5305 // Map two times. Quickly unmap, second unmap before destruction.
5306 MAP_TWO_TIMES,
5307 // Create this buffer as persistently mapped.
5308 PERSISTENTLY_MAPPED,
5309 COUNT
5310 };
5311 std::vector<BufferInfo> bufInfos{threadBufferCount};
5312 std::vector<MODE> bufModes{threadBufferCount};
5313
5314 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
5315 {
5316 BufferInfo& bufInfo = bufInfos[bufferIndex];
5317 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
5318 bufModes[bufferIndex] = mode;
5319
5320 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
5321 if(mode == MODE::PERSISTENTLY_MAPPED)
5322 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
5323
5324 VmaAllocationInfo allocInfo;
5325 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
5326 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005327 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005328
5329 if(memTypeIndex == UINT32_MAX)
5330 memTypeIndex = allocInfo.memoryType;
5331
5332 char* data = nullptr;
5333
5334 if(mode == MODE::PERSISTENTLY_MAPPED)
5335 {
5336 data = (char*)allocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02005337 TEST(data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005338 }
5339 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
5340 mode == MODE::MAP_TWO_TIMES)
5341 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005342 TEST(data == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005343 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005344 TEST(res == VK_SUCCESS && data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005345
5346 if(mode == MODE::MAP_TWO_TIMES)
5347 {
5348 char* data2 = nullptr;
5349 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005350 TEST(res == VK_SUCCESS && data2 == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005351 }
5352 }
5353 else if(mode == MODE::DONT_MAP)
5354 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005355 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005356 }
5357 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02005358 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005359
5360 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
5361 if(data)
5362 data[0xFFFF] = data[0];
5363
5364 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
5365 {
5366 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
5367
5368 VmaAllocationInfo allocInfo;
5369 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
5370 if(mode == MODE::MAP_FOR_MOMENT)
Adam Sawickib8d34d52018-10-03 17:41:20 +02005371 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005372 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02005373 TEST(allocInfo.pMappedData == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005374 }
5375
5376 switch(rand.Generate() % 3)
5377 {
5378 case 0: Sleep(0); break; // Yield.
5379 case 1: Sleep(10); break; // 10 ms
5380 // default: No sleep.
5381 }
5382
5383 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
5384 if(data)
5385 data[0xFFFF] = data[0];
5386 }
5387
5388 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
5389 {
5390 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
5391 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
5392 {
5393 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
5394
5395 VmaAllocationInfo allocInfo;
5396 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005397 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005398 }
5399
5400 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
5401 }
5402 });
5403 }
5404
5405 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
5406 threads[threadIndex].join();
5407
5408 vmaDestroyPool(g_hAllocator, pool);
5409 }
5410}
5411
5412static void WriteMainTestResultHeader(FILE* file)
5413{
5414 fprintf(file,
Adam Sawicki740b08f2018-08-27 13:42:07 +02005415 "Code,Time,"
5416 "Threads,Buffers and images,Sizes,Operations,Allocation strategy,Free order,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01005417 "Total Time (us),"
5418 "Allocation Time Min (us),"
5419 "Allocation Time Avg (us),"
5420 "Allocation Time Max (us),"
5421 "Deallocation Time Min (us),"
5422 "Deallocation Time Avg (us),"
5423 "Deallocation Time Max (us),"
5424 "Total Memory Allocated (B),"
5425 "Free Range Size Avg (B),"
5426 "Free Range Size Max (B)\n");
5427}
5428
5429static void WriteMainTestResult(
5430 FILE* file,
5431 const char* codeDescription,
5432 const char* testDescription,
5433 const Config& config, const Result& result)
5434{
5435 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
5436 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
5437 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
5438 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
5439 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
5440 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
5441 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
5442
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005443 std::string currTime;
5444 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005445
5446 fprintf(file,
5447 "%s,%s,%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01005448 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
5449 codeDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005450 currTime.c_str(),
Adam Sawicki740b08f2018-08-27 13:42:07 +02005451 testDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01005452 totalTimeSeconds * 1e6f,
5453 allocationTimeMinSeconds * 1e6f,
5454 allocationTimeAvgSeconds * 1e6f,
5455 allocationTimeMaxSeconds * 1e6f,
5456 deallocationTimeMinSeconds * 1e6f,
5457 deallocationTimeAvgSeconds * 1e6f,
5458 deallocationTimeMaxSeconds * 1e6f,
5459 result.TotalMemoryAllocated,
5460 result.FreeRangeSizeAvg,
5461 result.FreeRangeSizeMax);
5462}
5463
5464static void WritePoolTestResultHeader(FILE* file)
5465{
5466 fprintf(file,
5467 "Code,Test,Time,"
5468 "Config,"
5469 "Total Time (us),"
5470 "Allocation Time Min (us),"
5471 "Allocation Time Avg (us),"
5472 "Allocation Time Max (us),"
5473 "Deallocation Time Min (us),"
5474 "Deallocation Time Avg (us),"
5475 "Deallocation Time Max (us),"
5476 "Lost Allocation Count,"
5477 "Lost Allocation Total Size (B),"
5478 "Failed Allocation Count,"
5479 "Failed Allocation Total Size (B)\n");
5480}
5481
5482static void WritePoolTestResult(
5483 FILE* file,
5484 const char* codeDescription,
5485 const char* testDescription,
5486 const PoolTestConfig& config,
5487 const PoolTestResult& result)
5488{
5489 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
5490 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
5491 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
5492 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
5493 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
5494 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
5495 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
5496
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005497 std::string currTime;
5498 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005499
5500 fprintf(file,
5501 "%s,%s,%s,"
5502 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
5503 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
5504 // General
5505 codeDescription,
5506 testDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005507 currTime.c_str(),
Adam Sawickib8333fb2018-03-13 16:15:53 +01005508 // Config
5509 config.ThreadCount,
5510 (unsigned long long)config.PoolSize,
5511 config.FrameCount,
5512 config.TotalItemCount,
5513 config.UsedItemCountMin,
5514 config.UsedItemCountMax,
5515 config.ItemsToMakeUnusedPercent,
5516 // Results
5517 totalTimeSeconds * 1e6f,
5518 allocationTimeMinSeconds * 1e6f,
5519 allocationTimeAvgSeconds * 1e6f,
5520 allocationTimeMaxSeconds * 1e6f,
5521 deallocationTimeMinSeconds * 1e6f,
5522 deallocationTimeAvgSeconds * 1e6f,
5523 deallocationTimeMaxSeconds * 1e6f,
5524 result.LostAllocationCount,
5525 result.LostAllocationTotalSize,
5526 result.FailedAllocationCount,
5527 result.FailedAllocationTotalSize);
5528}
5529
5530static void PerformCustomMainTest(FILE* file)
5531{
5532 Config config{};
5533 config.RandSeed = 65735476;
5534 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
5535 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
5536 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
5537 config.FreeOrder = FREE_ORDER::FORWARD;
5538 config.ThreadCount = 16;
5539 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02005540 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005541
5542 // Buffers
5543 //config.AllocationSizes.push_back({4, 16, 1024});
5544 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5545
5546 // Images
5547 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5548 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5549
5550 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
5551 config.AdditionalOperationCount = 1024;
5552
5553 Result result{};
5554 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005555 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005556 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
5557}
5558
5559static void PerformCustomPoolTest(FILE* file)
5560{
5561 PoolTestConfig config;
5562 config.PoolSize = 100 * 1024 * 1024;
5563 config.RandSeed = 2345764;
5564 config.ThreadCount = 1;
5565 config.FrameCount = 200;
5566 config.ItemsToMakeUnusedPercent = 2;
5567
5568 AllocationSize allocSize = {};
5569 allocSize.BufferSizeMin = 1024;
5570 allocSize.BufferSizeMax = 1024 * 1024;
5571 allocSize.Probability = 1;
5572 config.AllocationSizes.push_back(allocSize);
5573
5574 allocSize.BufferSizeMin = 0;
5575 allocSize.BufferSizeMax = 0;
5576 allocSize.ImageSizeMin = 128;
5577 allocSize.ImageSizeMax = 1024;
5578 allocSize.Probability = 1;
5579 config.AllocationSizes.push_back(allocSize);
5580
5581 config.PoolSize = config.CalcAvgResourceSize() * 200;
5582 config.UsedItemCountMax = 160;
5583 config.TotalItemCount = config.UsedItemCountMax * 10;
5584 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
5585
5586 g_MemoryAliasingWarningEnabled = false;
5587 PoolTestResult result = {};
5588 TestPool_Benchmark(result, config);
5589 g_MemoryAliasingWarningEnabled = true;
5590
5591 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
5592}
5593
Adam Sawickib8333fb2018-03-13 16:15:53 +01005594static void PerformMainTests(FILE* file)
5595{
5596 uint32_t repeatCount = 1;
5597 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
5598
5599 Config config{};
5600 config.RandSeed = 65735476;
5601 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
5602 config.FreeOrder = FREE_ORDER::FORWARD;
5603
5604 size_t threadCountCount = 1;
5605 switch(ConfigType)
5606 {
5607 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
5608 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
5609 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
5610 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
5611 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
5612 default: assert(0);
5613 }
Adam Sawicki0667e332018-08-24 17:26:44 +02005614
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02005615 const size_t strategyCount = GetAllocationStrategyCount();
Adam Sawicki0667e332018-08-24 17:26:44 +02005616
Adam Sawickib8333fb2018-03-13 16:15:53 +01005617 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
5618 {
5619 std::string desc1;
5620
5621 switch(threadCountIndex)
5622 {
5623 case 0:
5624 desc1 += "1_thread";
5625 config.ThreadCount = 1;
5626 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5627 break;
5628 case 1:
5629 desc1 += "16_threads+0%_common";
5630 config.ThreadCount = 16;
5631 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5632 break;
5633 case 2:
5634 desc1 += "16_threads+50%_common";
5635 config.ThreadCount = 16;
5636 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
5637 break;
5638 case 3:
5639 desc1 += "16_threads+100%_common";
5640 config.ThreadCount = 16;
5641 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
5642 break;
5643 case 4:
5644 desc1 += "2_threads+0%_common";
5645 config.ThreadCount = 2;
5646 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5647 break;
5648 case 5:
5649 desc1 += "2_threads+50%_common";
5650 config.ThreadCount = 2;
5651 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
5652 break;
5653 case 6:
5654 desc1 += "2_threads+100%_common";
5655 config.ThreadCount = 2;
5656 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
5657 break;
5658 default:
5659 assert(0);
5660 }
5661
5662 // 0 = buffers, 1 = images, 2 = buffers and images
5663 size_t buffersVsImagesCount = 2;
5664 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
5665 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
5666 {
5667 std::string desc2 = desc1;
5668 switch(buffersVsImagesIndex)
5669 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005670 case 0: desc2 += ",Buffers"; break;
5671 case 1: desc2 += ",Images"; break;
5672 case 2: desc2 += ",Buffers+Images"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005673 default: assert(0);
5674 }
5675
5676 // 0 = small, 1 = large, 2 = small and large
5677 size_t smallVsLargeCount = 2;
5678 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
5679 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
5680 {
5681 std::string desc3 = desc2;
5682 switch(smallVsLargeIndex)
5683 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005684 case 0: desc3 += ",Small"; break;
5685 case 1: desc3 += ",Large"; break;
5686 case 2: desc3 += ",Small+Large"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005687 default: assert(0);
5688 }
5689
5690 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5691 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
5692 else
5693 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
5694
5695 // 0 = varying sizes min...max, 1 = set of constant sizes
5696 size_t constantSizesCount = 1;
5697 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
5698 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
5699 {
5700 std::string desc4 = desc3;
5701 switch(constantSizesIndex)
5702 {
5703 case 0: desc4 += " Varying_sizes"; break;
5704 case 1: desc4 += " Constant_sizes"; break;
5705 default: assert(0);
5706 }
5707
5708 config.AllocationSizes.clear();
5709 // Buffers present
5710 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
5711 {
5712 // Small
5713 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5714 {
5715 // Varying size
5716 if(constantSizesIndex == 0)
5717 config.AllocationSizes.push_back({4, 16, 1024});
5718 // Constant sizes
5719 else
5720 {
5721 config.AllocationSizes.push_back({1, 16, 16});
5722 config.AllocationSizes.push_back({1, 64, 64});
5723 config.AllocationSizes.push_back({1, 256, 256});
5724 config.AllocationSizes.push_back({1, 1024, 1024});
5725 }
5726 }
5727 // Large
5728 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5729 {
5730 // Varying size
5731 if(constantSizesIndex == 0)
5732 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5733 // Constant sizes
5734 else
5735 {
5736 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
5737 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
5738 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
5739 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
5740 }
5741 }
5742 }
5743 // Images present
5744 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
5745 {
5746 // Small
5747 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5748 {
5749 // Varying size
5750 if(constantSizesIndex == 0)
5751 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5752 // Constant sizes
5753 else
5754 {
5755 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
5756 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
5757 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
5758 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
5759 }
5760 }
5761 // Large
5762 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5763 {
5764 // Varying size
5765 if(constantSizesIndex == 0)
5766 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5767 // Constant sizes
5768 else
5769 {
5770 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
5771 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
5772 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
5773 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
5774 }
5775 }
5776 }
5777
5778 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
5779 size_t beginBytesToAllocateCount = 1;
5780 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
5781 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
5782 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
5783 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
5784 {
5785 std::string desc5 = desc4;
5786
5787 switch(beginBytesToAllocateIndex)
5788 {
5789 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005790 desc5 += ",Allocate_100%";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005791 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
5792 config.AdditionalOperationCount = 0;
5793 break;
5794 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005795 desc5 += ",Allocate_50%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005796 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
5797 config.AdditionalOperationCount = 1024;
5798 break;
5799 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005800 desc5 += ",Allocate_5%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005801 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
5802 config.AdditionalOperationCount = 1024;
5803 break;
5804 case 3:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005805 desc5 += ",Allocate_95%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005806 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
5807 config.AdditionalOperationCount = 1024;
5808 break;
5809 default:
5810 assert(0);
5811 }
5812
Adam Sawicki0667e332018-08-24 17:26:44 +02005813 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01005814 {
Adam Sawicki0667e332018-08-24 17:26:44 +02005815 std::string desc6 = desc5;
5816 switch(strategyIndex)
5817 {
5818 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005819 desc6 += ",BestFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02005820 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
5821 break;
5822 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005823 desc6 += ",WorstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02005824 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
5825 break;
5826 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005827 desc6 += ",FirstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02005828 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
5829 break;
5830 default:
5831 assert(0);
5832 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01005833
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005834 desc6 += ',';
5835 desc6 += FREE_ORDER_NAMES[(uint32_t)config.FreeOrder];
Adam Sawicki740b08f2018-08-27 13:42:07 +02005836
5837 const char* testDescription = desc6.c_str();
Adam Sawicki0667e332018-08-24 17:26:44 +02005838
5839 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
5840 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005841 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawicki0667e332018-08-24 17:26:44 +02005842
5843 Result result{};
5844 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005845 TEST(res == VK_SUCCESS);
Adam Sawicki740b08f2018-08-27 13:42:07 +02005846 if(file)
5847 {
5848 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
5849 }
Adam Sawicki0667e332018-08-24 17:26:44 +02005850 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01005851 }
5852 }
5853 }
5854 }
5855 }
5856 }
5857}
5858
5859static void PerformPoolTests(FILE* file)
5860{
5861 const size_t AVG_RESOURCES_PER_POOL = 300;
5862
5863 uint32_t repeatCount = 1;
5864 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
5865
5866 PoolTestConfig config{};
5867 config.RandSeed = 2346343;
5868 config.FrameCount = 200;
5869 config.ItemsToMakeUnusedPercent = 2;
5870
5871 size_t threadCountCount = 1;
5872 switch(ConfigType)
5873 {
5874 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
5875 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
5876 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
5877 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
5878 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
5879 default: assert(0);
5880 }
5881 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
5882 {
5883 std::string desc1;
5884
5885 switch(threadCountIndex)
5886 {
5887 case 0:
5888 desc1 += "1_thread";
5889 config.ThreadCount = 1;
5890 break;
5891 case 1:
5892 desc1 += "16_threads";
5893 config.ThreadCount = 16;
5894 break;
5895 case 2:
5896 desc1 += "2_threads";
5897 config.ThreadCount = 2;
5898 break;
5899 default:
5900 assert(0);
5901 }
5902
5903 // 0 = buffers, 1 = images, 2 = buffers and images
5904 size_t buffersVsImagesCount = 2;
5905 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
5906 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
5907 {
5908 std::string desc2 = desc1;
5909 switch(buffersVsImagesIndex)
5910 {
5911 case 0: desc2 += " Buffers"; break;
5912 case 1: desc2 += " Images"; break;
5913 case 2: desc2 += " Buffers+Images"; break;
5914 default: assert(0);
5915 }
5916
5917 // 0 = small, 1 = large, 2 = small and large
5918 size_t smallVsLargeCount = 2;
5919 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
5920 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
5921 {
5922 std::string desc3 = desc2;
5923 switch(smallVsLargeIndex)
5924 {
5925 case 0: desc3 += " Small"; break;
5926 case 1: desc3 += " Large"; break;
5927 case 2: desc3 += " Small+Large"; break;
5928 default: assert(0);
5929 }
5930
5931 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5932 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
5933 else
5934 config.PoolSize = 4ull * 1024 * 1024;
5935
5936 // 0 = varying sizes min...max, 1 = set of constant sizes
5937 size_t constantSizesCount = 1;
5938 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
5939 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
5940 {
5941 std::string desc4 = desc3;
5942 switch(constantSizesIndex)
5943 {
5944 case 0: desc4 += " Varying_sizes"; break;
5945 case 1: desc4 += " Constant_sizes"; break;
5946 default: assert(0);
5947 }
5948
5949 config.AllocationSizes.clear();
5950 // Buffers present
5951 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
5952 {
5953 // Small
5954 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5955 {
5956 // Varying size
5957 if(constantSizesIndex == 0)
5958 config.AllocationSizes.push_back({4, 16, 1024});
5959 // Constant sizes
5960 else
5961 {
5962 config.AllocationSizes.push_back({1, 16, 16});
5963 config.AllocationSizes.push_back({1, 64, 64});
5964 config.AllocationSizes.push_back({1, 256, 256});
5965 config.AllocationSizes.push_back({1, 1024, 1024});
5966 }
5967 }
5968 // Large
5969 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5970 {
5971 // Varying size
5972 if(constantSizesIndex == 0)
5973 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5974 // Constant sizes
5975 else
5976 {
5977 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
5978 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
5979 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
5980 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
5981 }
5982 }
5983 }
5984 // Images present
5985 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
5986 {
5987 // Small
5988 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5989 {
5990 // Varying size
5991 if(constantSizesIndex == 0)
5992 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5993 // Constant sizes
5994 else
5995 {
5996 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
5997 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
5998 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
5999 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
6000 }
6001 }
6002 // Large
6003 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
6004 {
6005 // Varying size
6006 if(constantSizesIndex == 0)
6007 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
6008 // Constant sizes
6009 else
6010 {
6011 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
6012 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
6013 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
6014 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
6015 }
6016 }
6017 }
6018
6019 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
6020 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
6021
6022 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
6023 size_t subscriptionModeCount;
6024 switch(ConfigType)
6025 {
6026 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
6027 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
6028 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
6029 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
6030 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
6031 default: assert(0);
6032 }
6033 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
6034 {
6035 std::string desc5 = desc4;
6036
6037 switch(subscriptionModeIndex)
6038 {
6039 case 0:
6040 desc5 += " Subscription_66%";
6041 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
6042 break;
6043 case 1:
6044 desc5 += " Subscription_133%";
6045 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
6046 break;
6047 case 2:
6048 desc5 += " Subscription_100%";
6049 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
6050 break;
6051 case 3:
6052 desc5 += " Subscription_33%";
6053 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
6054 break;
6055 case 4:
6056 desc5 += " Subscription_166%";
6057 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
6058 break;
6059 default:
6060 assert(0);
6061 }
6062
6063 config.TotalItemCount = config.UsedItemCountMax * 5;
6064 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
6065
6066 const char* testDescription = desc5.c_str();
6067
6068 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
6069 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02006070 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawickib8333fb2018-03-13 16:15:53 +01006071
6072 PoolTestResult result{};
6073 g_MemoryAliasingWarningEnabled = false;
6074 TestPool_Benchmark(result, config);
6075 g_MemoryAliasingWarningEnabled = true;
6076 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
6077 }
6078 }
6079 }
6080 }
6081 }
6082 }
6083}
6084
Adam Sawickia83793a2018-09-03 13:40:42 +02006085static void BasicTestBuddyAllocator()
6086{
6087 wprintf(L"Basic test buddy allocator\n");
6088
6089 RandomNumberGenerator rand{76543};
6090
6091 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6092 sampleBufCreateInfo.size = 1024; // Whatever.
6093 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6094
6095 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
6096 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6097
6098 VmaPoolCreateInfo poolCreateInfo = {};
6099 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006100 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006101
Adam Sawickid6e6d6b2018-09-21 14:07:02 +02006102 // Deliberately adding 1023 to test usable size smaller than memory block size.
6103 poolCreateInfo.blockSize = 1024 * 1024 + 1023;
Adam Sawickia83793a2018-09-03 13:40:42 +02006104 poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
Adam Sawicki80927152018-09-07 17:27:23 +02006105 //poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
Adam Sawickia83793a2018-09-03 13:40:42 +02006106
6107 VmaPool pool = nullptr;
6108 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006109 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006110
6111 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
6112
6113 VmaAllocationCreateInfo allocCreateInfo = {};
6114 allocCreateInfo.pool = pool;
6115
6116 std::vector<BufferInfo> bufInfo;
6117 BufferInfo newBufInfo;
6118 VmaAllocationInfo allocInfo;
6119
6120 bufCreateInfo.size = 1024 * 256;
6121 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6122 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006123 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006124 bufInfo.push_back(newBufInfo);
6125
6126 bufCreateInfo.size = 1024 * 512;
6127 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6128 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006129 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006130 bufInfo.push_back(newBufInfo);
6131
6132 bufCreateInfo.size = 1024 * 128;
6133 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6134 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006135 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006136 bufInfo.push_back(newBufInfo);
Adam Sawickia01d4582018-09-21 14:22:35 +02006137
6138 // Test very small allocation, smaller than minimum node size.
6139 bufCreateInfo.size = 1;
6140 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6141 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006142 TEST(res == VK_SUCCESS);
Adam Sawickia01d4582018-09-21 14:22:35 +02006143 bufInfo.push_back(newBufInfo);
Adam Sawickia83793a2018-09-03 13:40:42 +02006144
Adam Sawicki9933c5c2018-09-21 14:57:24 +02006145 // Test some small allocation with alignment requirement.
6146 {
6147 VkMemoryRequirements memReq;
6148 memReq.alignment = 256;
6149 memReq.memoryTypeBits = UINT32_MAX;
6150 memReq.size = 32;
6151
6152 newBufInfo.Buffer = VK_NULL_HANDLE;
6153 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo,
6154 &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006155 TEST(res == VK_SUCCESS);
6156 TEST(allocInfo.offset % memReq.alignment == 0);
Adam Sawicki9933c5c2018-09-21 14:57:24 +02006157 bufInfo.push_back(newBufInfo);
6158 }
6159
6160 //SaveAllocatorStatsToFile(L"TEST.json");
6161
Adam Sawicki21017c62018-09-07 15:26:59 +02006162 VmaPoolStats stats = {};
6163 vmaGetPoolStats(g_hAllocator, pool, &stats);
6164 int DBG = 0; // Set breakpoint here to inspect `stats`.
6165
Adam Sawicki80927152018-09-07 17:27:23 +02006166 // Allocate enough new buffers to surely fall into second block.
6167 for(uint32_t i = 0; i < 32; ++i)
6168 {
6169 bufCreateInfo.size = 1024 * (rand.Generate() % 32 + 1);
6170 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6171 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006172 TEST(res == VK_SUCCESS);
Adam Sawicki80927152018-09-07 17:27:23 +02006173 bufInfo.push_back(newBufInfo);
6174 }
6175
6176 SaveAllocatorStatsToFile(L"BuddyTest01.json");
6177
Adam Sawickia83793a2018-09-03 13:40:42 +02006178 // Destroy the buffers in random order.
6179 while(!bufInfo.empty())
6180 {
6181 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
6182 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
6183 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
6184 bufInfo.erase(bufInfo.begin() + indexToDestroy);
6185 }
6186
6187 vmaDestroyPool(g_hAllocator, pool);
6188}
6189
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006190static void BasicTestAllocatePages()
6191{
6192 wprintf(L"Basic test allocate pages\n");
6193
6194 RandomNumberGenerator rand{765461};
6195
6196 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6197 sampleBufCreateInfo.size = 1024; // Whatever.
6198 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
6199
6200 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
6201 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
6202
6203 VmaPoolCreateInfo poolCreateInfo = {};
6204 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickia7d77692018-10-03 16:15:27 +02006205 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006206
6207 // 1 block of 1 MB.
6208 poolCreateInfo.blockSize = 1024 * 1024;
6209 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
6210
6211 // Create pool.
6212 VmaPool pool = nullptr;
6213 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickia7d77692018-10-03 16:15:27 +02006214 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006215
6216 // Make 100 allocations of 4 KB - they should fit into the pool.
6217 VkMemoryRequirements memReq;
6218 memReq.memoryTypeBits = UINT32_MAX;
6219 memReq.alignment = 4 * 1024;
6220 memReq.size = 4 * 1024;
6221
6222 VmaAllocationCreateInfo allocCreateInfo = {};
6223 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
6224 allocCreateInfo.pool = pool;
6225
6226 constexpr uint32_t allocCount = 100;
6227
6228 std::vector<VmaAllocation> alloc{allocCount};
6229 std::vector<VmaAllocationInfo> allocInfo{allocCount};
6230 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006231 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006232 for(uint32_t i = 0; i < allocCount; ++i)
6233 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006234 TEST(alloc[i] != VK_NULL_HANDLE &&
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006235 allocInfo[i].pMappedData != nullptr &&
6236 allocInfo[i].deviceMemory == allocInfo[0].deviceMemory &&
6237 allocInfo[i].memoryType == allocInfo[0].memoryType);
6238 }
6239
6240 // Free the allocations.
6241 vmaFreeMemoryPages(g_hAllocator, allocCount, alloc.data());
6242 std::fill(alloc.begin(), alloc.end(), nullptr);
6243 std::fill(allocInfo.begin(), allocInfo.end(), VmaAllocationInfo{});
6244
6245 // Try to make 100 allocations of 100 KB. This call should fail due to not enough memory.
6246 // Also test optional allocationInfo = null.
6247 memReq.size = 100 * 1024;
6248 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), nullptr);
Adam Sawickia7d77692018-10-03 16:15:27 +02006249 TEST(res != VK_SUCCESS);
6250 TEST(std::find_if(alloc.begin(), alloc.end(), [](VmaAllocation alloc){ return alloc != VK_NULL_HANDLE; }) == alloc.end());
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006251
6252 // Make 100 allocations of 4 KB, but with required alignment of 128 KB. This should also fail.
6253 memReq.size = 4 * 1024;
6254 memReq.alignment = 128 * 1024;
6255 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006256 TEST(res != VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006257
6258 // Make 100 dedicated allocations of 4 KB.
6259 memReq.alignment = 4 * 1024;
6260 memReq.size = 4 * 1024;
6261
6262 VmaAllocationCreateInfo dedicatedAllocCreateInfo = {};
6263 dedicatedAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
6264 dedicatedAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
6265 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &dedicatedAllocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006266 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006267 for(uint32_t i = 0; i < allocCount; ++i)
6268 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006269 TEST(alloc[i] != VK_NULL_HANDLE &&
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006270 allocInfo[i].pMappedData != nullptr &&
6271 allocInfo[i].memoryType == allocInfo[0].memoryType &&
6272 allocInfo[i].offset == 0);
6273 if(i > 0)
6274 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006275 TEST(allocInfo[i].deviceMemory != allocInfo[0].deviceMemory);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006276 }
6277 }
6278
6279 // Free the allocations.
6280 vmaFreeMemoryPages(g_hAllocator, allocCount, alloc.data());
6281 std::fill(alloc.begin(), alloc.end(), nullptr);
6282 std::fill(allocInfo.begin(), allocInfo.end(), VmaAllocationInfo{});
6283
6284 vmaDestroyPool(g_hAllocator, pool);
6285}
6286
Adam Sawickif2975342018-10-16 13:49:02 +02006287// Test the testing environment.
6288static void TestGpuData()
6289{
6290 RandomNumberGenerator rand = { 53434 };
6291
6292 std::vector<AllocInfo> allocInfo;
6293
6294 for(size_t i = 0; i < 100; ++i)
6295 {
6296 AllocInfo info = {};
6297
6298 info.m_BufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
6299 info.m_BufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT |
6300 VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
6301 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6302 info.m_BufferInfo.size = 1024 * 1024 * (rand.Generate() % 9 + 1);
6303
6304 VmaAllocationCreateInfo allocCreateInfo = {};
6305 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6306
6307 VkResult res = vmaCreateBuffer(g_hAllocator, &info.m_BufferInfo, &allocCreateInfo, &info.m_Buffer, &info.m_Allocation, nullptr);
6308 TEST(res == VK_SUCCESS);
6309
6310 info.m_StartValue = rand.Generate();
6311
6312 allocInfo.push_back(std::move(info));
6313 }
6314
6315 UploadGpuData(allocInfo.data(), allocInfo.size());
6316
6317 ValidateGpuData(allocInfo.data(), allocInfo.size());
6318
6319 DestroyAllAllocations(allocInfo);
6320}
6321
Adam Sawickib8333fb2018-03-13 16:15:53 +01006322void Test()
6323{
6324 wprintf(L"TESTING:\n");
6325
Adam Sawicki48b8a332019-11-02 15:24:33 +01006326 if(false)
Adam Sawicki70a683e2018-08-24 15:36:32 +02006327 {
Adam Sawicki1a8424f2018-12-13 11:01:16 +01006328 ////////////////////////////////////////////////////////////////////////////////
6329 // Temporarily insert custom tests here:
Adam Sawicki70a683e2018-08-24 15:36:32 +02006330 return;
6331 }
6332
Adam Sawickib8333fb2018-03-13 16:15:53 +01006333 // # Simple tests
6334
6335 TestBasics();
Adam Sawickiaaa1a562020-06-24 17:41:09 +02006336 TestAllocationVersusResourceSize();
Adam Sawickif2975342018-10-16 13:49:02 +02006337 //TestGpuData(); // Not calling this because it's just testing the testing environment.
Adam Sawicki212a4a62018-06-14 15:44:45 +02006338#if VMA_DEBUG_MARGIN
6339 TestDebugMargin();
6340#else
6341 TestPool_SameSize();
Adam Sawickiddcbf8c2019-11-22 15:22:42 +01006342 TestPool_MinBlockCount();
Adam Sawicki212a4a62018-06-14 15:44:45 +02006343 TestHeapSizeLimit();
6344#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02006345#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
6346 TestAllocationsInitialization();
6347#endif
Adam Sawickiefa88c42019-11-18 16:33:56 +01006348 TestMemoryUsage();
Adam Sawicki50882502020-02-07 16:51:31 +01006349 TestDeviceCoherentMemory();
Adam Sawicki40ffe982019-10-11 15:56:02 +02006350 TestBudget();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006351 TestMapping();
Adam Sawickidaa6a552019-06-25 15:26:37 +02006352 TestDeviceLocalMapped();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006353 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02006354 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02006355 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02006356 TestLinearAllocatorMultiBlock();
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006357
Adam Sawicki4338f662018-09-07 14:12:37 +02006358 BasicTestBuddyAllocator();
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006359 BasicTestAllocatePages();
Adam Sawicki4338f662018-09-07 14:12:37 +02006360
Adam Sawickie73e9882020-03-20 18:05:42 +01006361 if(g_BufferDeviceAddressEnabled)
6362 TestBufferDeviceAddress();
6363
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006364 {
6365 FILE* file;
Adam Sawickic6432d12018-09-21 16:44:16 +02006366 fopen_s(&file, "Algorithms.csv", "w");
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006367 assert(file != NULL);
Adam Sawicki80927152018-09-07 17:27:23 +02006368 BenchmarkAlgorithms(file);
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006369 fclose(file);
6370 }
6371
Adam Sawickib8333fb2018-03-13 16:15:53 +01006372 TestDefragmentationSimple();
6373 TestDefragmentationFull();
Adam Sawicki52076eb2018-11-22 16:14:50 +01006374 TestDefragmentationWholePool();
Adam Sawicki9a4f5082018-11-23 17:26:05 +01006375 TestDefragmentationGpu();
Adam Sawickia52012d2019-12-23 15:28:51 +01006376 TestDefragmentationIncrementalBasic();
6377 TestDefragmentationIncrementalComplex();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006378
6379 // # Detailed tests
6380 FILE* file;
6381 fopen_s(&file, "Results.csv", "w");
6382 assert(file != NULL);
6383
6384 WriteMainTestResultHeader(file);
6385 PerformMainTests(file);
6386 //PerformCustomMainTest(file);
6387
6388 WritePoolTestResultHeader(file);
6389 PerformPoolTests(file);
6390 //PerformCustomPoolTest(file);
6391
6392 fclose(file);
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01006393
Adam Sawickib8333fb2018-03-13 16:15:53 +01006394 wprintf(L"Done.\n");
6395}
6396
Adam Sawickif1a793c2018-03-13 15:42:22 +01006397#endif // #ifdef _WIN32