blob: 7cccb4dedd4ee0c30bbf559f5f70e9869cd6881f [file] [log] [blame]
Adam Sawickiae5c4662019-01-02 10:23:35 +01001//
Adam Sawickiaa183742021-02-16 17:28:49 +01002// Copyright (c) 2017-2021 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 Sawicki0a3c6b52021-03-02 16:48:32 +010037extern bool VK_KHR_buffer_device_address_enabled;
Adam Sawickif2012052021-01-11 18:04:42 +010038extern bool VK_EXT_memory_priority_enabled;
Adam Sawicki0a3c6b52021-03-02 16:48:32 +010039extern PFN_vkGetBufferDeviceAddressKHR g_vkGetBufferDeviceAddressKHR;
Adam Sawickif2975342018-10-16 13:49:02 +020040void BeginSingleTimeCommands();
41void EndSingleTimeCommands();
Adam Sawicki41b41112021-03-02 15:11:18 +010042void SetDebugUtilsObjectName(VkObjectType type, uint64_t handle, const char* name);
Adam Sawickif2975342018-10-16 13:49:02 +020043
Adam Sawickibdb89a92018-12-13 11:56:30 +010044#ifndef VMA_DEBUG_MARGIN
45 #define VMA_DEBUG_MARGIN 0
46#endif
47
Adam Sawicki0a607132018-08-24 11:18:41 +020048enum CONFIG_TYPE {
49 CONFIG_TYPE_MINIMUM,
50 CONFIG_TYPE_SMALL,
51 CONFIG_TYPE_AVERAGE,
52 CONFIG_TYPE_LARGE,
53 CONFIG_TYPE_MAXIMUM,
54 CONFIG_TYPE_COUNT
55};
56
Adam Sawickif2975342018-10-16 13:49:02 +020057static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
58//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
Adam Sawicki0a607132018-08-24 11:18:41 +020059
Adam Sawickib8333fb2018-03-13 16:15:53 +010060enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
61
Adam Sawicki0667e332018-08-24 17:26:44 +020062static const char* FREE_ORDER_NAMES[] = {
63 "FORWARD",
64 "BACKWARD",
65 "RANDOM",
Adam Sawicki0a607132018-08-24 11:18:41 +020066};
67
Adam Sawicki80927152018-09-07 17:27:23 +020068// Copy of internal VmaAlgorithmToStr.
69static const char* AlgorithmToStr(uint32_t algorithm)
70{
71 switch(algorithm)
72 {
73 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
74 return "Linear";
75 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
76 return "Buddy";
77 case 0:
78 return "Default";
79 default:
80 assert(0);
81 return "";
82 }
83}
84
Adam Sawickib8333fb2018-03-13 16:15:53 +010085struct AllocationSize
86{
87 uint32_t Probability;
88 VkDeviceSize BufferSizeMin, BufferSizeMax;
89 uint32_t ImageSizeMin, ImageSizeMax;
90};
91
92struct Config
93{
94 uint32_t RandSeed;
95 VkDeviceSize BeginBytesToAllocate;
96 uint32_t AdditionalOperationCount;
97 VkDeviceSize MaxBytesToAllocate;
98 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
99 std::vector<AllocationSize> AllocationSizes;
100 uint32_t ThreadCount;
101 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
102 FREE_ORDER FreeOrder;
Adam Sawicki0667e332018-08-24 17:26:44 +0200103 VmaAllocationCreateFlags AllocationStrategy; // For VMA_ALLOCATION_CREATE_STRATEGY_*
Adam Sawickib8333fb2018-03-13 16:15:53 +0100104};
105
106struct Result
107{
108 duration TotalTime;
109 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
110 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
111 VkDeviceSize TotalMemoryAllocated;
112 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
113};
114
115void TestDefragmentationSimple();
116void TestDefragmentationFull();
117
118struct PoolTestConfig
119{
120 uint32_t RandSeed;
121 uint32_t ThreadCount;
122 VkDeviceSize PoolSize;
123 uint32_t FrameCount;
124 uint32_t TotalItemCount;
125 // Range for number of items used in each frame.
126 uint32_t UsedItemCountMin, UsedItemCountMax;
127 // Percent of items to make unused, and possibly make some others used in each frame.
128 uint32_t ItemsToMakeUnusedPercent;
129 std::vector<AllocationSize> AllocationSizes;
130
131 VkDeviceSize CalcAvgResourceSize() const
132 {
133 uint32_t probabilitySum = 0;
134 VkDeviceSize sizeSum = 0;
135 for(size_t i = 0; i < AllocationSizes.size(); ++i)
136 {
137 const AllocationSize& allocSize = AllocationSizes[i];
138 if(allocSize.BufferSizeMax > 0)
139 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
140 else
141 {
142 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
143 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
144 }
145 probabilitySum += allocSize.Probability;
146 }
147 return sizeSum / probabilitySum;
148 }
149
150 bool UsesBuffers() const
151 {
152 for(size_t i = 0; i < AllocationSizes.size(); ++i)
153 if(AllocationSizes[i].BufferSizeMax > 0)
154 return true;
155 return false;
156 }
157
158 bool UsesImages() const
159 {
160 for(size_t i = 0; i < AllocationSizes.size(); ++i)
161 if(AllocationSizes[i].ImageSizeMax > 0)
162 return true;
163 return false;
164 }
165};
166
167struct PoolTestResult
168{
169 duration TotalTime;
170 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
171 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
172 size_t LostAllocationCount, LostAllocationTotalSize;
173 size_t FailedAllocationCount, FailedAllocationTotalSize;
174};
175
176static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
177
Adam Sawicki51fa9662018-10-03 13:44:29 +0200178uint32_t g_FrameIndex = 0;
Adam Sawicki8cfe05f2018-08-22 16:48:17 +0200179
Adam Sawickib8333fb2018-03-13 16:15:53 +0100180struct BufferInfo
181{
182 VkBuffer Buffer = VK_NULL_HANDLE;
183 VmaAllocation Allocation = VK_NULL_HANDLE;
184};
185
Adam Sawicki40ffe982019-10-11 15:56:02 +0200186static uint32_t MemoryTypeToHeap(uint32_t memoryTypeIndex)
187{
188 const VkPhysicalDeviceMemoryProperties* props;
189 vmaGetMemoryProperties(g_hAllocator, &props);
190 return props->memoryTypes[memoryTypeIndex].heapIndex;
191}
192
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +0200193static uint32_t GetAllocationStrategyCount()
194{
195 uint32_t strategyCount = 0;
196 switch(ConfigType)
197 {
198 case CONFIG_TYPE_MINIMUM: strategyCount = 1; break;
199 case CONFIG_TYPE_SMALL: strategyCount = 1; break;
200 case CONFIG_TYPE_AVERAGE: strategyCount = 2; break;
201 case CONFIG_TYPE_LARGE: strategyCount = 2; break;
202 case CONFIG_TYPE_MAXIMUM: strategyCount = 3; break;
203 default: assert(0);
204 }
205 return strategyCount;
206}
207
208static const char* GetAllocationStrategyName(VmaAllocationCreateFlags allocStrategy)
209{
210 switch(allocStrategy)
211 {
212 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT: return "BEST_FIT"; break;
213 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT: return "WORST_FIT"; break;
214 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT: return "FIRST_FIT"; break;
215 case 0: return "Default"; break;
216 default: assert(0); return "";
217 }
218}
219
Adam Sawickib8333fb2018-03-13 16:15:53 +0100220static void InitResult(Result& outResult)
221{
222 outResult.TotalTime = duration::zero();
223 outResult.AllocationTimeMin = duration::max();
224 outResult.AllocationTimeAvg = duration::zero();
225 outResult.AllocationTimeMax = duration::min();
226 outResult.DeallocationTimeMin = duration::max();
227 outResult.DeallocationTimeAvg = duration::zero();
228 outResult.DeallocationTimeMax = duration::min();
229 outResult.TotalMemoryAllocated = 0;
230 outResult.FreeRangeSizeAvg = 0;
231 outResult.FreeRangeSizeMax = 0;
232}
233
234class TimeRegisterObj
235{
236public:
237 TimeRegisterObj(duration& min, duration& sum, duration& max) :
238 m_Min(min),
239 m_Sum(sum),
240 m_Max(max),
241 m_TimeBeg(std::chrono::high_resolution_clock::now())
242 {
243 }
244
245 ~TimeRegisterObj()
246 {
247 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
248 m_Sum += d;
249 if(d < m_Min) m_Min = d;
250 if(d > m_Max) m_Max = d;
251 }
252
253private:
254 duration& m_Min;
255 duration& m_Sum;
256 duration& m_Max;
257 time_point m_TimeBeg;
258};
259
260struct PoolTestThreadResult
261{
262 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
263 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
264 size_t AllocationCount, DeallocationCount;
265 size_t LostAllocationCount, LostAllocationTotalSize;
266 size_t FailedAllocationCount, FailedAllocationTotalSize;
267};
268
269class AllocationTimeRegisterObj : public TimeRegisterObj
270{
271public:
272 AllocationTimeRegisterObj(Result& result) :
273 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
274 {
275 }
276};
277
278class DeallocationTimeRegisterObj : public TimeRegisterObj
279{
280public:
281 DeallocationTimeRegisterObj(Result& result) :
282 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
283 {
284 }
285};
286
287class PoolAllocationTimeRegisterObj : public TimeRegisterObj
288{
289public:
290 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
291 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
292 {
293 }
294};
295
296class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
297{
298public:
299 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
300 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
301 {
302 }
303};
304
Adam Sawicki33d2ce72018-08-27 13:59:13 +0200305static void CurrentTimeToStr(std::string& out)
306{
307 time_t rawTime; time(&rawTime);
308 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
309 char timeStr[128];
310 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
311 out = timeStr;
312}
313
Adam Sawickib8333fb2018-03-13 16:15:53 +0100314VkResult MainTest(Result& outResult, const Config& config)
315{
316 assert(config.ThreadCount > 0);
317
318 InitResult(outResult);
319
320 RandomNumberGenerator mainRand{config.RandSeed};
321
322 time_point timeBeg = std::chrono::high_resolution_clock::now();
323
324 std::atomic<size_t> allocationCount = 0;
325 VkResult res = VK_SUCCESS;
326
327 uint32_t memUsageProbabilitySum =
328 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
329 config.MemUsageProbability[2] + config.MemUsageProbability[3];
330 assert(memUsageProbabilitySum > 0);
331
332 uint32_t allocationSizeProbabilitySum = std::accumulate(
333 config.AllocationSizes.begin(),
334 config.AllocationSizes.end(),
335 0u,
336 [](uint32_t sum, const AllocationSize& allocSize) {
337 return sum + allocSize.Probability;
338 });
339
340 struct Allocation
341 {
342 VkBuffer Buffer;
343 VkImage Image;
344 VmaAllocation Alloc;
345 };
346
347 std::vector<Allocation> commonAllocations;
348 std::mutex commonAllocationsMutex;
349
350 auto Allocate = [&](
351 VkDeviceSize bufferSize,
352 const VkExtent2D imageExtent,
353 RandomNumberGenerator& localRand,
354 VkDeviceSize& totalAllocatedBytes,
355 std::vector<Allocation>& allocations) -> VkResult
356 {
357 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
358
359 uint32_t memUsageIndex = 0;
360 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
361 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
362 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
363
364 VmaAllocationCreateInfo memReq = {};
365 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
Adam Sawicki0667e332018-08-24 17:26:44 +0200366 memReq.flags |= config.AllocationStrategy;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100367
368 Allocation allocation = {};
369 VmaAllocationInfo allocationInfo;
370
371 // Buffer
372 if(bufferSize > 0)
373 {
374 assert(imageExtent.width == 0);
375 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
376 bufferInfo.size = bufferSize;
377 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
378
379 {
380 AllocationTimeRegisterObj timeRegisterObj{outResult};
381 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
382 }
383 }
384 // Image
385 else
386 {
387 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
388 imageInfo.imageType = VK_IMAGE_TYPE_2D;
389 imageInfo.extent.width = imageExtent.width;
390 imageInfo.extent.height = imageExtent.height;
391 imageInfo.extent.depth = 1;
392 imageInfo.mipLevels = 1;
393 imageInfo.arrayLayers = 1;
394 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
395 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
396 VK_IMAGE_TILING_OPTIMAL :
397 VK_IMAGE_TILING_LINEAR;
398 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
399 switch(memReq.usage)
400 {
401 case VMA_MEMORY_USAGE_GPU_ONLY:
402 switch(localRand.Generate() % 3)
403 {
404 case 0:
405 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
406 break;
407 case 1:
408 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
409 break;
410 case 2:
411 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
412 break;
413 }
414 break;
415 case VMA_MEMORY_USAGE_CPU_ONLY:
416 case VMA_MEMORY_USAGE_CPU_TO_GPU:
417 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
418 break;
419 case VMA_MEMORY_USAGE_GPU_TO_CPU:
420 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
421 break;
422 }
423 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
424 imageInfo.flags = 0;
425
426 {
427 AllocationTimeRegisterObj timeRegisterObj{outResult};
428 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
429 }
430 }
431
432 if(res == VK_SUCCESS)
433 {
434 ++allocationCount;
435 totalAllocatedBytes += allocationInfo.size;
436 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
437 if(useCommonAllocations)
438 {
439 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
440 commonAllocations.push_back(allocation);
441 }
442 else
443 allocations.push_back(allocation);
444 }
445 else
446 {
Adam Sawickib8d34d52018-10-03 17:41:20 +0200447 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100448 }
449 return res;
450 };
451
452 auto GetNextAllocationSize = [&](
453 VkDeviceSize& outBufSize,
454 VkExtent2D& outImageSize,
455 RandomNumberGenerator& localRand)
456 {
457 outBufSize = 0;
458 outImageSize = {0, 0};
459
460 uint32_t allocSizeIndex = 0;
461 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
462 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
463 r -= config.AllocationSizes[allocSizeIndex++].Probability;
464
465 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
466 if(allocSize.BufferSizeMax > 0)
467 {
468 assert(allocSize.ImageSizeMax == 0);
469 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
470 outBufSize = allocSize.BufferSizeMin;
471 else
472 {
473 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
474 outBufSize = outBufSize / 16 * 16;
475 }
476 }
477 else
478 {
479 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
480 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
481 else
482 {
483 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
484 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
485 }
486 }
487 };
488
489 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
490 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
491
492 auto ThreadProc = [&](uint32_t randSeed) -> void
493 {
494 RandomNumberGenerator threadRand(randSeed);
495 VkDeviceSize threadTotalAllocatedBytes = 0;
496 std::vector<Allocation> threadAllocations;
497 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
498 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
499 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
500
501 // BEGIN ALLOCATIONS
502 for(;;)
503 {
504 VkDeviceSize bufferSize = 0;
505 VkExtent2D imageExtent = {};
506 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
507 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
508 threadBeginBytesToAllocate)
509 {
510 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
511 break;
512 }
513 else
514 break;
515 }
516
517 // ADDITIONAL ALLOCATIONS AND FREES
518 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
519 {
520 VkDeviceSize bufferSize = 0;
521 VkExtent2D imageExtent = {};
522 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
523
524 // true = allocate, false = free
525 bool allocate = threadRand.Generate() % 2 != 0;
526
527 if(allocate)
528 {
529 if(threadTotalAllocatedBytes +
530 bufferSize +
531 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
532 threadMaxBytesToAllocate)
533 {
534 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
535 break;
536 }
537 }
538 else
539 {
540 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
541 if(useCommonAllocations)
542 {
543 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
544 if(!commonAllocations.empty())
545 {
546 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
547 VmaAllocationInfo allocationInfo;
548 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
549 if(threadTotalAllocatedBytes >= allocationInfo.size)
550 {
551 DeallocationTimeRegisterObj timeRegisterObj{outResult};
552 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
553 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
554 else
555 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
556 threadTotalAllocatedBytes -= allocationInfo.size;
557 commonAllocations.erase(commonAllocations.begin() + indexToFree);
558 }
559 }
560 }
561 else
562 {
563 if(!threadAllocations.empty())
564 {
565 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
566 VmaAllocationInfo allocationInfo;
567 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
568 if(threadTotalAllocatedBytes >= allocationInfo.size)
569 {
570 DeallocationTimeRegisterObj timeRegisterObj{outResult};
571 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
572 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
573 else
574 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
575 threadTotalAllocatedBytes -= allocationInfo.size;
576 threadAllocations.erase(threadAllocations.begin() + indexToFree);
577 }
578 }
579 }
580 }
581 }
582
583 ++numThreadsReachedMaxAllocations;
584
585 WaitForSingleObject(threadsFinishEvent, INFINITE);
586
587 // DEALLOCATION
588 while(!threadAllocations.empty())
589 {
590 size_t indexToFree = 0;
591 switch(config.FreeOrder)
592 {
593 case FREE_ORDER::FORWARD:
594 indexToFree = 0;
595 break;
596 case FREE_ORDER::BACKWARD:
597 indexToFree = threadAllocations.size() - 1;
598 break;
599 case FREE_ORDER::RANDOM:
600 indexToFree = mainRand.Generate() % threadAllocations.size();
601 break;
602 }
603
604 {
605 DeallocationTimeRegisterObj timeRegisterObj{outResult};
606 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
607 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
608 else
609 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
610 }
611 threadAllocations.erase(threadAllocations.begin() + indexToFree);
612 }
613 };
614
615 uint32_t threadRandSeed = mainRand.Generate();
616 std::vector<std::thread> bkgThreads;
617 for(size_t i = 0; i < config.ThreadCount; ++i)
618 {
619 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
620 }
621
622 // Wait for threads reached max allocations
623 while(numThreadsReachedMaxAllocations < config.ThreadCount)
624 Sleep(0);
625
626 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
627 VmaStats vmaStats = {};
628 vmaCalculateStats(g_hAllocator, &vmaStats);
629 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
630 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
631 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
632
633 // Signal threads to deallocate
634 SetEvent(threadsFinishEvent);
635
636 // Wait for threads finished
637 for(size_t i = 0; i < bkgThreads.size(); ++i)
638 bkgThreads[i].join();
639 bkgThreads.clear();
640
641 CloseHandle(threadsFinishEvent);
642
643 // Deallocate remaining common resources
644 while(!commonAllocations.empty())
645 {
646 size_t indexToFree = 0;
647 switch(config.FreeOrder)
648 {
649 case FREE_ORDER::FORWARD:
650 indexToFree = 0;
651 break;
652 case FREE_ORDER::BACKWARD:
653 indexToFree = commonAllocations.size() - 1;
654 break;
655 case FREE_ORDER::RANDOM:
656 indexToFree = mainRand.Generate() % commonAllocations.size();
657 break;
658 }
659
660 {
661 DeallocationTimeRegisterObj timeRegisterObj{outResult};
662 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
663 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
664 else
665 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
666 }
667 commonAllocations.erase(commonAllocations.begin() + indexToFree);
668 }
669
670 if(allocationCount)
671 {
672 outResult.AllocationTimeAvg /= allocationCount;
673 outResult.DeallocationTimeAvg /= allocationCount;
674 }
675
676 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
677
678 return res;
679}
680
Adam Sawicki51fa9662018-10-03 13:44:29 +0200681void SaveAllocatorStatsToFile(const wchar_t* filePath)
Adam Sawickib8333fb2018-03-13 16:15:53 +0100682{
Adam Sawicki4d844e22019-01-24 16:21:05 +0100683 wprintf(L"Saving JSON dump to file \"%s\"\n", filePath);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100684 char* stats;
Adam Sawickie44c6262018-06-15 14:30:39 +0200685 vmaBuildStatsString(g_hAllocator, &stats, VK_TRUE);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100686 SaveFile(filePath, stats, strlen(stats));
Adam Sawickie44c6262018-06-15 14:30:39 +0200687 vmaFreeStatsString(g_hAllocator, stats);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100688}
689
690struct AllocInfo
691{
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200692 VmaAllocation m_Allocation = VK_NULL_HANDLE;
693 VkBuffer m_Buffer = VK_NULL_HANDLE;
694 VkImage m_Image = VK_NULL_HANDLE;
Adam Sawickia52012d2019-12-23 15:28:51 +0100695 VkImageLayout m_ImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200696 uint32_t m_StartValue = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100697 union
698 {
699 VkBufferCreateInfo m_BufferInfo;
700 VkImageCreateInfo m_ImageInfo;
701 };
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200702
Adam Sawickic467e282019-12-23 16:38:31 +0100703 // After defragmentation.
704 VkBuffer m_NewBuffer = VK_NULL_HANDLE;
705 VkImage m_NewImage = VK_NULL_HANDLE;
706
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200707 void CreateBuffer(
708 const VkBufferCreateInfo& bufCreateInfo,
709 const VmaAllocationCreateInfo& allocCreateInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +0100710 void CreateImage(
711 const VkImageCreateInfo& imageCreateInfo,
712 const VmaAllocationCreateInfo& allocCreateInfo,
713 VkImageLayout layout);
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200714 void Destroy();
Adam Sawickib8333fb2018-03-13 16:15:53 +0100715};
716
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200717void AllocInfo::CreateBuffer(
718 const VkBufferCreateInfo& bufCreateInfo,
719 const VmaAllocationCreateInfo& allocCreateInfo)
720{
721 m_BufferInfo = bufCreateInfo;
722 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &m_Buffer, &m_Allocation, nullptr);
723 TEST(res == VK_SUCCESS);
724}
Adam Sawickia52012d2019-12-23 15:28:51 +0100725void AllocInfo::CreateImage(
726 const VkImageCreateInfo& imageCreateInfo,
727 const VmaAllocationCreateInfo& allocCreateInfo,
728 VkImageLayout layout)
729{
730 m_ImageInfo = imageCreateInfo;
731 m_ImageLayout = layout;
732 VkResult res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &m_Image, &m_Allocation, nullptr);
733 TEST(res == VK_SUCCESS);
734}
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200735
736void AllocInfo::Destroy()
737{
738 if(m_Image)
739 {
Adam Sawickic467e282019-12-23 16:38:31 +0100740 assert(!m_Buffer);
Adam Sawicki1f84f622019-07-02 13:40:01 +0200741 vkDestroyImage(g_hDevice, m_Image, g_Allocs);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100742 m_Image = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200743 }
744 if(m_Buffer)
745 {
Adam Sawickic467e282019-12-23 16:38:31 +0100746 assert(!m_Image);
Adam Sawicki1f84f622019-07-02 13:40:01 +0200747 vkDestroyBuffer(g_hDevice, m_Buffer, g_Allocs);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100748 m_Buffer = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200749 }
750 if(m_Allocation)
751 {
752 vmaFreeMemory(g_hAllocator, m_Allocation);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100753 m_Allocation = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200754 }
755}
756
Adam Sawickif2975342018-10-16 13:49:02 +0200757class StagingBufferCollection
758{
759public:
760 StagingBufferCollection() { }
761 ~StagingBufferCollection();
762 // Returns false if maximum total size of buffers would be exceeded.
763 bool AcquireBuffer(VkDeviceSize size, VkBuffer& outBuffer, void*& outMappedPtr);
764 void ReleaseAllBuffers();
765
766private:
767 static const VkDeviceSize MAX_TOTAL_SIZE = 256ull * 1024 * 1024;
768 struct BufInfo
769 {
770 VmaAllocation Allocation = VK_NULL_HANDLE;
771 VkBuffer Buffer = VK_NULL_HANDLE;
772 VkDeviceSize Size = VK_WHOLE_SIZE;
773 void* MappedPtr = nullptr;
774 bool Used = false;
775 };
776 std::vector<BufInfo> m_Bufs;
777 // Including both used and unused.
778 VkDeviceSize m_TotalSize = 0;
779};
780
781StagingBufferCollection::~StagingBufferCollection()
782{
783 for(size_t i = m_Bufs.size(); i--; )
784 {
785 vmaDestroyBuffer(g_hAllocator, m_Bufs[i].Buffer, m_Bufs[i].Allocation);
786 }
787}
788
789bool StagingBufferCollection::AcquireBuffer(VkDeviceSize size, VkBuffer& outBuffer, void*& outMappedPtr)
790{
791 assert(size <= MAX_TOTAL_SIZE);
792
793 // Try to find existing unused buffer with best size.
794 size_t bestIndex = SIZE_MAX;
795 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
796 {
797 BufInfo& currBufInfo = m_Bufs[i];
798 if(!currBufInfo.Used && currBufInfo.Size >= size &&
799 (bestIndex == SIZE_MAX || currBufInfo.Size < m_Bufs[bestIndex].Size))
800 {
801 bestIndex = i;
802 }
803 }
804
805 if(bestIndex != SIZE_MAX)
806 {
807 m_Bufs[bestIndex].Used = true;
808 outBuffer = m_Bufs[bestIndex].Buffer;
809 outMappedPtr = m_Bufs[bestIndex].MappedPtr;
810 return true;
811 }
812
813 // Allocate new buffer with requested size.
814 if(m_TotalSize + size <= MAX_TOTAL_SIZE)
815 {
816 BufInfo bufInfo;
817 bufInfo.Size = size;
818 bufInfo.Used = true;
819
820 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
821 bufCreateInfo.size = size;
822 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
823
824 VmaAllocationCreateInfo allocCreateInfo = {};
825 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
826 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
827
828 VmaAllocationInfo allocInfo;
829 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
830 bufInfo.MappedPtr = allocInfo.pMappedData;
831 TEST(res == VK_SUCCESS && bufInfo.MappedPtr);
832
833 outBuffer = bufInfo.Buffer;
834 outMappedPtr = bufInfo.MappedPtr;
835
836 m_Bufs.push_back(std::move(bufInfo));
837
838 m_TotalSize += size;
839
840 return true;
841 }
842
843 // There are some unused but smaller buffers: Free them and try again.
844 bool hasUnused = false;
845 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
846 {
847 if(!m_Bufs[i].Used)
848 {
849 hasUnused = true;
850 break;
851 }
852 }
853 if(hasUnused)
854 {
855 for(size_t i = m_Bufs.size(); i--; )
856 {
857 if(!m_Bufs[i].Used)
858 {
859 m_TotalSize -= m_Bufs[i].Size;
860 vmaDestroyBuffer(g_hAllocator, m_Bufs[i].Buffer, m_Bufs[i].Allocation);
861 m_Bufs.erase(m_Bufs.begin() + i);
862 }
863 }
864
865 return AcquireBuffer(size, outBuffer, outMappedPtr);
866 }
867
868 return false;
869}
870
871void StagingBufferCollection::ReleaseAllBuffers()
872{
873 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
874 {
875 m_Bufs[i].Used = false;
876 }
877}
878
879static void UploadGpuData(const AllocInfo* allocInfo, size_t allocInfoCount)
880{
881 StagingBufferCollection stagingBufs;
882
883 bool cmdBufferStarted = false;
884 for(size_t allocInfoIndex = 0; allocInfoIndex < allocInfoCount; ++allocInfoIndex)
885 {
886 const AllocInfo& currAllocInfo = allocInfo[allocInfoIndex];
887 if(currAllocInfo.m_Buffer)
888 {
889 const VkDeviceSize size = currAllocInfo.m_BufferInfo.size;
890
891 VkBuffer stagingBuf = VK_NULL_HANDLE;
892 void* stagingBufMappedPtr = nullptr;
893 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
894 {
895 TEST(cmdBufferStarted);
896 EndSingleTimeCommands();
897 stagingBufs.ReleaseAllBuffers();
898 cmdBufferStarted = false;
899
900 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
901 TEST(ok);
902 }
903
904 // Fill staging buffer.
905 {
906 assert(size % sizeof(uint32_t) == 0);
907 uint32_t* stagingValPtr = (uint32_t*)stagingBufMappedPtr;
908 uint32_t val = currAllocInfo.m_StartValue;
909 for(size_t i = 0; i < size / sizeof(uint32_t); ++i)
910 {
911 *stagingValPtr = val;
912 ++stagingValPtr;
913 ++val;
914 }
915 }
916
917 // Issue copy command from staging buffer to destination buffer.
918 if(!cmdBufferStarted)
919 {
920 cmdBufferStarted = true;
921 BeginSingleTimeCommands();
922 }
923
924 VkBufferCopy copy = {};
925 copy.srcOffset = 0;
926 copy.dstOffset = 0;
927 copy.size = size;
928 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingBuf, currAllocInfo.m_Buffer, 1, &copy);
929 }
930 else
931 {
Adam Sawickia52012d2019-12-23 15:28:51 +0100932 TEST(currAllocInfo.m_ImageInfo.format == VK_FORMAT_R8G8B8A8_UNORM && "Only RGBA8 images are currently supported.");
933 TEST(currAllocInfo.m_ImageInfo.mipLevels == 1 && "Only single mip images are currently supported.");
934
Adam Sawickic467e282019-12-23 16:38:31 +0100935 const VkDeviceSize size = (VkDeviceSize)currAllocInfo.m_ImageInfo.extent.width * currAllocInfo.m_ImageInfo.extent.height * sizeof(uint32_t);
Adam Sawickia52012d2019-12-23 15:28:51 +0100936
937 VkBuffer stagingBuf = VK_NULL_HANDLE;
938 void* stagingBufMappedPtr = nullptr;
939 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
940 {
941 TEST(cmdBufferStarted);
942 EndSingleTimeCommands();
943 stagingBufs.ReleaseAllBuffers();
944 cmdBufferStarted = false;
945
946 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
947 TEST(ok);
948 }
949
950 // Fill staging buffer.
951 {
952 assert(size % sizeof(uint32_t) == 0);
953 uint32_t *stagingValPtr = (uint32_t *)stagingBufMappedPtr;
954 uint32_t val = currAllocInfo.m_StartValue;
955 for(size_t i = 0; i < size / sizeof(uint32_t); ++i)
956 {
957 *stagingValPtr = val;
958 ++stagingValPtr;
959 ++val;
960 }
961 }
962
963 // Issue copy command from staging buffer to destination buffer.
964 if(!cmdBufferStarted)
965 {
966 cmdBufferStarted = true;
967 BeginSingleTimeCommands();
968 }
969
970
971 // Transfer to transfer dst layout
972 VkImageSubresourceRange subresourceRange = {
973 VK_IMAGE_ASPECT_COLOR_BIT,
974 0, VK_REMAINING_MIP_LEVELS,
975 0, VK_REMAINING_ARRAY_LAYERS
976 };
977
978 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
979 barrier.srcAccessMask = 0;
980 barrier.dstAccessMask = 0;
981 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
982 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
983 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
984 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
985 barrier.image = currAllocInfo.m_Image;
986 barrier.subresourceRange = subresourceRange;
987
988 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
989 0, nullptr,
990 0, nullptr,
991 1, &barrier);
992
993 // Copy image date
994 VkBufferImageCopy copy = {};
995 copy.bufferOffset = 0;
996 copy.bufferRowLength = 0;
997 copy.bufferImageHeight = 0;
998 copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
999 copy.imageSubresource.layerCount = 1;
1000 copy.imageExtent = currAllocInfo.m_ImageInfo.extent;
1001
1002 vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, stagingBuf, currAllocInfo.m_Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
1003
1004 // Transfer to desired layout
1005 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1006 barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1007 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1008 barrier.newLayout = currAllocInfo.m_ImageLayout;
1009
1010 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
1011 0, nullptr,
1012 0, nullptr,
1013 1, &barrier);
Adam Sawickif2975342018-10-16 13:49:02 +02001014 }
1015 }
1016
1017 if(cmdBufferStarted)
1018 {
1019 EndSingleTimeCommands();
1020 stagingBufs.ReleaseAllBuffers();
1021 }
1022}
1023
1024static void ValidateGpuData(const AllocInfo* allocInfo, size_t allocInfoCount)
1025{
1026 StagingBufferCollection stagingBufs;
1027
1028 bool cmdBufferStarted = false;
1029 size_t validateAllocIndexOffset = 0;
1030 std::vector<void*> validateStagingBuffers;
1031 for(size_t allocInfoIndex = 0; allocInfoIndex < allocInfoCount; ++allocInfoIndex)
1032 {
1033 const AllocInfo& currAllocInfo = allocInfo[allocInfoIndex];
1034 if(currAllocInfo.m_Buffer)
1035 {
1036 const VkDeviceSize size = currAllocInfo.m_BufferInfo.size;
1037
1038 VkBuffer stagingBuf = VK_NULL_HANDLE;
1039 void* stagingBufMappedPtr = nullptr;
1040 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
1041 {
1042 TEST(cmdBufferStarted);
1043 EndSingleTimeCommands();
1044 cmdBufferStarted = false;
1045
1046 for(size_t validateIndex = 0;
1047 validateIndex < validateStagingBuffers.size();
1048 ++validateIndex)
1049 {
1050 const size_t validateAllocIndex = validateIndex + validateAllocIndexOffset;
1051 const VkDeviceSize validateSize = allocInfo[validateAllocIndex].m_BufferInfo.size;
1052 TEST(validateSize % sizeof(uint32_t) == 0);
1053 const uint32_t* stagingValPtr = (const uint32_t*)validateStagingBuffers[validateIndex];
1054 uint32_t val = allocInfo[validateAllocIndex].m_StartValue;
1055 bool valid = true;
1056 for(size_t i = 0; i < validateSize / sizeof(uint32_t); ++i)
1057 {
1058 if(*stagingValPtr != val)
1059 {
1060 valid = false;
1061 break;
1062 }
1063 ++stagingValPtr;
1064 ++val;
1065 }
1066 TEST(valid);
1067 }
1068
1069 stagingBufs.ReleaseAllBuffers();
1070
1071 validateAllocIndexOffset = allocInfoIndex;
1072 validateStagingBuffers.clear();
1073
1074 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
1075 TEST(ok);
1076 }
1077
1078 // Issue copy command from staging buffer to destination buffer.
1079 if(!cmdBufferStarted)
1080 {
1081 cmdBufferStarted = true;
1082 BeginSingleTimeCommands();
1083 }
1084
1085 VkBufferCopy copy = {};
1086 copy.srcOffset = 0;
1087 copy.dstOffset = 0;
1088 copy.size = size;
1089 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, currAllocInfo.m_Buffer, stagingBuf, 1, &copy);
1090
1091 // Sava mapped pointer for later validation.
1092 validateStagingBuffers.push_back(stagingBufMappedPtr);
1093 }
1094 else
1095 {
1096 TEST(0 && "Images not currently supported.");
1097 }
1098 }
1099
1100 if(cmdBufferStarted)
1101 {
1102 EndSingleTimeCommands();
1103
1104 for(size_t validateIndex = 0;
1105 validateIndex < validateStagingBuffers.size();
1106 ++validateIndex)
1107 {
1108 const size_t validateAllocIndex = validateIndex + validateAllocIndexOffset;
1109 const VkDeviceSize validateSize = allocInfo[validateAllocIndex].m_BufferInfo.size;
1110 TEST(validateSize % sizeof(uint32_t) == 0);
1111 const uint32_t* stagingValPtr = (const uint32_t*)validateStagingBuffers[validateIndex];
1112 uint32_t val = allocInfo[validateAllocIndex].m_StartValue;
1113 bool valid = true;
1114 for(size_t i = 0; i < validateSize / sizeof(uint32_t); ++i)
1115 {
1116 if(*stagingValPtr != val)
1117 {
1118 valid = false;
1119 break;
1120 }
1121 ++stagingValPtr;
1122 ++val;
1123 }
1124 TEST(valid);
1125 }
1126
1127 stagingBufs.ReleaseAllBuffers();
1128 }
1129}
1130
Adam Sawickib8333fb2018-03-13 16:15:53 +01001131static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
1132{
1133 outMemReq = {};
1134 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
1135 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
1136}
1137
1138static void CreateBuffer(
1139 VmaPool pool,
1140 const VkBufferCreateInfo& bufCreateInfo,
1141 bool persistentlyMapped,
1142 AllocInfo& outAllocInfo)
1143{
1144 outAllocInfo = {};
1145 outAllocInfo.m_BufferInfo = bufCreateInfo;
1146
1147 VmaAllocationCreateInfo allocCreateInfo = {};
1148 allocCreateInfo.pool = pool;
1149 if(persistentlyMapped)
1150 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1151
1152 VmaAllocationInfo vmaAllocInfo = {};
1153 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
1154
1155 // Setup StartValue and fill.
1156 {
1157 outAllocInfo.m_StartValue = (uint32_t)rand();
1158 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001159 TEST((data != nullptr) == persistentlyMapped);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001160 if(!persistentlyMapped)
1161 {
1162 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
1163 }
1164
1165 uint32_t value = outAllocInfo.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001166 TEST(bufCreateInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001167 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
1168 data[i] = value++;
1169
1170 if(!persistentlyMapped)
1171 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
1172 }
1173}
1174
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001175static void CreateAllocation(AllocInfo& outAllocation)
Adam Sawickib8333fb2018-03-13 16:15:53 +01001176{
1177 outAllocation.m_Allocation = nullptr;
1178 outAllocation.m_Buffer = nullptr;
1179 outAllocation.m_Image = nullptr;
1180 outAllocation.m_StartValue = (uint32_t)rand();
1181
1182 VmaAllocationCreateInfo vmaMemReq;
1183 GetMemReq(vmaMemReq);
1184
1185 VmaAllocationInfo allocInfo;
1186
1187 const bool isBuffer = true;//(rand() & 0x1) != 0;
1188 const bool isLarge = (rand() % 16) == 0;
1189 if(isBuffer)
1190 {
1191 const uint32_t bufferSize = isLarge ?
1192 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
1193 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
1194
1195 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1196 bufferInfo.size = bufferSize;
1197 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1198
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001199 VkResult res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001200 outAllocation.m_BufferInfo = bufferInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001201 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001202 }
1203 else
1204 {
1205 const uint32_t imageSizeX = isLarge ?
1206 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
1207 rand() % 1024 + 1; // 1 ... 1024
1208 const uint32_t imageSizeY = isLarge ?
1209 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
1210 rand() % 1024 + 1; // 1 ... 1024
1211
1212 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1213 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1214 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
1215 imageInfo.extent.width = imageSizeX;
1216 imageInfo.extent.height = imageSizeY;
1217 imageInfo.extent.depth = 1;
1218 imageInfo.mipLevels = 1;
1219 imageInfo.arrayLayers = 1;
1220 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1221 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1222 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
1223 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1224
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001225 VkResult res = vmaCreateImage(g_hAllocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001226 outAllocation.m_ImageInfo = imageInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001227 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001228 }
1229
1230 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
1231 if(allocInfo.pMappedData == nullptr)
1232 {
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001233 VkResult res = vmaMapMemory(g_hAllocator, outAllocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001234 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001235 }
1236
1237 uint32_t value = outAllocation.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001238 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001239 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
1240 data[i] = value++;
1241
1242 if(allocInfo.pMappedData == nullptr)
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001243 vmaUnmapMemory(g_hAllocator, outAllocation.m_Allocation);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001244}
1245
1246static void DestroyAllocation(const AllocInfo& allocation)
1247{
1248 if(allocation.m_Buffer)
1249 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
1250 else
1251 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
1252}
1253
1254static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
1255{
1256 for(size_t i = allocations.size(); i--; )
1257 DestroyAllocation(allocations[i]);
1258 allocations.clear();
1259}
1260
1261static void ValidateAllocationData(const AllocInfo& allocation)
1262{
1263 VmaAllocationInfo allocInfo;
1264 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
1265
1266 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
1267 if(allocInfo.pMappedData == nullptr)
1268 {
1269 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001270 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001271 }
1272
1273 uint32_t value = allocation.m_StartValue;
1274 bool ok = true;
1275 size_t i;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001276 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001277 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
1278 {
1279 if(data[i] != value++)
1280 {
1281 ok = false;
1282 break;
1283 }
1284 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02001285 TEST(ok);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001286
1287 if(allocInfo.pMappedData == nullptr)
1288 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
1289}
1290
1291static void RecreateAllocationResource(AllocInfo& allocation)
1292{
1293 VmaAllocationInfo allocInfo;
1294 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
1295
1296 if(allocation.m_Buffer)
1297 {
Adam Sawicki1f84f622019-07-02 13:40:01 +02001298 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001299
Adam Sawicki1f84f622019-07-02 13:40:01 +02001300 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, g_Allocs, &allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001301 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001302
1303 // Just to silence validation layer warnings.
1304 VkMemoryRequirements vkMemReq;
1305 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
Adam Sawicki2af57d72018-12-06 15:35:05 +01001306 TEST(vkMemReq.size >= allocation.m_BufferInfo.size);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001307
Adam Sawickiaf88c1b2019-07-02 12:34:26 +02001308 res = vmaBindBufferMemory(g_hAllocator, allocation.m_Allocation, allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001309 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001310 }
1311 else
1312 {
Adam Sawicki1f84f622019-07-02 13:40:01 +02001313 vkDestroyImage(g_hDevice, allocation.m_Image, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001314
Adam Sawicki1f84f622019-07-02 13:40:01 +02001315 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, g_Allocs, &allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001316 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001317
1318 // Just to silence validation layer warnings.
1319 VkMemoryRequirements vkMemReq;
1320 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
1321
Adam Sawickiaf88c1b2019-07-02 12:34:26 +02001322 res = vmaBindImageMemory(g_hAllocator, allocation.m_Allocation, allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001323 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001324 }
1325}
1326
1327static void Defragment(AllocInfo* allocs, size_t allocCount,
1328 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
1329 VmaDefragmentationStats* defragmentationStats = nullptr)
1330{
1331 std::vector<VmaAllocation> vmaAllocs(allocCount);
1332 for(size_t i = 0; i < allocCount; ++i)
1333 vmaAllocs[i] = allocs[i].m_Allocation;
1334
1335 std::vector<VkBool32> allocChanged(allocCount);
1336
1337 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
1338 defragmentationInfo, defragmentationStats) );
1339
1340 for(size_t i = 0; i < allocCount; ++i)
1341 {
1342 if(allocChanged[i])
1343 {
1344 RecreateAllocationResource(allocs[i]);
1345 }
1346 }
1347}
1348
1349static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
1350{
1351 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
1352 ValidateAllocationData(allocInfo);
1353 });
1354}
1355
1356void TestDefragmentationSimple()
1357{
1358 wprintf(L"Test defragmentation simple\n");
1359
1360 RandomNumberGenerator rand(667);
1361
1362 const VkDeviceSize BUF_SIZE = 0x10000;
1363 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
1364
1365 const VkDeviceSize MIN_BUF_SIZE = 32;
1366 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
1367 auto RandomBufSize = [&]() -> VkDeviceSize {
1368 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
1369 };
1370
1371 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1372 bufCreateInfo.size = BUF_SIZE;
1373 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1374
1375 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
1376 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1377
1378 uint32_t memTypeIndex = UINT32_MAX;
1379 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
1380
1381 VmaPoolCreateInfo poolCreateInfo = {};
1382 poolCreateInfo.blockSize = BLOCK_SIZE;
1383 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1384
1385 VmaPool pool;
1386 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
1387
Adam Sawickie1681912018-11-23 17:50:12 +01001388 // Defragmentation of empty pool.
1389 {
1390 VmaDefragmentationInfo2 defragInfo = {};
1391 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
1392 defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
1393 defragInfo.poolCount = 1;
1394 defragInfo.pPools = &pool;
1395
1396 VmaDefragmentationStats defragStats = {};
1397 VmaDefragmentationContext defragCtx = nullptr;
1398 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &defragStats, &defragCtx);
1399 TEST(res >= VK_SUCCESS);
1400 vmaDefragmentationEnd(g_hAllocator, defragCtx);
1401 TEST(defragStats.allocationsMoved == 0 && defragStats.bytesFreed == 0 &&
1402 defragStats.bytesMoved == 0 && defragStats.deviceMemoryBlocksFreed == 0);
1403 }
1404
Adam Sawickib8333fb2018-03-13 16:15:53 +01001405 std::vector<AllocInfo> allocations;
1406
1407 // persistentlyMappedOption = 0 - not persistently mapped.
1408 // persistentlyMappedOption = 1 - persistently mapped.
1409 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
1410 {
1411 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
1412 const bool persistentlyMapped = persistentlyMappedOption != 0;
1413
1414 // # Test 1
1415 // Buffers of fixed size.
1416 // Fill 2 blocks. Remove odd buffers. Defragment everything.
1417 // Expected result: at least 1 block freed.
1418 {
1419 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1420 {
1421 AllocInfo allocInfo;
1422 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1423 allocations.push_back(allocInfo);
1424 }
1425
1426 for(size_t i = 1; i < allocations.size(); ++i)
1427 {
1428 DestroyAllocation(allocations[i]);
1429 allocations.erase(allocations.begin() + i);
1430 }
1431
1432 VmaDefragmentationStats defragStats;
1433 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001434 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
1435 TEST(defragStats.deviceMemoryBlocksFreed >= 1);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001436
1437 ValidateAllocationsData(allocations.data(), allocations.size());
1438
1439 DestroyAllAllocations(allocations);
1440 }
1441
1442 // # Test 2
1443 // Buffers of fixed size.
1444 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
1445 // Expected result: Each of 4 interations makes some progress.
1446 {
1447 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1448 {
1449 AllocInfo allocInfo;
1450 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1451 allocations.push_back(allocInfo);
1452 }
1453
1454 for(size_t i = 1; i < allocations.size(); ++i)
1455 {
1456 DestroyAllocation(allocations[i]);
1457 allocations.erase(allocations.begin() + i);
1458 }
1459
1460 VmaDefragmentationInfo defragInfo = {};
1461 defragInfo.maxAllocationsToMove = 1;
1462 defragInfo.maxBytesToMove = BUF_SIZE;
1463
1464 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
1465 {
1466 VmaDefragmentationStats defragStats;
1467 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001468 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001469 }
1470
1471 ValidateAllocationsData(allocations.data(), allocations.size());
1472
1473 DestroyAllAllocations(allocations);
1474 }
1475
1476 // # Test 3
1477 // Buffers of variable size.
1478 // Create a number of buffers. Remove some percent of them.
1479 // Defragment while having some percent of them unmovable.
1480 // Expected result: Just simple validation.
1481 {
1482 for(size_t i = 0; i < 100; ++i)
1483 {
1484 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
1485 localBufCreateInfo.size = RandomBufSize();
1486
1487 AllocInfo allocInfo;
1488 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1489 allocations.push_back(allocInfo);
1490 }
1491
1492 const uint32_t percentToDelete = 60;
1493 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
1494 for(size_t i = 0; i < numberToDelete; ++i)
1495 {
1496 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
1497 DestroyAllocation(allocations[indexToDelete]);
1498 allocations.erase(allocations.begin() + indexToDelete);
1499 }
1500
1501 // Non-movable allocations will be at the beginning of allocations array.
1502 const uint32_t percentNonMovable = 20;
1503 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
1504 for(size_t i = 0; i < numberNonMovable; ++i)
1505 {
1506 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
1507 if(indexNonMovable != i)
1508 std::swap(allocations[i], allocations[indexNonMovable]);
1509 }
1510
1511 VmaDefragmentationStats defragStats;
1512 Defragment(
1513 allocations.data() + numberNonMovable,
1514 allocations.size() - numberNonMovable,
1515 nullptr, &defragStats);
1516
1517 ValidateAllocationsData(allocations.data(), allocations.size());
1518
1519 DestroyAllAllocations(allocations);
1520 }
1521 }
1522
Adam Sawicki647cf242018-11-23 17:58:00 +01001523 /*
1524 Allocation that must be move to an overlapping place using memmove().
1525 Create 2 buffers, second slightly bigger than the first. Delete first. Then defragment.
1526 */
Adam Sawickibdb89a92018-12-13 11:56:30 +01001527 if(VMA_DEBUG_MARGIN == 0) // FAST algorithm works only when DEBUG_MARGIN disabled.
Adam Sawicki647cf242018-11-23 17:58:00 +01001528 {
1529 AllocInfo allocInfo[2];
1530
1531 bufCreateInfo.size = BUF_SIZE;
1532 CreateBuffer(pool, bufCreateInfo, false, allocInfo[0]);
1533 const VkDeviceSize biggerBufSize = BUF_SIZE + BUF_SIZE / 256;
1534 bufCreateInfo.size = biggerBufSize;
1535 CreateBuffer(pool, bufCreateInfo, false, allocInfo[1]);
1536
1537 DestroyAllocation(allocInfo[0]);
1538
1539 VmaDefragmentationStats defragStats;
1540 Defragment(&allocInfo[1], 1, nullptr, &defragStats);
1541 // If this fails, it means we couldn't do memmove with overlapping regions.
1542 TEST(defragStats.allocationsMoved == 1 && defragStats.bytesMoved > 0);
1543
1544 ValidateAllocationsData(&allocInfo[1], 1);
1545 DestroyAllocation(allocInfo[1]);
1546 }
1547
Adam Sawickib8333fb2018-03-13 16:15:53 +01001548 vmaDestroyPool(g_hAllocator, pool);
1549}
1550
Adam Sawicki52076eb2018-11-22 16:14:50 +01001551void TestDefragmentationWholePool()
1552{
1553 wprintf(L"Test defragmentation whole pool\n");
1554
1555 RandomNumberGenerator rand(668);
1556
1557 const VkDeviceSize BUF_SIZE = 0x10000;
1558 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
1559
1560 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1561 bufCreateInfo.size = BUF_SIZE;
1562 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1563
1564 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
1565 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1566
1567 uint32_t memTypeIndex = UINT32_MAX;
1568 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
1569
1570 VmaPoolCreateInfo poolCreateInfo = {};
1571 poolCreateInfo.blockSize = BLOCK_SIZE;
1572 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1573
1574 VmaDefragmentationStats defragStats[2];
1575 for(size_t caseIndex = 0; caseIndex < 2; ++caseIndex)
1576 {
1577 VmaPool pool;
1578 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
1579
1580 std::vector<AllocInfo> allocations;
1581
1582 // Buffers of fixed size.
1583 // Fill 2 blocks. Remove odd buffers. Defragment all of them.
1584 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1585 {
1586 AllocInfo allocInfo;
1587 CreateBuffer(pool, bufCreateInfo, false, allocInfo);
1588 allocations.push_back(allocInfo);
1589 }
1590
1591 for(size_t i = 1; i < allocations.size(); ++i)
1592 {
1593 DestroyAllocation(allocations[i]);
1594 allocations.erase(allocations.begin() + i);
1595 }
1596
1597 VmaDefragmentationInfo2 defragInfo = {};
1598 defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
1599 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
1600 std::vector<VmaAllocation> allocationsToDefrag;
1601 if(caseIndex == 0)
1602 {
1603 defragInfo.poolCount = 1;
1604 defragInfo.pPools = &pool;
1605 }
1606 else
1607 {
1608 const size_t allocCount = allocations.size();
1609 allocationsToDefrag.resize(allocCount);
1610 std::transform(
1611 allocations.begin(), allocations.end(),
1612 allocationsToDefrag.begin(),
1613 [](const AllocInfo& allocInfo) { return allocInfo.m_Allocation; });
1614 defragInfo.allocationCount = (uint32_t)allocCount;
1615 defragInfo.pAllocations = allocationsToDefrag.data();
1616 }
1617
1618 VmaDefragmentationContext defragCtx = VK_NULL_HANDLE;
1619 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &defragStats[caseIndex], &defragCtx);
1620 TEST(res >= VK_SUCCESS);
1621 vmaDefragmentationEnd(g_hAllocator, defragCtx);
1622
1623 TEST(defragStats[caseIndex].allocationsMoved > 0 && defragStats[caseIndex].bytesMoved > 0);
1624
1625 ValidateAllocationsData(allocations.data(), allocations.size());
1626
1627 DestroyAllAllocations(allocations);
1628
1629 vmaDestroyPool(g_hAllocator, pool);
1630 }
1631
1632 TEST(defragStats[0].bytesMoved == defragStats[1].bytesMoved);
1633 TEST(defragStats[0].allocationsMoved == defragStats[1].allocationsMoved);
1634 TEST(defragStats[0].bytesFreed == defragStats[1].bytesFreed);
1635 TEST(defragStats[0].deviceMemoryBlocksFreed == defragStats[1].deviceMemoryBlocksFreed);
1636}
1637
Adam Sawickib8333fb2018-03-13 16:15:53 +01001638void TestDefragmentationFull()
1639{
1640 std::vector<AllocInfo> allocations;
1641
1642 // Create initial allocations.
1643 for(size_t i = 0; i < 400; ++i)
1644 {
1645 AllocInfo allocation;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001646 CreateAllocation(allocation);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001647 allocations.push_back(allocation);
1648 }
1649
1650 // Delete random allocations
1651 const size_t allocationsToDeletePercent = 80;
1652 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
1653 for(size_t i = 0; i < allocationsToDelete; ++i)
1654 {
1655 size_t index = (size_t)rand() % allocations.size();
1656 DestroyAllocation(allocations[index]);
1657 allocations.erase(allocations.begin() + index);
1658 }
1659
1660 for(size_t i = 0; i < allocations.size(); ++i)
1661 ValidateAllocationData(allocations[i]);
1662
Adam Sawicki0667e332018-08-24 17:26:44 +02001663 //SaveAllocatorStatsToFile(L"Before.csv");
Adam Sawickib8333fb2018-03-13 16:15:53 +01001664
1665 {
1666 std::vector<VmaAllocation> vmaAllocations(allocations.size());
1667 for(size_t i = 0; i < allocations.size(); ++i)
1668 vmaAllocations[i] = allocations[i].m_Allocation;
1669
1670 const size_t nonMovablePercent = 0;
1671 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
1672 for(size_t i = 0; i < nonMovableCount; ++i)
1673 {
1674 size_t index = (size_t)rand() % vmaAllocations.size();
1675 vmaAllocations.erase(vmaAllocations.begin() + index);
1676 }
1677
1678 const uint32_t defragCount = 1;
1679 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1680 {
1681 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1682
1683 VmaDefragmentationInfo defragmentationInfo;
1684 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1685 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1686
1687 wprintf(L"Defragmentation #%u\n", defragIndex);
1688
1689 time_point begTime = std::chrono::high_resolution_clock::now();
1690
1691 VmaDefragmentationStats stats;
1692 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001693 TEST(res >= 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001694
1695 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1696
1697 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1698 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1699 wprintf(L"Time: %.2f s\n", defragmentDuration);
1700
1701 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1702 {
1703 if(allocationsChanged[i])
1704 {
1705 RecreateAllocationResource(allocations[i]);
1706 }
1707 }
1708
1709 for(size_t i = 0; i < allocations.size(); ++i)
1710 ValidateAllocationData(allocations[i]);
1711
Adam Sawicki0667e332018-08-24 17:26:44 +02001712 //wchar_t fileName[MAX_PATH];
1713 //swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
1714 //SaveAllocatorStatsToFile(fileName);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001715 }
1716 }
1717
1718 // Destroy all remaining allocations.
1719 DestroyAllAllocations(allocations);
1720}
1721
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001722static void TestDefragmentationGpu()
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001723{
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001724 wprintf(L"Test defragmentation GPU\n");
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001725
1726 std::vector<AllocInfo> allocations;
1727
1728 // Create that many allocations to surely fill 3 new blocks of 256 MB.
Adam Sawickic6ede152018-11-16 17:04:14 +01001729 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
1730 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001731 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
Adam Sawickic6ede152018-11-16 17:04:14 +01001732 const size_t bufCount = (size_t)(totalSize / bufSizeMin);
1733 const size_t percentToLeave = 30;
1734 const size_t percentNonMovable = 3;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001735 RandomNumberGenerator rand = { 234522 };
1736
1737 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001738
1739 VmaAllocationCreateInfo allocCreateInfo = {};
1740 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
Adam Sawickic6ede152018-11-16 17:04:14 +01001741 allocCreateInfo.flags = 0;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001742
1743 // Create all intended buffers.
1744 for(size_t i = 0; i < bufCount; ++i)
1745 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001746 bufCreateInfo.size = align_up(rand.Generate() % (bufSizeMax - bufSizeMin) + bufSizeMin, 32ull);
1747
1748 if(rand.Generate() % 100 < percentNonMovable)
1749 {
1750 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
1751 VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1752 VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1753 allocCreateInfo.pUserData = (void*)(uintptr_t)2;
1754 }
1755 else
1756 {
1757 // Different usage just to see different color in output from VmaDumpVis.
1758 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
1759 VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1760 VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1761 // And in JSON dump.
1762 allocCreateInfo.pUserData = (void*)(uintptr_t)1;
1763 }
1764
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001765 AllocInfo alloc;
1766 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
1767 alloc.m_StartValue = rand.Generate();
1768 allocations.push_back(alloc);
1769 }
1770
1771 // Destroy some percentage of them.
1772 {
1773 const size_t buffersToDestroy = round_div<size_t>(bufCount * (100 - percentToLeave), 100);
1774 for(size_t i = 0; i < buffersToDestroy; ++i)
1775 {
1776 const size_t index = rand.Generate() % allocations.size();
1777 allocations[index].Destroy();
1778 allocations.erase(allocations.begin() + index);
1779 }
1780 }
1781
1782 // Fill them with meaningful data.
1783 UploadGpuData(allocations.data(), allocations.size());
1784
Adam Sawickic6ede152018-11-16 17:04:14 +01001785 wchar_t fileName[MAX_PATH];
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001786 swprintf_s(fileName, L"GPU_defragmentation_A_before.json");
Adam Sawickic6ede152018-11-16 17:04:14 +01001787 SaveAllocatorStatsToFile(fileName);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001788
1789 // Defragment using GPU only.
1790 {
1791 const size_t allocCount = allocations.size();
Adam Sawicki440307e2018-10-18 15:05:19 +02001792
Adam Sawickic6ede152018-11-16 17:04:14 +01001793 std::vector<VmaAllocation> allocationPtrs;
1794 std::vector<VkBool32> allocationChanged;
1795 std::vector<size_t> allocationOriginalIndex;
1796
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001797 for(size_t i = 0; i < allocCount; ++i)
1798 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001799 VmaAllocationInfo allocInfo = {};
1800 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
1801 if((uintptr_t)allocInfo.pUserData == 1) // Movable
1802 {
1803 allocationPtrs.push_back(allocations[i].m_Allocation);
1804 allocationChanged.push_back(VK_FALSE);
1805 allocationOriginalIndex.push_back(i);
1806 }
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001807 }
Adam Sawickic6ede152018-11-16 17:04:14 +01001808
1809 const size_t movableAllocCount = allocationPtrs.size();
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001810
1811 BeginSingleTimeCommands();
1812
1813 VmaDefragmentationInfo2 defragInfo = {};
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001814 defragInfo.flags = 0;
Adam Sawickic6ede152018-11-16 17:04:14 +01001815 defragInfo.allocationCount = (uint32_t)movableAllocCount;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001816 defragInfo.pAllocations = allocationPtrs.data();
Adam Sawicki440307e2018-10-18 15:05:19 +02001817 defragInfo.pAllocationsChanged = allocationChanged.data();
1818 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001819 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
1820 defragInfo.commandBuffer = g_hTemporaryCommandBuffer;
1821
1822 VmaDefragmentationStats stats = {};
1823 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
1824 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
1825 TEST(res >= VK_SUCCESS);
1826
1827 EndSingleTimeCommands();
1828
1829 vmaDefragmentationEnd(g_hAllocator, ctx);
1830
Adam Sawickic6ede152018-11-16 17:04:14 +01001831 for(size_t i = 0; i < movableAllocCount; ++i)
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001832 {
1833 if(allocationChanged[i])
1834 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001835 const size_t origAllocIndex = allocationOriginalIndex[i];
1836 RecreateAllocationResource(allocations[origAllocIndex]);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001837 }
1838 }
1839
Adam Sawicki4d844e22019-01-24 16:21:05 +01001840 // If corruption detection is enabled, GPU defragmentation may not work on
1841 // memory types that have this detection active, e.g. on Intel.
Adam Sawickia1f727c2019-01-24 16:25:11 +01001842 #if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
Adam Sawicki4d844e22019-01-24 16:21:05 +01001843 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
1844 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
Adam Sawickia1f727c2019-01-24 16:25:11 +01001845 #endif
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001846 }
1847
1848 ValidateGpuData(allocations.data(), allocations.size());
1849
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001850 swprintf_s(fileName, L"GPU_defragmentation_B_after.json");
Adam Sawickic6ede152018-11-16 17:04:14 +01001851 SaveAllocatorStatsToFile(fileName);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001852
1853 // Destroy all remaining buffers.
1854 for(size_t i = allocations.size(); i--; )
1855 {
1856 allocations[i].Destroy();
1857 }
1858}
1859
Adam Sawickic467e282019-12-23 16:38:31 +01001860static void ProcessDefragmentationStepInfo(VmaDefragmentationPassInfo &stepInfo)
Adam Sawickia52012d2019-12-23 15:28:51 +01001861{
1862 std::vector<VkImageMemoryBarrier> beginImageBarriers;
1863 std::vector<VkImageMemoryBarrier> finalizeImageBarriers;
1864
1865 VkPipelineStageFlags beginSrcStageMask = 0;
1866 VkPipelineStageFlags beginDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1867
1868 VkPipelineStageFlags finalizeSrcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1869 VkPipelineStageFlags finalizeDstStageMask = 0;
1870
1871 bool wantsMemoryBarrier = false;
1872
1873 VkMemoryBarrier beginMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
1874 VkMemoryBarrier finalizeMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
1875
Adam Sawickic467e282019-12-23 16:38:31 +01001876 for(uint32_t i = 0; i < stepInfo.moveCount; ++i)
Adam Sawickia52012d2019-12-23 15:28:51 +01001877 {
1878 VmaAllocationInfo info;
1879 vmaGetAllocationInfo(g_hAllocator, stepInfo.pMoves[i].allocation, &info);
1880
1881 AllocInfo *allocInfo = (AllocInfo *)info.pUserData;
1882
1883 if(allocInfo->m_Image)
1884 {
1885 VkImage newImage;
1886
1887 const VkResult result = vkCreateImage(g_hDevice, &allocInfo->m_ImageInfo, g_Allocs, &newImage);
1888 TEST(result >= VK_SUCCESS);
1889
1890 vkBindImageMemory(g_hDevice, newImage, stepInfo.pMoves[i].memory, stepInfo.pMoves[i].offset);
Adam Sawickic467e282019-12-23 16:38:31 +01001891 allocInfo->m_NewImage = newImage;
Adam Sawickia52012d2019-12-23 15:28:51 +01001892
1893 // Keep track of our pipeline stages that we need to wait/signal on
1894 beginSrcStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1895 finalizeDstStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1896
1897 // We need one pipeline barrier and two image layout transitions here
1898 // First we'll have to turn our newly created image into VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
1899 // And the second one is turning the old image into VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
1900
1901 VkImageSubresourceRange subresourceRange = {
1902 VK_IMAGE_ASPECT_COLOR_BIT,
1903 0, VK_REMAINING_MIP_LEVELS,
1904 0, VK_REMAINING_ARRAY_LAYERS
1905 };
1906
1907 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
1908 barrier.srcAccessMask = 0;
1909 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1910 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1911 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1912 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1913 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1914 barrier.image = newImage;
1915 barrier.subresourceRange = subresourceRange;
1916
1917 beginImageBarriers.push_back(barrier);
1918
1919 // Second barrier to convert the existing image. This one actually needs a real barrier
1920 barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
1921 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1922 barrier.oldLayout = allocInfo->m_ImageLayout;
1923 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1924 barrier.image = allocInfo->m_Image;
1925
1926 beginImageBarriers.push_back(barrier);
1927
1928 // And lastly we need a barrier that turns our new image into the layout of the old one
1929 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1930 barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1931 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1932 barrier.newLayout = allocInfo->m_ImageLayout;
1933 barrier.image = newImage;
1934
1935 finalizeImageBarriers.push_back(barrier);
1936 }
1937 else if(allocInfo->m_Buffer)
1938 {
1939 VkBuffer newBuffer;
1940
1941 const VkResult result = vkCreateBuffer(g_hDevice, &allocInfo->m_BufferInfo, g_Allocs, &newBuffer);
1942 TEST(result >= VK_SUCCESS);
1943
1944 vkBindBufferMemory(g_hDevice, newBuffer, stepInfo.pMoves[i].memory, stepInfo.pMoves[i].offset);
Adam Sawickic467e282019-12-23 16:38:31 +01001945 allocInfo->m_NewBuffer = newBuffer;
Adam Sawickia52012d2019-12-23 15:28:51 +01001946
1947 // Keep track of our pipeline stages that we need to wait/signal on
1948 beginSrcStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1949 finalizeDstStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1950
1951 beginMemoryBarrier.srcAccessMask |= VK_ACCESS_MEMORY_WRITE_BIT;
1952 beginMemoryBarrier.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
1953
1954 finalizeMemoryBarrier.srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
1955 finalizeMemoryBarrier.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT;
1956
1957 wantsMemoryBarrier = true;
1958 }
1959 }
1960
1961 if(!beginImageBarriers.empty() || wantsMemoryBarrier)
1962 {
1963 const uint32_t memoryBarrierCount = wantsMemoryBarrier ? 1 : 0;
1964
1965 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, beginSrcStageMask, beginDstStageMask, 0,
1966 memoryBarrierCount, &beginMemoryBarrier,
1967 0, nullptr,
1968 (uint32_t)beginImageBarriers.size(), beginImageBarriers.data());
1969 }
1970
1971 for(uint32_t i = 0; i < stepInfo.moveCount; ++ i)
1972 {
1973 VmaAllocationInfo info;
1974 vmaGetAllocationInfo(g_hAllocator, stepInfo.pMoves[i].allocation, &info);
1975
1976 AllocInfo *allocInfo = (AllocInfo *)info.pUserData;
1977
1978 if(allocInfo->m_Image)
1979 {
1980 std::vector<VkImageCopy> imageCopies;
1981
1982 // Copy all mips of the source image into the target image
1983 VkOffset3D offset = { 0, 0, 0 };
1984 VkExtent3D extent = allocInfo->m_ImageInfo.extent;
1985
1986 VkImageSubresourceLayers subresourceLayers = {
1987 VK_IMAGE_ASPECT_COLOR_BIT,
1988 0,
1989 0, 1
1990 };
1991
1992 for(uint32_t mip = 0; mip < allocInfo->m_ImageInfo.mipLevels; ++ mip)
1993 {
1994 subresourceLayers.mipLevel = mip;
1995
1996 VkImageCopy imageCopy{
1997 subresourceLayers,
1998 offset,
1999 subresourceLayers,
2000 offset,
2001 extent
2002 };
2003
2004 imageCopies.push_back(imageCopy);
2005
2006 extent.width = std::max(uint32_t(1), extent.width >> 1);
2007 extent.height = std::max(uint32_t(1), extent.height >> 1);
2008 extent.depth = std::max(uint32_t(1), extent.depth >> 1);
2009 }
2010
2011 vkCmdCopyImage(
2012 g_hTemporaryCommandBuffer,
2013 allocInfo->m_Image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
Adam Sawickic467e282019-12-23 16:38:31 +01002014 allocInfo->m_NewImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
Adam Sawickia52012d2019-12-23 15:28:51 +01002015 (uint32_t)imageCopies.size(), imageCopies.data());
Adam Sawickia52012d2019-12-23 15:28:51 +01002016 }
2017 else if(allocInfo->m_Buffer)
2018 {
2019 VkBufferCopy region = {
2020 0,
2021 0,
2022 allocInfo->m_BufferInfo.size };
2023
2024 vkCmdCopyBuffer(g_hTemporaryCommandBuffer,
Adam Sawickic467e282019-12-23 16:38:31 +01002025 allocInfo->m_Buffer, allocInfo->m_NewBuffer,
Adam Sawickia52012d2019-12-23 15:28:51 +01002026 1, &region);
Adam Sawickia52012d2019-12-23 15:28:51 +01002027 }
2028 }
2029
Adam Sawickia52012d2019-12-23 15:28:51 +01002030 if(!finalizeImageBarriers.empty() || wantsMemoryBarrier)
2031 {
2032 const uint32_t memoryBarrierCount = wantsMemoryBarrier ? 1 : 0;
2033
2034 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, finalizeSrcStageMask, finalizeDstStageMask, 0,
2035 memoryBarrierCount, &finalizeMemoryBarrier,
2036 0, nullptr,
2037 (uint32_t)finalizeImageBarriers.size(), finalizeImageBarriers.data());
2038 }
2039}
2040
2041
2042static void TestDefragmentationIncrementalBasic()
2043{
2044 wprintf(L"Test defragmentation incremental basic\n");
Adam Sawickia52012d2019-12-23 15:28:51 +01002045
2046 std::vector<AllocInfo> allocations;
2047
2048 // Create that many allocations to surely fill 3 new blocks of 256 MB.
2049 const std::array<uint32_t, 3> imageSizes = { 256, 512, 1024 };
2050 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
2051 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
2052 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
Adam Sawickic467e282019-12-23 16:38:31 +01002053 const size_t imageCount = totalSize / ((size_t)imageSizes[0] * imageSizes[0] * 4) / 2;
Adam Sawickia52012d2019-12-23 15:28:51 +01002054 const size_t bufCount = (size_t)(totalSize / bufSizeMin) / 2;
2055 const size_t percentToLeave = 30;
2056 RandomNumberGenerator rand = { 234522 };
2057
2058 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2059 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2060 imageInfo.extent.depth = 1;
2061 imageInfo.mipLevels = 1;
2062 imageInfo.arrayLayers = 1;
2063 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2064 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
2065 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2066 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2067 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2068
2069 VmaAllocationCreateInfo allocCreateInfo = {};
2070 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2071 allocCreateInfo.flags = 0;
2072
2073 // Create all intended images.
2074 for(size_t i = 0; i < imageCount; ++i)
2075 {
2076 const uint32_t size = imageSizes[rand.Generate() % 3];
2077
2078 imageInfo.extent.width = size;
2079 imageInfo.extent.height = size;
2080
2081 AllocInfo alloc;
2082 alloc.CreateImage(imageInfo, allocCreateInfo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
2083 alloc.m_StartValue = 0;
2084
2085 allocations.push_back(alloc);
2086 }
2087
2088 // And all buffers
2089 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2090
2091 for(size_t i = 0; i < bufCount; ++i)
2092 {
2093 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
2094 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2095
2096 AllocInfo alloc;
2097 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
2098 alloc.m_StartValue = 0;
2099
2100 allocations.push_back(alloc);
2101 }
2102
2103 // Destroy some percentage of them.
2104 {
2105 const size_t allocationsToDestroy = round_div<size_t>((imageCount + bufCount) * (100 - percentToLeave), 100);
2106 for(size_t i = 0; i < allocationsToDestroy; ++i)
2107 {
2108 const size_t index = rand.Generate() % allocations.size();
2109 allocations[index].Destroy();
2110 allocations.erase(allocations.begin() + index);
2111 }
2112 }
2113
2114 {
2115 // Set our user data pointers. A real application should probably be more clever here
2116 const size_t allocationCount = allocations.size();
2117 for(size_t i = 0; i < allocationCount; ++i)
2118 {
2119 AllocInfo &alloc = allocations[i];
2120 vmaSetAllocationUserData(g_hAllocator, alloc.m_Allocation, &alloc);
2121 }
2122 }
2123
2124 // Fill them with meaningful data.
2125 UploadGpuData(allocations.data(), allocations.size());
2126
2127 wchar_t fileName[MAX_PATH];
2128 swprintf_s(fileName, L"GPU_defragmentation_incremental_basic_A_before.json");
2129 SaveAllocatorStatsToFile(fileName);
2130
2131 // Defragment using GPU only.
2132 {
2133 const size_t allocCount = allocations.size();
2134
2135 std::vector<VmaAllocation> allocationPtrs;
2136
2137 for(size_t i = 0; i < allocCount; ++i)
2138 {
Adam Sawickia52012d2019-12-23 15:28:51 +01002139 allocationPtrs.push_back(allocations[i].m_Allocation);
2140 }
2141
2142 const size_t movableAllocCount = allocationPtrs.size();
2143
2144 VmaDefragmentationInfo2 defragInfo = {};
2145 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_INCREMENTAL;
2146 defragInfo.allocationCount = (uint32_t)movableAllocCount;
2147 defragInfo.pAllocations = allocationPtrs.data();
2148 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2149 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2150
2151 VmaDefragmentationStats stats = {};
2152 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
2153 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
2154 TEST(res >= VK_SUCCESS);
2155
2156 res = VK_NOT_READY;
2157
Adam Sawickic467e282019-12-23 16:38:31 +01002158 std::vector<VmaDefragmentationPassMoveInfo> moveInfo;
Adam Sawickia52012d2019-12-23 15:28:51 +01002159 moveInfo.resize(movableAllocCount);
2160
2161 while(res == VK_NOT_READY)
2162 {
Adam Sawickic467e282019-12-23 16:38:31 +01002163 VmaDefragmentationPassInfo stepInfo = {};
Adam Sawickia52012d2019-12-23 15:28:51 +01002164 stepInfo.pMoves = moveInfo.data();
2165 stepInfo.moveCount = (uint32_t)moveInfo.size();
2166
Adam Sawickic467e282019-12-23 16:38:31 +01002167 res = vmaBeginDefragmentationPass(g_hAllocator, ctx, &stepInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +01002168 TEST(res >= VK_SUCCESS);
2169
2170 BeginSingleTimeCommands();
Adam Sawickic467e282019-12-23 16:38:31 +01002171 std::vector<void*> newHandles;
Adam Sawickia52012d2019-12-23 15:28:51 +01002172 ProcessDefragmentationStepInfo(stepInfo);
2173 EndSingleTimeCommands();
2174
Adam Sawickic467e282019-12-23 16:38:31 +01002175 res = vmaEndDefragmentationPass(g_hAllocator, ctx);
2176
2177 // Destroy old buffers/images and replace them with new handles.
2178 for(size_t i = 0; i < stepInfo.moveCount; ++i)
2179 {
2180 VmaAllocation const alloc = stepInfo.pMoves[i].allocation;
2181 VmaAllocationInfo vmaAllocInfo;
2182 vmaGetAllocationInfo(g_hAllocator, alloc, &vmaAllocInfo);
2183 AllocInfo* allocInfo = (AllocInfo*)vmaAllocInfo.pUserData;
2184 if(allocInfo->m_Buffer)
2185 {
2186 assert(allocInfo->m_NewBuffer && !allocInfo->m_Image && !allocInfo->m_NewImage);
2187 vkDestroyBuffer(g_hDevice, allocInfo->m_Buffer, g_Allocs);
2188 allocInfo->m_Buffer = allocInfo->m_NewBuffer;
2189 allocInfo->m_NewBuffer = VK_NULL_HANDLE;
2190 }
2191 else if(allocInfo->m_Image)
2192 {
2193 assert(allocInfo->m_NewImage && !allocInfo->m_Buffer && !allocInfo->m_NewBuffer);
2194 vkDestroyImage(g_hDevice, allocInfo->m_Image, g_Allocs);
2195 allocInfo->m_Image = allocInfo->m_NewImage;
2196 allocInfo->m_NewImage = VK_NULL_HANDLE;
2197 }
2198 else
2199 assert(0);
2200 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002201 }
2202
2203 TEST(res >= VK_SUCCESS);
2204 vmaDefragmentationEnd(g_hAllocator, ctx);
2205
2206 // If corruption detection is enabled, GPU defragmentation may not work on
2207 // memory types that have this detection active, e.g. on Intel.
2208#if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
2209 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
2210 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
2211#endif
2212 }
2213
2214 //ValidateGpuData(allocations.data(), allocations.size());
2215
2216 swprintf_s(fileName, L"GPU_defragmentation_incremental_basic_B_after.json");
2217 SaveAllocatorStatsToFile(fileName);
2218
Adam Sawickic467e282019-12-23 16:38:31 +01002219 // Destroy all remaining buffers and images.
Adam Sawickia52012d2019-12-23 15:28:51 +01002220 for(size_t i = allocations.size(); i--; )
2221 {
2222 allocations[i].Destroy();
2223 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002224}
2225
2226void TestDefragmentationIncrementalComplex()
2227{
2228 wprintf(L"Test defragmentation incremental complex\n");
Adam Sawickidb4c1632020-07-16 16:41:53 +02002229
Adam Sawickia52012d2019-12-23 15:28:51 +01002230 std::vector<AllocInfo> allocations;
2231
2232 // Create that many allocations to surely fill 3 new blocks of 256 MB.
2233 const std::array<uint32_t, 3> imageSizes = { 256, 512, 1024 };
2234 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
2235 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
2236 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
2237 const size_t imageCount = (size_t)(totalSize / (imageSizes[0] * imageSizes[0] * 4)) / 2;
2238 const size_t bufCount = (size_t)(totalSize / bufSizeMin) / 2;
2239 const size_t percentToLeave = 30;
2240 RandomNumberGenerator rand = { 234522 };
2241
2242 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2243 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2244 imageInfo.extent.depth = 1;
2245 imageInfo.mipLevels = 1;
2246 imageInfo.arrayLayers = 1;
2247 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2248 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
2249 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2250 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2251 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2252
2253 VmaAllocationCreateInfo allocCreateInfo = {};
2254 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2255 allocCreateInfo.flags = 0;
2256
2257 // Create all intended images.
2258 for(size_t i = 0; i < imageCount; ++i)
2259 {
2260 const uint32_t size = imageSizes[rand.Generate() % 3];
2261
2262 imageInfo.extent.width = size;
2263 imageInfo.extent.height = size;
2264
2265 AllocInfo alloc;
2266 alloc.CreateImage(imageInfo, allocCreateInfo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
2267 alloc.m_StartValue = 0;
2268
2269 allocations.push_back(alloc);
2270 }
2271
2272 // And all buffers
2273 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2274
2275 for(size_t i = 0; i < bufCount; ++i)
2276 {
2277 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
2278 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2279
2280 AllocInfo alloc;
2281 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
2282 alloc.m_StartValue = 0;
2283
2284 allocations.push_back(alloc);
2285 }
2286
2287 // Destroy some percentage of them.
2288 {
2289 const size_t allocationsToDestroy = round_div<size_t>((imageCount + bufCount) * (100 - percentToLeave), 100);
2290 for(size_t i = 0; i < allocationsToDestroy; ++i)
2291 {
2292 const size_t index = rand.Generate() % allocations.size();
2293 allocations[index].Destroy();
2294 allocations.erase(allocations.begin() + index);
2295 }
2296 }
2297
2298 {
2299 // Set our user data pointers. A real application should probably be more clever here
2300 const size_t allocationCount = allocations.size();
2301 for(size_t i = 0; i < allocationCount; ++i)
2302 {
2303 AllocInfo &alloc = allocations[i];
2304 vmaSetAllocationUserData(g_hAllocator, alloc.m_Allocation, &alloc);
2305 }
2306 }
2307
2308 // Fill them with meaningful data.
2309 UploadGpuData(allocations.data(), allocations.size());
2310
2311 wchar_t fileName[MAX_PATH];
2312 swprintf_s(fileName, L"GPU_defragmentation_incremental_complex_A_before.json");
2313 SaveAllocatorStatsToFile(fileName);
2314
2315 std::vector<AllocInfo> additionalAllocations;
2316
2317#define MakeAdditionalAllocation() \
2318 do { \
2319 { \
2320 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16); \
2321 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; \
2322 \
2323 AllocInfo alloc; \
2324 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo); \
2325 \
2326 additionalAllocations.push_back(alloc); \
2327 } \
2328 } while(0)
2329
2330 // Defragment using GPU only.
2331 {
2332 const size_t allocCount = allocations.size();
2333
2334 std::vector<VmaAllocation> allocationPtrs;
2335
2336 for(size_t i = 0; i < allocCount; ++i)
2337 {
2338 VmaAllocationInfo allocInfo = {};
2339 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
2340
2341 allocationPtrs.push_back(allocations[i].m_Allocation);
2342 }
2343
2344 const size_t movableAllocCount = allocationPtrs.size();
2345
2346 VmaDefragmentationInfo2 defragInfo = {};
2347 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_INCREMENTAL;
2348 defragInfo.allocationCount = (uint32_t)movableAllocCount;
2349 defragInfo.pAllocations = allocationPtrs.data();
2350 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2351 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2352
2353 VmaDefragmentationStats stats = {};
2354 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
2355 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
2356 TEST(res >= VK_SUCCESS);
2357
2358 res = VK_NOT_READY;
2359
Adam Sawickic467e282019-12-23 16:38:31 +01002360 std::vector<VmaDefragmentationPassMoveInfo> moveInfo;
Adam Sawickia52012d2019-12-23 15:28:51 +01002361 moveInfo.resize(movableAllocCount);
2362
2363 MakeAdditionalAllocation();
2364
2365 while(res == VK_NOT_READY)
2366 {
Adam Sawickic467e282019-12-23 16:38:31 +01002367 VmaDefragmentationPassInfo stepInfo = {};
Adam Sawickia52012d2019-12-23 15:28:51 +01002368 stepInfo.pMoves = moveInfo.data();
2369 stepInfo.moveCount = (uint32_t)moveInfo.size();
2370
Adam Sawickic467e282019-12-23 16:38:31 +01002371 res = vmaBeginDefragmentationPass(g_hAllocator, ctx, &stepInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +01002372 TEST(res >= VK_SUCCESS);
2373
2374 MakeAdditionalAllocation();
2375
2376 BeginSingleTimeCommands();
2377 ProcessDefragmentationStepInfo(stepInfo);
2378 EndSingleTimeCommands();
2379
Adam Sawickic467e282019-12-23 16:38:31 +01002380 res = vmaEndDefragmentationPass(g_hAllocator, ctx);
2381
2382 // Destroy old buffers/images and replace them with new handles.
2383 for(size_t i = 0; i < stepInfo.moveCount; ++i)
2384 {
2385 VmaAllocation const alloc = stepInfo.pMoves[i].allocation;
2386 VmaAllocationInfo vmaAllocInfo;
2387 vmaGetAllocationInfo(g_hAllocator, alloc, &vmaAllocInfo);
2388 AllocInfo* allocInfo = (AllocInfo*)vmaAllocInfo.pUserData;
2389 if(allocInfo->m_Buffer)
2390 {
2391 assert(allocInfo->m_NewBuffer && !allocInfo->m_Image && !allocInfo->m_NewImage);
2392 vkDestroyBuffer(g_hDevice, allocInfo->m_Buffer, g_Allocs);
2393 allocInfo->m_Buffer = allocInfo->m_NewBuffer;
2394 allocInfo->m_NewBuffer = VK_NULL_HANDLE;
2395 }
2396 else if(allocInfo->m_Image)
2397 {
2398 assert(allocInfo->m_NewImage && !allocInfo->m_Buffer && !allocInfo->m_NewBuffer);
2399 vkDestroyImage(g_hDevice, allocInfo->m_Image, g_Allocs);
2400 allocInfo->m_Image = allocInfo->m_NewImage;
2401 allocInfo->m_NewImage = VK_NULL_HANDLE;
2402 }
2403 else
2404 assert(0);
2405 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002406
2407 MakeAdditionalAllocation();
2408 }
2409
2410 TEST(res >= VK_SUCCESS);
2411 vmaDefragmentationEnd(g_hAllocator, ctx);
2412
2413 // If corruption detection is enabled, GPU defragmentation may not work on
2414 // memory types that have this detection active, e.g. on Intel.
2415#if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
2416 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
2417 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
2418#endif
2419 }
2420
2421 //ValidateGpuData(allocations.data(), allocations.size());
2422
2423 swprintf_s(fileName, L"GPU_defragmentation_incremental_complex_B_after.json");
2424 SaveAllocatorStatsToFile(fileName);
2425
2426 // Destroy all remaining buffers.
2427 for(size_t i = allocations.size(); i--; )
2428 {
2429 allocations[i].Destroy();
2430 }
2431
2432 for(size_t i = additionalAllocations.size(); i--; )
2433 {
2434 additionalAllocations[i].Destroy();
2435 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002436}
2437
2438
Adam Sawickib8333fb2018-03-13 16:15:53 +01002439static void TestUserData()
2440{
2441 VkResult res;
2442
2443 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2444 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
2445 bufCreateInfo.size = 0x10000;
2446
2447 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
2448 {
2449 // Opaque pointer
2450 {
2451
2452 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
2453 void* pointerToSomething = &res;
2454
2455 VmaAllocationCreateInfo allocCreateInfo = {};
2456 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2457 allocCreateInfo.pUserData = numberAsPointer;
2458 if(testIndex == 1)
2459 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2460
2461 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2462 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002463 TEST(res == VK_SUCCESS);
2464 TEST(allocInfo.pUserData = numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002465
2466 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002467 TEST(allocInfo.pUserData == numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002468
2469 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
2470 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002471 TEST(allocInfo.pUserData == pointerToSomething);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002472
2473 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2474 }
2475
2476 // String
2477 {
2478 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
2479 const char* name2 = "2";
2480 const size_t name1Len = strlen(name1);
2481
2482 char* name1Buf = new char[name1Len + 1];
2483 strcpy_s(name1Buf, name1Len + 1, name1);
2484
2485 VmaAllocationCreateInfo allocCreateInfo = {};
2486 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2487 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
2488 allocCreateInfo.pUserData = name1Buf;
2489 if(testIndex == 1)
2490 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2491
2492 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2493 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002494 TEST(res == VK_SUCCESS);
2495 TEST(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
2496 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002497
2498 delete[] name1Buf;
2499
2500 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002501 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002502
2503 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
2504 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002505 TEST(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002506
2507 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
2508 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002509 TEST(allocInfo.pUserData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002510
2511 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2512 }
2513 }
2514}
2515
Adam Sawicki370ab182018-11-08 16:31:00 +01002516static void TestInvalidAllocations()
2517{
2518 VkResult res;
2519
2520 VmaAllocationCreateInfo allocCreateInfo = {};
2521 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2522
2523 // Try to allocate 0 bytes.
2524 {
2525 VkMemoryRequirements memReq = {};
2526 memReq.size = 0; // !!!
2527 memReq.alignment = 4;
2528 memReq.memoryTypeBits = UINT32_MAX;
2529 VmaAllocation alloc = VK_NULL_HANDLE;
2530 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2531 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && alloc == VK_NULL_HANDLE);
2532 }
2533
2534 // Try to create buffer with size = 0.
2535 {
2536 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2537 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2538 bufCreateInfo.size = 0; // !!!
2539 VkBuffer buf = VK_NULL_HANDLE;
2540 VmaAllocation alloc = VK_NULL_HANDLE;
2541 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
2542 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && buf == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
2543 }
2544
2545 // Try to create image with one dimension = 0.
2546 {
2547 VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2548 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
2549 imageCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
2550 imageCreateInfo.extent.width = 128;
2551 imageCreateInfo.extent.height = 0; // !!!
2552 imageCreateInfo.extent.depth = 1;
2553 imageCreateInfo.mipLevels = 1;
2554 imageCreateInfo.arrayLayers = 1;
2555 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2556 imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
2557 imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
2558 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2559 VkImage image = VK_NULL_HANDLE;
2560 VmaAllocation alloc = VK_NULL_HANDLE;
2561 res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &image, &alloc, nullptr);
2562 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && image == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
2563 }
2564}
2565
Adam Sawickib8333fb2018-03-13 16:15:53 +01002566static void TestMemoryRequirements()
2567{
2568 VkResult res;
2569 VkBuffer buf;
2570 VmaAllocation alloc;
2571 VmaAllocationInfo allocInfo;
2572
2573 const VkPhysicalDeviceMemoryProperties* memProps;
2574 vmaGetMemoryProperties(g_hAllocator, &memProps);
2575
2576 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2577 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2578 bufInfo.size = 128;
2579
2580 VmaAllocationCreateInfo allocCreateInfo = {};
2581
2582 // No requirements.
2583 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002584 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002585 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2586
2587 // Usage.
2588 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2589 allocCreateInfo.requiredFlags = 0;
2590 allocCreateInfo.preferredFlags = 0;
2591 allocCreateInfo.memoryTypeBits = UINT32_MAX;
2592
2593 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002594 TEST(res == VK_SUCCESS);
2595 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002596 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2597
2598 // Required flags, preferred flags.
2599 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
2600 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2601 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
2602 allocCreateInfo.memoryTypeBits = 0;
2603
2604 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002605 TEST(res == VK_SUCCESS);
2606 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
2607 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002608 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2609
2610 // memoryTypeBits.
2611 const uint32_t memType = allocInfo.memoryType;
2612 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2613 allocCreateInfo.requiredFlags = 0;
2614 allocCreateInfo.preferredFlags = 0;
2615 allocCreateInfo.memoryTypeBits = 1u << memType;
2616
2617 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002618 TEST(res == VK_SUCCESS);
2619 TEST(allocInfo.memoryType == memType);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002620 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2621
2622}
2623
Adam Sawickia1d992f2020-03-02 15:32:10 +01002624static void TestGetAllocatorInfo()
2625{
2626 wprintf(L"Test vnaGetAllocatorInfo\n");
2627
2628 VmaAllocatorInfo allocInfo = {};
2629 vmaGetAllocatorInfo(g_hAllocator, &allocInfo);
2630 TEST(allocInfo.instance == g_hVulkanInstance);
2631 TEST(allocInfo.physicalDevice == g_hPhysicalDevice);
2632 TEST(allocInfo.device == g_hDevice);
2633}
2634
Adam Sawickib8333fb2018-03-13 16:15:53 +01002635static void TestBasics()
2636{
Adam Sawickiaaa1a562020-06-24 17:41:09 +02002637 wprintf(L"Test basics\n");
2638
Adam Sawickib8333fb2018-03-13 16:15:53 +01002639 VkResult res;
2640
Adam Sawickia1d992f2020-03-02 15:32:10 +01002641 TestGetAllocatorInfo();
2642
Adam Sawickib8333fb2018-03-13 16:15:53 +01002643 TestMemoryRequirements();
2644
2645 // Lost allocation
2646 {
2647 VmaAllocation alloc = VK_NULL_HANDLE;
2648 vmaCreateLostAllocation(g_hAllocator, &alloc);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002649 TEST(alloc != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002650
2651 VmaAllocationInfo allocInfo;
2652 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002653 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
2654 TEST(allocInfo.size == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002655
2656 vmaFreeMemory(g_hAllocator, alloc);
2657 }
2658
2659 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
2660 {
2661 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2662 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
2663 bufCreateInfo.size = 128;
2664
2665 VmaAllocationCreateInfo allocCreateInfo = {};
2666 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2667 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2668
2669 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2670 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002671 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002672
2673 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2674
2675 // Same with OWN_MEMORY.
2676 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2677
2678 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002679 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002680
2681 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2682 }
2683
2684 TestUserData();
Adam Sawicki370ab182018-11-08 16:31:00 +01002685
2686 TestInvalidAllocations();
Adam Sawickib8333fb2018-03-13 16:15:53 +01002687}
2688
Adam Sawickiaaa1a562020-06-24 17:41:09 +02002689static void TestAllocationVersusResourceSize()
2690{
2691 wprintf(L"Test allocation versus resource size\n");
2692
2693 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2694 bufCreateInfo.size = 22921; // Prime number
2695 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2696
2697 VmaAllocationCreateInfo allocCreateInfo = {};
2698 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2699
2700 for(uint32_t i = 0; i < 2; ++i)
2701 {
2702 allocCreateInfo.flags = (i == 1) ? VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT : 0;
2703
2704 AllocInfo info;
2705 info.CreateBuffer(bufCreateInfo, allocCreateInfo);
2706
2707 VmaAllocationInfo allocInfo = {};
2708 vmaGetAllocationInfo(g_hAllocator, info.m_Allocation, &allocInfo);
2709 //wprintf(L" Buffer size = %llu, allocation size = %llu\n", bufCreateInfo.size, allocInfo.size);
2710
2711 // Map and test accessing entire area of the allocation, not only the buffer.
2712 void* mappedPtr = nullptr;
2713 VkResult res = vmaMapMemory(g_hAllocator, info.m_Allocation, &mappedPtr);
2714 TEST(res == VK_SUCCESS);
2715
2716 memset(mappedPtr, 0xCC, (size_t)allocInfo.size);
2717
2718 vmaUnmapMemory(g_hAllocator, info.m_Allocation);
2719
2720 info.Destroy();
2721 }
2722}
2723
Adam Sawickiddcbf8c2019-11-22 15:22:42 +01002724static void TestPool_MinBlockCount()
2725{
2726#if defined(VMA_DEBUG_MARGIN) && VMA_DEBUG_MARGIN > 0
2727 return;
2728#endif
2729
2730 wprintf(L"Test Pool MinBlockCount\n");
2731 VkResult res;
2732
2733 static const VkDeviceSize ALLOC_SIZE = 512ull * 1024;
2734 static const VkDeviceSize BLOCK_SIZE = ALLOC_SIZE * 2; // Each block can fit 2 allocations.
2735
2736 VmaAllocationCreateInfo allocCreateInfo = {};
2737 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_COPY;
2738
2739 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2740 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2741 bufCreateInfo.size = ALLOC_SIZE;
2742
2743 VmaPoolCreateInfo poolCreateInfo = {};
2744 poolCreateInfo.blockSize = BLOCK_SIZE;
2745 poolCreateInfo.minBlockCount = 2; // At least 2 blocks always present.
2746 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2747 TEST(res == VK_SUCCESS);
2748
2749 VmaPool pool = VK_NULL_HANDLE;
2750 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2751 TEST(res == VK_SUCCESS && pool != VK_NULL_HANDLE);
2752
2753 // Check that there are 2 blocks preallocated as requested.
2754 VmaPoolStats begPoolStats = {};
2755 vmaGetPoolStats(g_hAllocator, pool, &begPoolStats);
2756 TEST(begPoolStats.blockCount == 2 && begPoolStats.allocationCount == 0 && begPoolStats.size == BLOCK_SIZE * 2);
2757
2758 // Allocate 5 buffers to create 3 blocks.
2759 static const uint32_t BUF_COUNT = 5;
2760 allocCreateInfo.pool = pool;
2761 std::vector<AllocInfo> allocs(BUF_COUNT);
2762 for(uint32_t i = 0; i < BUF_COUNT; ++i)
2763 {
2764 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &allocs[i].m_Buffer, &allocs[i].m_Allocation, nullptr);
2765 TEST(res == VK_SUCCESS && allocs[i].m_Buffer != VK_NULL_HANDLE && allocs[i].m_Allocation != VK_NULL_HANDLE);
2766 }
2767
2768 // Check that there are really 3 blocks.
2769 VmaPoolStats poolStats2 = {};
2770 vmaGetPoolStats(g_hAllocator, pool, &poolStats2);
2771 TEST(poolStats2.blockCount == 3 && poolStats2.allocationCount == BUF_COUNT && poolStats2.size == BLOCK_SIZE * 3);
2772
2773 // Free two first allocations to make one block empty.
2774 allocs[0].Destroy();
2775 allocs[1].Destroy();
2776
2777 // Check that there are still 3 blocks due to hysteresis.
2778 VmaPoolStats poolStats3 = {};
2779 vmaGetPoolStats(g_hAllocator, pool, &poolStats3);
2780 TEST(poolStats3.blockCount == 3 && poolStats3.allocationCount == BUF_COUNT - 2 && poolStats2.size == BLOCK_SIZE * 3);
2781
2782 // Free the last allocation to make second block empty.
2783 allocs[BUF_COUNT - 1].Destroy();
2784
2785 // Check that there are now 2 blocks only.
2786 VmaPoolStats poolStats4 = {};
2787 vmaGetPoolStats(g_hAllocator, pool, &poolStats4);
2788 TEST(poolStats4.blockCount == 2 && poolStats4.allocationCount == BUF_COUNT - 3 && poolStats4.size == BLOCK_SIZE * 2);
2789
2790 // Cleanup.
2791 for(size_t i = allocs.size(); i--; )
2792 {
2793 allocs[i].Destroy();
2794 }
2795 vmaDestroyPool(g_hAllocator, pool);
2796}
2797
Adam Sawickib8333fb2018-03-13 16:15:53 +01002798void TestHeapSizeLimit()
2799{
Adam Sawickifbaccff2020-03-09 17:09:23 +01002800 const VkDeviceSize HEAP_SIZE_LIMIT = 100ull * 1024 * 1024; // 100 MB
2801 const VkDeviceSize BLOCK_SIZE = 10ull * 1024 * 1024; // 10 MB
Adam Sawickib8333fb2018-03-13 16:15:53 +01002802
2803 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
2804 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
2805 {
2806 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
2807 }
2808
2809 VmaAllocatorCreateInfo allocatorCreateInfo = {};
2810 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
2811 allocatorCreateInfo.device = g_hDevice;
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002812 allocatorCreateInfo.instance = g_hVulkanInstance;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002813 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
2814
2815 VmaAllocator hAllocator;
2816 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002817 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002818
2819 struct Item
2820 {
2821 VkBuffer hBuf;
2822 VmaAllocation hAlloc;
2823 };
2824 std::vector<Item> items;
2825
2826 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2827 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2828
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002829 // 1. Allocate two blocks of dedicated memory, half the size of BLOCK_SIZE.
2830 VmaAllocationInfo dedicatedAllocInfo;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002831 {
2832 VmaAllocationCreateInfo allocCreateInfo = {};
2833 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2834 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2835
2836 bufCreateInfo.size = BLOCK_SIZE / 2;
2837
2838 for(size_t i = 0; i < 2; ++i)
2839 {
2840 Item item;
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002841 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &dedicatedAllocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002842 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002843 items.push_back(item);
2844 }
2845 }
2846
2847 // Create pool to make sure allocations must be out of this memory type.
2848 VmaPoolCreateInfo poolCreateInfo = {};
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002849 poolCreateInfo.memoryTypeIndex = dedicatedAllocInfo.memoryType;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002850 poolCreateInfo.blockSize = BLOCK_SIZE;
2851
2852 VmaPool hPool;
2853 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002854 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002855
2856 // 2. Allocate normal buffers from all the remaining memory.
2857 {
2858 VmaAllocationCreateInfo allocCreateInfo = {};
2859 allocCreateInfo.pool = hPool;
2860
2861 bufCreateInfo.size = BLOCK_SIZE / 2;
2862
2863 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
2864 for(size_t i = 0; i < bufCount; ++i)
2865 {
2866 Item item;
2867 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002868 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002869 items.push_back(item);
2870 }
2871 }
2872
2873 // 3. Allocation of one more (even small) buffer should fail.
2874 {
2875 VmaAllocationCreateInfo allocCreateInfo = {};
2876 allocCreateInfo.pool = hPool;
2877
2878 bufCreateInfo.size = 128;
2879
2880 VkBuffer hBuf;
2881 VmaAllocation hAlloc;
2882 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002883 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002884 }
2885
2886 // Destroy everything.
2887 for(size_t i = items.size(); i--; )
2888 {
2889 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
2890 }
2891
2892 vmaDestroyPool(hAllocator, hPool);
2893
2894 vmaDestroyAllocator(hAllocator);
2895}
2896
Adam Sawicki212a4a62018-06-14 15:44:45 +02002897#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02002898static void TestDebugMargin()
2899{
2900 if(VMA_DEBUG_MARGIN == 0)
2901 {
2902 return;
2903 }
2904
2905 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02002906 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02002907
2908 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02002909 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02002910
2911 // Create few buffers of different size.
2912 const size_t BUF_COUNT = 10;
2913 BufferInfo buffers[BUF_COUNT];
2914 VmaAllocationInfo allocInfo[BUF_COUNT];
2915 for(size_t i = 0; i < 10; ++i)
2916 {
2917 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02002918 // Last one will be mapped.
2919 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02002920
2921 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002922 TEST(res == VK_SUCCESS);
Adam Sawicki73b16652018-06-11 16:39:25 +02002923 // Margin is preserved also at the beginning of a block.
Adam Sawickib8d34d52018-10-03 17:41:20 +02002924 TEST(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002925
2926 if(i == BUF_COUNT - 1)
2927 {
2928 // Fill with data.
Adam Sawickib8d34d52018-10-03 17:41:20 +02002929 TEST(allocInfo[i].pMappedData != nullptr);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002930 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
2931 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
2932 }
Adam Sawicki73b16652018-06-11 16:39:25 +02002933 }
2934
2935 // Check if their offsets preserve margin between them.
2936 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
2937 {
2938 if(lhs.deviceMemory != rhs.deviceMemory)
2939 {
2940 return lhs.deviceMemory < rhs.deviceMemory;
2941 }
2942 return lhs.offset < rhs.offset;
2943 });
2944 for(size_t i = 1; i < BUF_COUNT; ++i)
2945 {
2946 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
2947 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02002948 TEST(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
Adam Sawicki73b16652018-06-11 16:39:25 +02002949 }
2950 }
2951
Adam Sawicki212a4a62018-06-14 15:44:45 +02002952 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002953 TEST(res == VK_SUCCESS);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002954
Adam Sawicki73b16652018-06-11 16:39:25 +02002955 // Destroy all buffers.
2956 for(size_t i = BUF_COUNT; i--; )
2957 {
2958 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
2959 }
2960}
Adam Sawicki212a4a62018-06-14 15:44:45 +02002961#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02002962
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002963static void TestLinearAllocator()
2964{
2965 wprintf(L"Test linear allocator\n");
2966
2967 RandomNumberGenerator rand{645332};
2968
2969 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2970 sampleBufCreateInfo.size = 1024; // Whatever.
2971 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2972
2973 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2974 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2975
2976 VmaPoolCreateInfo poolCreateInfo = {};
2977 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002978 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002979
Adam Sawickiee082772018-06-20 17:45:49 +02002980 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002981 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2982 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2983
2984 VmaPool pool = nullptr;
2985 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002986 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002987
2988 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
2989
2990 VmaAllocationCreateInfo allocCreateInfo = {};
2991 allocCreateInfo.pool = pool;
2992
2993 constexpr size_t maxBufCount = 100;
2994 std::vector<BufferInfo> bufInfo;
2995
2996 constexpr VkDeviceSize bufSizeMin = 16;
2997 constexpr VkDeviceSize bufSizeMax = 1024;
2998 VmaAllocationInfo allocInfo;
2999 VkDeviceSize prevOffset = 0;
3000
3001 // Test one-time free.
3002 for(size_t i = 0; i < 2; ++i)
3003 {
3004 // Allocate number of buffers of varying size that surely fit into this block.
3005 VkDeviceSize bufSumSize = 0;
3006 for(size_t i = 0; i < maxBufCount; ++i)
3007 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003008 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003009 BufferInfo newBufInfo;
3010 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3011 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003012 TEST(res == VK_SUCCESS);
3013 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003014 bufInfo.push_back(newBufInfo);
3015 prevOffset = allocInfo.offset;
3016 bufSumSize += bufCreateInfo.size;
3017 }
3018
3019 // Validate pool stats.
3020 VmaPoolStats stats;
3021 vmaGetPoolStats(g_hAllocator, pool, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003022 TEST(stats.size == poolCreateInfo.blockSize);
3023 TEST(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
3024 TEST(stats.allocationCount == bufInfo.size());
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003025
3026 // Destroy the buffers in random order.
3027 while(!bufInfo.empty())
3028 {
3029 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3030 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3031 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3032 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3033 }
3034 }
3035
3036 // Test stack.
3037 {
3038 // Allocate number of buffers of varying size that surely fit into this block.
3039 for(size_t i = 0; i < maxBufCount; ++i)
3040 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003041 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003042 BufferInfo newBufInfo;
3043 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3044 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003045 TEST(res == VK_SUCCESS);
3046 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003047 bufInfo.push_back(newBufInfo);
3048 prevOffset = allocInfo.offset;
3049 }
3050
3051 // Destroy few buffers from top of the stack.
3052 for(size_t i = 0; i < maxBufCount / 5; ++i)
3053 {
3054 const BufferInfo& currBufInfo = bufInfo.back();
3055 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3056 bufInfo.pop_back();
3057 }
3058
3059 // Create some more
3060 for(size_t i = 0; i < maxBufCount / 5; ++i)
3061 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003062 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003063 BufferInfo newBufInfo;
3064 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3065 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003066 TEST(res == VK_SUCCESS);
3067 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003068 bufInfo.push_back(newBufInfo);
3069 prevOffset = allocInfo.offset;
3070 }
3071
3072 // Destroy the buffers in reverse order.
3073 while(!bufInfo.empty())
3074 {
3075 const BufferInfo& currBufInfo = bufInfo.back();
3076 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3077 bufInfo.pop_back();
3078 }
3079 }
3080
Adam Sawickiee082772018-06-20 17:45:49 +02003081 // Test ring buffer.
3082 {
3083 // Allocate number of buffers that surely fit into this block.
3084 bufCreateInfo.size = bufSizeMax;
3085 for(size_t i = 0; i < maxBufCount; ++i)
3086 {
3087 BufferInfo newBufInfo;
3088 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3089 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003090 TEST(res == VK_SUCCESS);
3091 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawickiee082772018-06-20 17:45:49 +02003092 bufInfo.push_back(newBufInfo);
3093 prevOffset = allocInfo.offset;
3094 }
3095
3096 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
3097 const size_t buffersPerIter = maxBufCount / 10 - 1;
3098 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
3099 for(size_t iter = 0; iter < iterCount; ++iter)
3100 {
3101 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
3102 {
3103 const BufferInfo& currBufInfo = bufInfo.front();
3104 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3105 bufInfo.erase(bufInfo.begin());
3106 }
3107 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
3108 {
3109 BufferInfo newBufInfo;
3110 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3111 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003112 TEST(res == VK_SUCCESS);
Adam Sawickiee082772018-06-20 17:45:49 +02003113 bufInfo.push_back(newBufInfo);
3114 }
3115 }
3116
3117 // Allocate buffers until we reach out-of-memory.
3118 uint32_t debugIndex = 0;
3119 while(res == VK_SUCCESS)
3120 {
3121 BufferInfo newBufInfo;
3122 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3123 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3124 if(res == VK_SUCCESS)
3125 {
3126 bufInfo.push_back(newBufInfo);
3127 }
3128 else
3129 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003130 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickiee082772018-06-20 17:45:49 +02003131 }
3132 ++debugIndex;
3133 }
3134
3135 // Destroy the buffers in random order.
3136 while(!bufInfo.empty())
3137 {
3138 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3139 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3140 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3141 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3142 }
3143 }
3144
Adam Sawicki680b2252018-08-22 14:47:32 +02003145 // Test double stack.
3146 {
3147 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
3148 VkDeviceSize prevOffsetLower = 0;
3149 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
3150 for(size_t i = 0; i < maxBufCount; ++i)
3151 {
3152 const bool upperAddress = (i % 2) != 0;
3153 if(upperAddress)
3154 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3155 else
3156 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003157 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003158 BufferInfo newBufInfo;
3159 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3160 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003161 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02003162 if(upperAddress)
3163 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003164 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003165 prevOffsetUpper = allocInfo.offset;
3166 }
3167 else
3168 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003169 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02003170 prevOffsetLower = allocInfo.offset;
3171 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003172 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003173 bufInfo.push_back(newBufInfo);
3174 }
3175
3176 // Destroy few buffers from top of the stack.
3177 for(size_t i = 0; i < maxBufCount / 5; ++i)
3178 {
3179 const BufferInfo& currBufInfo = bufInfo.back();
3180 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3181 bufInfo.pop_back();
3182 }
3183
3184 // Create some more
3185 for(size_t i = 0; i < maxBufCount / 5; ++i)
3186 {
3187 const bool upperAddress = (i % 2) != 0;
3188 if(upperAddress)
3189 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3190 else
3191 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003192 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003193 BufferInfo newBufInfo;
3194 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3195 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003196 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02003197 bufInfo.push_back(newBufInfo);
3198 }
3199
3200 // Destroy the buffers in reverse order.
3201 while(!bufInfo.empty())
3202 {
3203 const BufferInfo& currBufInfo = bufInfo.back();
3204 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3205 bufInfo.pop_back();
3206 }
3207
3208 // Create buffers on both sides until we reach out of memory.
3209 prevOffsetLower = 0;
3210 prevOffsetUpper = poolCreateInfo.blockSize;
3211 res = VK_SUCCESS;
3212 for(size_t i = 0; res == VK_SUCCESS; ++i)
3213 {
3214 const bool upperAddress = (i % 2) != 0;
3215 if(upperAddress)
3216 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3217 else
3218 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003219 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003220 BufferInfo newBufInfo;
3221 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3222 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3223 if(res == VK_SUCCESS)
3224 {
3225 if(upperAddress)
3226 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003227 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003228 prevOffsetUpper = allocInfo.offset;
3229 }
3230 else
3231 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003232 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02003233 prevOffsetLower = allocInfo.offset;
3234 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003235 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003236 bufInfo.push_back(newBufInfo);
3237 }
3238 }
3239
3240 // Destroy the buffers in random order.
3241 while(!bufInfo.empty())
3242 {
3243 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3244 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3245 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3246 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3247 }
3248
3249 // Create buffers on upper side only, constant size, until we reach out of memory.
3250 prevOffsetUpper = poolCreateInfo.blockSize;
3251 res = VK_SUCCESS;
3252 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3253 bufCreateInfo.size = bufSizeMax;
3254 for(size_t i = 0; res == VK_SUCCESS; ++i)
3255 {
3256 BufferInfo newBufInfo;
3257 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3258 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3259 if(res == VK_SUCCESS)
3260 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003261 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003262 prevOffsetUpper = allocInfo.offset;
3263 bufInfo.push_back(newBufInfo);
3264 }
3265 }
3266
3267 // Destroy the buffers in reverse order.
3268 while(!bufInfo.empty())
3269 {
3270 const BufferInfo& currBufInfo = bufInfo.back();
3271 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3272 bufInfo.pop_back();
3273 }
3274 }
3275
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003276 // Test ring buffer with lost allocations.
3277 {
3278 // Allocate number of buffers until pool is full.
3279 // Notice CAN_BECOME_LOST flag and call to vmaSetCurrentFrameIndex.
3280 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT;
3281 res = VK_SUCCESS;
3282 for(size_t i = 0; res == VK_SUCCESS; ++i)
3283 {
3284 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3285
Adam Sawickifd366b62019-01-24 15:26:43 +01003286 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003287
3288 BufferInfo newBufInfo;
3289 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3290 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3291 if(res == VK_SUCCESS)
3292 bufInfo.push_back(newBufInfo);
3293 }
3294
3295 // Free first half of it.
3296 {
3297 const size_t buffersToDelete = bufInfo.size() / 2;
3298 for(size_t i = 0; i < buffersToDelete; ++i)
3299 {
3300 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3301 }
3302 bufInfo.erase(bufInfo.begin(), bufInfo.begin() + buffersToDelete);
3303 }
3304
3305 // Allocate number of buffers until pool is full again.
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003306 // This way we make sure ring buffers wraps around, front in in the middle.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003307 res = VK_SUCCESS;
3308 for(size_t i = 0; res == VK_SUCCESS; ++i)
3309 {
3310 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3311
Adam Sawickifd366b62019-01-24 15:26:43 +01003312 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003313
3314 BufferInfo newBufInfo;
3315 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3316 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3317 if(res == VK_SUCCESS)
3318 bufInfo.push_back(newBufInfo);
3319 }
3320
3321 VkDeviceSize firstNewOffset;
3322 {
3323 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3324
3325 // Allocate a large buffer with CAN_MAKE_OTHER_LOST.
3326 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
3327 bufCreateInfo.size = bufSizeMax;
3328
3329 BufferInfo newBufInfo;
3330 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3331 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003332 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003333 bufInfo.push_back(newBufInfo);
3334 firstNewOffset = allocInfo.offset;
3335
3336 // Make sure at least one buffer from the beginning became lost.
3337 vmaGetAllocationInfo(g_hAllocator, bufInfo[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003338 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003339 }
3340
Adam Sawickifd366b62019-01-24 15:26:43 +01003341#if 0 // TODO Fix and uncomment. Failing on Intel.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003342 // Allocate more buffers that CAN_MAKE_OTHER_LOST until we wrap-around with this.
3343 size_t newCount = 1;
3344 for(;;)
3345 {
3346 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3347
Adam Sawickifd366b62019-01-24 15:26:43 +01003348 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003349
3350 BufferInfo newBufInfo;
3351 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3352 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickifd366b62019-01-24 15:26:43 +01003353
Adam Sawickib8d34d52018-10-03 17:41:20 +02003354 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003355 bufInfo.push_back(newBufInfo);
3356 ++newCount;
3357 if(allocInfo.offset < firstNewOffset)
3358 break;
3359 }
Adam Sawickifd366b62019-01-24 15:26:43 +01003360#endif
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003361
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003362 // Delete buffers that are lost.
3363 for(size_t i = bufInfo.size(); i--; )
3364 {
3365 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
3366 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3367 {
3368 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3369 bufInfo.erase(bufInfo.begin() + i);
3370 }
3371 }
3372
3373 // Test vmaMakePoolAllocationsLost
3374 {
3375 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3376
Adam Sawicki4d35a5d2019-01-24 15:51:59 +01003377 size_t lostAllocCount = 0;
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003378 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostAllocCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003379 TEST(lostAllocCount > 0);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003380
3381 size_t realLostAllocCount = 0;
3382 for(size_t i = 0; i < bufInfo.size(); ++i)
3383 {
3384 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
3385 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3386 ++realLostAllocCount;
3387 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003388 TEST(realLostAllocCount == lostAllocCount);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003389 }
3390
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003391 // Destroy all the buffers in forward order.
3392 for(size_t i = 0; i < bufInfo.size(); ++i)
3393 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3394 bufInfo.clear();
3395 }
3396
Adam Sawicki70a683e2018-08-24 15:36:32 +02003397 vmaDestroyPool(g_hAllocator, pool);
3398}
Adam Sawickif799c4f2018-08-23 10:40:30 +02003399
Adam Sawicki70a683e2018-08-24 15:36:32 +02003400static void TestLinearAllocatorMultiBlock()
3401{
3402 wprintf(L"Test linear allocator multi block\n");
3403
3404 RandomNumberGenerator rand{345673};
3405
3406 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3407 sampleBufCreateInfo.size = 1024 * 1024;
3408 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3409
3410 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3411 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3412
3413 VmaPoolCreateInfo poolCreateInfo = {};
3414 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3415 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003416 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003417
3418 VmaPool pool = nullptr;
3419 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003420 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003421
3422 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
3423
3424 VmaAllocationCreateInfo allocCreateInfo = {};
3425 allocCreateInfo.pool = pool;
3426
3427 std::vector<BufferInfo> bufInfo;
3428 VmaAllocationInfo allocInfo;
3429
3430 // Test one-time free.
3431 {
3432 // Allocate buffers until we move to a second block.
3433 VkDeviceMemory lastMem = VK_NULL_HANDLE;
3434 for(uint32_t i = 0; ; ++i)
3435 {
3436 BufferInfo newBufInfo;
3437 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3438 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003439 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003440 bufInfo.push_back(newBufInfo);
3441 if(lastMem && allocInfo.deviceMemory != lastMem)
3442 {
3443 break;
3444 }
3445 lastMem = allocInfo.deviceMemory;
3446 }
3447
Adam Sawickib8d34d52018-10-03 17:41:20 +02003448 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003449
3450 // Make sure that pool has now two blocks.
3451 VmaPoolStats poolStats = {};
3452 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003453 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003454
3455 // Destroy all the buffers in random order.
3456 while(!bufInfo.empty())
3457 {
3458 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3459 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3460 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3461 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3462 }
3463
3464 // Make sure that pool has now at most one block.
3465 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003466 TEST(poolStats.blockCount <= 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003467 }
3468
3469 // Test stack.
3470 {
3471 // Allocate buffers until we move to a second block.
3472 VkDeviceMemory lastMem = VK_NULL_HANDLE;
3473 for(uint32_t i = 0; ; ++i)
3474 {
3475 BufferInfo newBufInfo;
3476 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3477 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003478 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003479 bufInfo.push_back(newBufInfo);
3480 if(lastMem && allocInfo.deviceMemory != lastMem)
3481 {
3482 break;
3483 }
3484 lastMem = allocInfo.deviceMemory;
3485 }
3486
Adam Sawickib8d34d52018-10-03 17:41:20 +02003487 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003488
3489 // Add few more buffers.
3490 for(uint32_t i = 0; i < 5; ++i)
3491 {
3492 BufferInfo newBufInfo;
3493 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3494 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003495 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003496 bufInfo.push_back(newBufInfo);
3497 }
3498
3499 // Make sure that pool has now two blocks.
3500 VmaPoolStats poolStats = {};
3501 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003502 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003503
3504 // Delete half of buffers, LIFO.
3505 for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
3506 {
3507 const BufferInfo& currBufInfo = bufInfo.back();
3508 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3509 bufInfo.pop_back();
3510 }
3511
3512 // Add one more buffer.
3513 BufferInfo newBufInfo;
3514 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3515 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003516 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003517 bufInfo.push_back(newBufInfo);
3518
3519 // Make sure that pool has now one block.
3520 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003521 TEST(poolStats.blockCount == 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003522
3523 // Delete all the remaining buffers, LIFO.
3524 while(!bufInfo.empty())
3525 {
3526 const BufferInfo& currBufInfo = bufInfo.back();
3527 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3528 bufInfo.pop_back();
3529 }
Adam Sawickif799c4f2018-08-23 10:40:30 +02003530 }
3531
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003532 vmaDestroyPool(g_hAllocator, pool);
3533}
3534
Adam Sawickifd11d752018-08-22 15:02:10 +02003535static void ManuallyTestLinearAllocator()
3536{
3537 VmaStats origStats;
3538 vmaCalculateStats(g_hAllocator, &origStats);
3539
3540 wprintf(L"Manually test linear allocator\n");
3541
3542 RandomNumberGenerator rand{645332};
3543
3544 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3545 sampleBufCreateInfo.size = 1024; // Whatever.
3546 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3547
3548 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3549 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3550
3551 VmaPoolCreateInfo poolCreateInfo = {};
3552 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003553 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003554
3555 poolCreateInfo.blockSize = 10 * 1024;
3556 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3557 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
3558
3559 VmaPool pool = nullptr;
3560 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003561 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003562
3563 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
3564
3565 VmaAllocationCreateInfo allocCreateInfo = {};
3566 allocCreateInfo.pool = pool;
3567
3568 std::vector<BufferInfo> bufInfo;
3569 VmaAllocationInfo allocInfo;
3570 BufferInfo newBufInfo;
3571
3572 // Test double stack.
3573 {
3574 /*
3575 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
3576 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
3577
3578 Totally:
3579 1 block allocated
3580 10240 Vulkan bytes
3581 6 new allocations
3582 2256 bytes in allocations
3583 */
3584
3585 bufCreateInfo.size = 32;
3586 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3587 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003588 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003589 bufInfo.push_back(newBufInfo);
3590
3591 bufCreateInfo.size = 1024;
3592 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3593 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003594 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003595 bufInfo.push_back(newBufInfo);
3596
3597 bufCreateInfo.size = 32;
3598 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3599 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003600 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003601 bufInfo.push_back(newBufInfo);
3602
3603 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3604
3605 bufCreateInfo.size = 128;
3606 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3607 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003608 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003609 bufInfo.push_back(newBufInfo);
3610
3611 bufCreateInfo.size = 1024;
3612 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3613 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003614 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003615 bufInfo.push_back(newBufInfo);
3616
3617 bufCreateInfo.size = 16;
3618 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3619 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003620 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003621 bufInfo.push_back(newBufInfo);
3622
3623 VmaStats currStats;
3624 vmaCalculateStats(g_hAllocator, &currStats);
3625 VmaPoolStats poolStats;
3626 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
3627
3628 char* statsStr = nullptr;
3629 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
3630
3631 // PUT BREAKPOINT HERE TO CHECK.
3632 // Inspect: currStats versus origStats, poolStats, statsStr.
3633 int I = 0;
3634
3635 vmaFreeStatsString(g_hAllocator, statsStr);
3636
3637 // Destroy the buffers in reverse order.
3638 while(!bufInfo.empty())
3639 {
3640 const BufferInfo& currBufInfo = bufInfo.back();
3641 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3642 bufInfo.pop_back();
3643 }
3644 }
3645
3646 vmaDestroyPool(g_hAllocator, pool);
3647}
3648
Adam Sawicki80927152018-09-07 17:27:23 +02003649static void BenchmarkAlgorithmsCase(FILE* file,
3650 uint32_t algorithm,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003651 bool empty,
3652 VmaAllocationCreateFlags allocStrategy,
3653 FREE_ORDER freeOrder)
Adam Sawicki0a607132018-08-24 11:18:41 +02003654{
3655 RandomNumberGenerator rand{16223};
3656
3657 const VkDeviceSize bufSizeMin = 32;
3658 const VkDeviceSize bufSizeMax = 1024;
3659 const size_t maxBufCapacity = 10000;
3660 const uint32_t iterationCount = 10;
3661
3662 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3663 sampleBufCreateInfo.size = bufSizeMax;
3664 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3665
3666 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3667 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3668
3669 VmaPoolCreateInfo poolCreateInfo = {};
3670 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003671 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003672
3673 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
Adam Sawicki80927152018-09-07 17:27:23 +02003674 poolCreateInfo.flags |= algorithm;
Adam Sawicki0a607132018-08-24 11:18:41 +02003675 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
3676
3677 VmaPool pool = nullptr;
3678 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003679 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003680
3681 // Buffer created just to get memory requirements. Never bound to any memory.
3682 VkBuffer dummyBuffer = VK_NULL_HANDLE;
Adam Sawicki1f84f622019-07-02 13:40:01 +02003683 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003684 TEST(res == VK_SUCCESS && dummyBuffer);
Adam Sawicki0a607132018-08-24 11:18:41 +02003685
3686 VkMemoryRequirements memReq = {};
3687 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3688
Adam Sawicki1f84f622019-07-02 13:40:01 +02003689 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawicki0a607132018-08-24 11:18:41 +02003690
3691 VmaAllocationCreateInfo allocCreateInfo = {};
3692 allocCreateInfo.pool = pool;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003693 allocCreateInfo.flags = allocStrategy;
Adam Sawicki0a607132018-08-24 11:18:41 +02003694
3695 VmaAllocation alloc;
3696 std::vector<VmaAllocation> baseAllocations;
3697
3698 if(!empty)
3699 {
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003700 // Make allocations up to 1/3 of pool size.
Adam Sawicki0a607132018-08-24 11:18:41 +02003701 VkDeviceSize totalSize = 0;
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003702 while(totalSize < poolCreateInfo.blockSize / 3)
Adam Sawicki0a607132018-08-24 11:18:41 +02003703 {
Adam Sawicki4d844e22019-01-24 16:21:05 +01003704 // This test intentionally allows sizes that are aligned to 4 or 16 bytes.
3705 // This is theoretically allowed and already uncovered one bug.
Adam Sawicki0a607132018-08-24 11:18:41 +02003706 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
3707 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003708 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003709 baseAllocations.push_back(alloc);
3710 totalSize += memReq.size;
3711 }
3712
3713 // Delete half of them, choose randomly.
3714 size_t allocsToDelete = baseAllocations.size() / 2;
3715 for(size_t i = 0; i < allocsToDelete; ++i)
3716 {
3717 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
3718 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
3719 baseAllocations.erase(baseAllocations.begin() + index);
3720 }
3721 }
3722
3723 // BENCHMARK
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003724 const size_t allocCount = maxBufCapacity / 3;
Adam Sawicki0a607132018-08-24 11:18:41 +02003725 std::vector<VmaAllocation> testAllocations;
3726 testAllocations.reserve(allocCount);
3727 duration allocTotalDuration = duration::zero();
3728 duration freeTotalDuration = duration::zero();
3729 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
3730 {
3731 // Allocations
3732 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
3733 for(size_t i = 0; i < allocCount; ++i)
3734 {
3735 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
3736 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003737 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003738 testAllocations.push_back(alloc);
3739 }
3740 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
3741
3742 // Deallocations
3743 switch(freeOrder)
3744 {
3745 case FREE_ORDER::FORWARD:
3746 // Leave testAllocations unchanged.
3747 break;
3748 case FREE_ORDER::BACKWARD:
3749 std::reverse(testAllocations.begin(), testAllocations.end());
3750 break;
3751 case FREE_ORDER::RANDOM:
3752 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
3753 break;
3754 default: assert(0);
3755 }
3756
3757 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
3758 for(size_t i = 0; i < allocCount; ++i)
3759 vmaFreeMemory(g_hAllocator, testAllocations[i]);
3760 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
3761
3762 testAllocations.clear();
3763 }
3764
3765 // Delete baseAllocations
3766 while(!baseAllocations.empty())
3767 {
3768 vmaFreeMemory(g_hAllocator, baseAllocations.back());
3769 baseAllocations.pop_back();
3770 }
3771
3772 vmaDestroyPool(g_hAllocator, pool);
3773
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003774 const float allocTotalSeconds = ToFloatSeconds(allocTotalDuration);
3775 const float freeTotalSeconds = ToFloatSeconds(freeTotalDuration);
3776
Adam Sawicki80927152018-09-07 17:27:23 +02003777 printf(" Algorithm=%s %s Allocation=%s FreeOrder=%s: allocations %g s, free %g s\n",
3778 AlgorithmToStr(algorithm),
Adam Sawicki0667e332018-08-24 17:26:44 +02003779 empty ? "Empty" : "Not empty",
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003780 GetAllocationStrategyName(allocStrategy),
Adam Sawicki0a607132018-08-24 11:18:41 +02003781 FREE_ORDER_NAMES[(size_t)freeOrder],
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003782 allocTotalSeconds,
3783 freeTotalSeconds);
3784
3785 if(file)
3786 {
3787 std::string currTime;
3788 CurrentTimeToStr(currTime);
3789
Adam Sawicki80927152018-09-07 17:27:23 +02003790 fprintf(file, "%s,%s,%s,%u,%s,%s,%g,%g\n",
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003791 CODE_DESCRIPTION, currTime.c_str(),
Adam Sawicki80927152018-09-07 17:27:23 +02003792 AlgorithmToStr(algorithm),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003793 empty ? 1 : 0,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003794 GetAllocationStrategyName(allocStrategy),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003795 FREE_ORDER_NAMES[(uint32_t)freeOrder],
3796 allocTotalSeconds,
3797 freeTotalSeconds);
3798 }
Adam Sawicki0a607132018-08-24 11:18:41 +02003799}
3800
Adam Sawickie73e9882020-03-20 18:05:42 +01003801static void TestBufferDeviceAddress()
3802{
3803 wprintf(L"Test buffer device address\n");
3804
Adam Sawicki0a3c6b52021-03-02 16:48:32 +01003805 assert(VK_KHR_buffer_device_address_enabled);
Adam Sawickie73e9882020-03-20 18:05:42 +01003806
3807 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3808 bufCreateInfo.size = 0x10000;
3809 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
3810 VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; // !!!
3811
3812 VmaAllocationCreateInfo allocCreateInfo = {};
3813 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3814
3815 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
3816 {
3817 // 1st is placed, 2nd is dedicated.
3818 if(testIndex == 1)
3819 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3820
3821 BufferInfo bufInfo = {};
3822 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3823 &bufInfo.Buffer, &bufInfo.Allocation, nullptr);
3824 TEST(res == VK_SUCCESS);
3825
3826 VkBufferDeviceAddressInfoEXT bufferDeviceAddressInfo = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT };
3827 bufferDeviceAddressInfo.buffer = bufInfo.Buffer;
Adam Sawicki0a3c6b52021-03-02 16:48:32 +01003828 TEST(g_vkGetBufferDeviceAddressKHR != nullptr);
3829 VkDeviceAddress addr = g_vkGetBufferDeviceAddressKHR(g_hDevice, &bufferDeviceAddressInfo);
3830 TEST(addr != 0);
Adam Sawickie73e9882020-03-20 18:05:42 +01003831
3832 vmaDestroyBuffer(g_hAllocator, bufInfo.Buffer, bufInfo.Allocation);
3833 }
3834}
3835
Adam Sawickif2012052021-01-11 18:04:42 +01003836static void TestMemoryPriority()
3837{
3838 wprintf(L"Test memory priority\n");
3839
3840 assert(VK_EXT_memory_priority_enabled);
3841
3842 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3843 bufCreateInfo.size = 0x10000;
3844 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3845
3846 VmaAllocationCreateInfo allocCreateInfo = {};
3847 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3848 allocCreateInfo.priority = 1.f;
3849
3850 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
3851 {
3852 // 1st is placed, 2nd is dedicated.
3853 if(testIndex == 1)
3854 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3855
3856 BufferInfo bufInfo = {};
3857 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3858 &bufInfo.Buffer, &bufInfo.Allocation, nullptr);
3859 TEST(res == VK_SUCCESS);
3860
3861 // There is nothing we can do to validate the priority.
3862
3863 vmaDestroyBuffer(g_hAllocator, bufInfo.Buffer, bufInfo.Allocation);
3864 }
3865}
3866
Adam Sawicki80927152018-09-07 17:27:23 +02003867static void BenchmarkAlgorithms(FILE* file)
Adam Sawicki0a607132018-08-24 11:18:41 +02003868{
Adam Sawicki80927152018-09-07 17:27:23 +02003869 wprintf(L"Benchmark algorithms\n");
Adam Sawicki0a607132018-08-24 11:18:41 +02003870
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003871 if(file)
3872 {
3873 fprintf(file,
3874 "Code,Time,"
Adam Sawicki80927152018-09-07 17:27:23 +02003875 "Algorithm,Empty,Allocation strategy,Free order,"
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003876 "Allocation time (s),Deallocation time (s)\n");
3877 }
3878
Adam Sawicki0a607132018-08-24 11:18:41 +02003879 uint32_t freeOrderCount = 1;
3880 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
3881 freeOrderCount = 3;
3882 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
3883 freeOrderCount = 2;
3884
3885 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003886 const uint32_t allocStrategyCount = GetAllocationStrategyCount();
Adam Sawicki0a607132018-08-24 11:18:41 +02003887
3888 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
3889 {
3890 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
3891 switch(freeOrderIndex)
3892 {
3893 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
3894 case 1: freeOrder = FREE_ORDER::FORWARD; break;
3895 case 2: freeOrder = FREE_ORDER::RANDOM; break;
3896 default: assert(0);
3897 }
3898
3899 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
3900 {
Adam Sawicki80927152018-09-07 17:27:23 +02003901 for(uint32_t algorithmIndex = 0; algorithmIndex < 3; ++algorithmIndex)
Adam Sawicki0a607132018-08-24 11:18:41 +02003902 {
Adam Sawicki80927152018-09-07 17:27:23 +02003903 uint32_t algorithm = 0;
3904 switch(algorithmIndex)
3905 {
3906 case 0:
3907 break;
3908 case 1:
3909 algorithm = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
3910 break;
3911 case 2:
3912 algorithm = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3913 break;
3914 default:
3915 assert(0);
3916 }
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003917
Adam Sawicki80927152018-09-07 17:27:23 +02003918 uint32_t currAllocStrategyCount = algorithm != 0 ? 1 : allocStrategyCount;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003919 for(uint32_t allocStrategyIndex = 0; allocStrategyIndex < currAllocStrategyCount; ++allocStrategyIndex)
3920 {
3921 VmaAllocatorCreateFlags strategy = 0;
Adam Sawicki80927152018-09-07 17:27:23 +02003922 if(currAllocStrategyCount > 1)
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003923 {
3924 switch(allocStrategyIndex)
3925 {
3926 case 0: strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; break;
3927 case 1: strategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT; break;
3928 case 2: strategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT; break;
3929 default: assert(0);
3930 }
3931 }
3932
Adam Sawicki80927152018-09-07 17:27:23 +02003933 BenchmarkAlgorithmsCase(
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003934 file,
Adam Sawicki80927152018-09-07 17:27:23 +02003935 algorithm,
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003936 (emptyIndex == 0), // empty
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003937 strategy,
3938 freeOrder); // freeOrder
3939 }
Adam Sawicki0a607132018-08-24 11:18:41 +02003940 }
3941 }
3942 }
3943}
3944
Adam Sawickib8333fb2018-03-13 16:15:53 +01003945static void TestPool_SameSize()
3946{
3947 const VkDeviceSize BUF_SIZE = 1024 * 1024;
3948 const size_t BUF_COUNT = 100;
3949 VkResult res;
3950
3951 RandomNumberGenerator rand{123};
3952
3953 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3954 bufferInfo.size = BUF_SIZE;
3955 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3956
3957 uint32_t memoryTypeBits = UINT32_MAX;
3958 {
3959 VkBuffer dummyBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +02003960 res = vkCreateBuffer(g_hDevice, &bufferInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003961 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003962
3963 VkMemoryRequirements memReq;
3964 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3965 memoryTypeBits = memReq.memoryTypeBits;
3966
Adam Sawicki1f84f622019-07-02 13:40:01 +02003967 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003968 }
3969
3970 VmaAllocationCreateInfo poolAllocInfo = {};
3971 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3972 uint32_t memTypeIndex;
3973 res = vmaFindMemoryTypeIndex(
3974 g_hAllocator,
3975 memoryTypeBits,
3976 &poolAllocInfo,
3977 &memTypeIndex);
3978
3979 VmaPoolCreateInfo poolCreateInfo = {};
3980 poolCreateInfo.memoryTypeIndex = memTypeIndex;
3981 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
3982 poolCreateInfo.minBlockCount = 1;
3983 poolCreateInfo.maxBlockCount = 4;
3984 poolCreateInfo.frameInUseCount = 0;
3985
3986 VmaPool pool;
3987 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003988 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003989
Adam Sawickia020fb82019-11-02 14:43:06 +01003990 // Test pool name
3991 {
3992 static const char* const POOL_NAME = "Pool name";
3993 vmaSetPoolName(g_hAllocator, pool, POOL_NAME);
3994
3995 const char* fetchedPoolName = nullptr;
3996 vmaGetPoolName(g_hAllocator, pool, &fetchedPoolName);
3997 TEST(strcmp(fetchedPoolName, POOL_NAME) == 0);
3998
Adam Sawickia020fb82019-11-02 14:43:06 +01003999 vmaSetPoolName(g_hAllocator, pool, nullptr);
4000 }
4001
Adam Sawickib8333fb2018-03-13 16:15:53 +01004002 vmaSetCurrentFrameIndex(g_hAllocator, 1);
4003
4004 VmaAllocationCreateInfo allocInfo = {};
4005 allocInfo.pool = pool;
4006 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
4007 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
4008
4009 struct BufItem
4010 {
4011 VkBuffer Buf;
4012 VmaAllocation Alloc;
4013 };
4014 std::vector<BufItem> items;
4015
4016 // Fill entire pool.
4017 for(size_t i = 0; i < BUF_COUNT; ++i)
4018 {
4019 BufItem item;
4020 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004021 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004022 items.push_back(item);
4023 }
4024
4025 // Make sure that another allocation would fail.
4026 {
4027 BufItem item;
4028 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004029 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004030 }
4031
4032 // Validate that no buffer is lost. Also check that they are not mapped.
4033 for(size_t i = 0; i < items.size(); ++i)
4034 {
4035 VmaAllocationInfo allocInfo;
4036 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004037 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
4038 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004039 }
4040
4041 // Free some percent of random items.
4042 {
4043 const size_t PERCENT_TO_FREE = 10;
4044 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
4045 for(size_t i = 0; i < itemsToFree; ++i)
4046 {
4047 size_t index = (size_t)rand.Generate() % items.size();
4048 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
4049 items.erase(items.begin() + index);
4050 }
4051 }
4052
4053 // Randomly allocate and free items.
4054 {
4055 const size_t OPERATION_COUNT = BUF_COUNT;
4056 for(size_t i = 0; i < OPERATION_COUNT; ++i)
4057 {
4058 bool allocate = rand.Generate() % 2 != 0;
4059 if(allocate)
4060 {
4061 if(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 else // Free
4070 {
4071 if(!items.empty())
4072 {
4073 size_t index = (size_t)rand.Generate() % items.size();
4074 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
4075 items.erase(items.begin() + index);
4076 }
4077 }
4078 }
4079 }
4080
4081 // Allocate up to maximum.
4082 while(items.size() < BUF_COUNT)
4083 {
4084 BufItem item;
4085 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004086 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004087 items.push_back(item);
4088 }
4089
4090 // Validate that no buffer is lost.
4091 for(size_t i = 0; i < items.size(); ++i)
4092 {
4093 VmaAllocationInfo allocInfo;
4094 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004095 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004096 }
4097
4098 // Next frame.
4099 vmaSetCurrentFrameIndex(g_hAllocator, 2);
4100
4101 // Allocate another BUF_COUNT buffers.
4102 for(size_t i = 0; i < BUF_COUNT; ++i)
4103 {
4104 BufItem item;
4105 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004106 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004107 items.push_back(item);
4108 }
4109
4110 // Make sure the first BUF_COUNT is lost. Delete them.
4111 for(size_t i = 0; i < BUF_COUNT; ++i)
4112 {
4113 VmaAllocationInfo allocInfo;
4114 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004115 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004116 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4117 }
4118 items.erase(items.begin(), items.begin() + BUF_COUNT);
4119
4120 // Validate that no buffer is lost.
4121 for(size_t i = 0; i < items.size(); ++i)
4122 {
4123 VmaAllocationInfo allocInfo;
4124 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004125 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004126 }
4127
4128 // Free one item.
4129 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
4130 items.pop_back();
4131
4132 // Validate statistics.
4133 {
4134 VmaPoolStats poolStats = {};
4135 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004136 TEST(poolStats.allocationCount == items.size());
4137 TEST(poolStats.size = BUF_COUNT * BUF_SIZE);
4138 TEST(poolStats.unusedRangeCount == 1);
4139 TEST(poolStats.unusedRangeSizeMax == BUF_SIZE);
4140 TEST(poolStats.unusedSize == BUF_SIZE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004141 }
4142
4143 // Free all remaining items.
4144 for(size_t i = items.size(); i--; )
4145 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4146 items.clear();
4147
4148 // Allocate maximum items again.
4149 for(size_t i = 0; i < BUF_COUNT; ++i)
4150 {
4151 BufItem item;
4152 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004153 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004154 items.push_back(item);
4155 }
4156
4157 // Delete every other item.
4158 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
4159 {
4160 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4161 items.erase(items.begin() + i);
4162 }
4163
4164 // Defragment!
4165 {
4166 std::vector<VmaAllocation> allocationsToDefragment(items.size());
4167 for(size_t i = 0; i < items.size(); ++i)
4168 allocationsToDefragment[i] = items[i].Alloc;
4169
4170 VmaDefragmentationStats defragmentationStats;
4171 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004172 TEST(res == VK_SUCCESS);
4173 TEST(defragmentationStats.deviceMemoryBlocksFreed == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004174 }
4175
4176 // Free all remaining items.
4177 for(size_t i = items.size(); i--; )
4178 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4179 items.clear();
4180
4181 ////////////////////////////////////////////////////////////////////////////////
4182 // Test for vmaMakePoolAllocationsLost
4183
4184 // Allocate 4 buffers on frame 10.
4185 vmaSetCurrentFrameIndex(g_hAllocator, 10);
4186 for(size_t i = 0; i < 4; ++i)
4187 {
4188 BufItem item;
4189 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004190 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004191 items.push_back(item);
4192 }
4193
4194 // Touch first 2 of them on frame 11.
4195 vmaSetCurrentFrameIndex(g_hAllocator, 11);
4196 for(size_t i = 0; i < 2; ++i)
4197 {
4198 VmaAllocationInfo allocInfo;
4199 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
4200 }
4201
4202 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
4203 size_t lostCount = 0xDEADC0DE;
4204 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004205 TEST(lostCount == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004206
4207 // Make another call. Now 0 should be lost.
4208 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004209 TEST(lostCount == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004210
4211 // Make another call, with null count. Should not crash.
4212 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
4213
4214 // END: Free all remaining items.
4215 for(size_t i = items.size(); i--; )
4216 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4217
4218 items.clear();
4219
Adam Sawickid2924172018-06-11 12:48:46 +02004220 ////////////////////////////////////////////////////////////////////////////////
4221 // Test for allocation too large for pool
4222
4223 {
4224 VmaAllocationCreateInfo allocCreateInfo = {};
4225 allocCreateInfo.pool = pool;
4226
4227 VkMemoryRequirements memReq;
4228 memReq.memoryTypeBits = UINT32_MAX;
4229 memReq.alignment = 1;
4230 memReq.size = poolCreateInfo.blockSize + 4;
4231
4232 VmaAllocation alloc = nullptr;
4233 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004234 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
Adam Sawickid2924172018-06-11 12:48:46 +02004235 }
4236
Adam Sawickib8333fb2018-03-13 16:15:53 +01004237 vmaDestroyPool(g_hAllocator, pool);
4238}
4239
Adam Sawickie44c6262018-06-15 14:30:39 +02004240static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
4241{
4242 const uint8_t* pBytes = (const uint8_t*)pMemory;
4243 for(size_t i = 0; i < size; ++i)
4244 {
4245 if(pBytes[i] != pattern)
4246 {
4247 return false;
4248 }
4249 }
4250 return true;
4251}
4252
4253static void TestAllocationsInitialization()
4254{
4255 VkResult res;
4256
4257 const size_t BUF_SIZE = 1024;
4258
4259 // Create pool.
4260
4261 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4262 bufInfo.size = BUF_SIZE;
4263 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4264
4265 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
4266 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
4267
4268 VmaPoolCreateInfo poolCreateInfo = {};
4269 poolCreateInfo.blockSize = BUF_SIZE * 10;
4270 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
4271 poolCreateInfo.maxBlockCount = 1;
4272 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004273 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004274
4275 VmaAllocationCreateInfo bufAllocCreateInfo = {};
4276 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004277 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004278
4279 // Create one persistently mapped buffer to keep memory of this block mapped,
4280 // so that pointer to mapped data will remain (more or less...) valid even
4281 // after destruction of other allocations.
4282
4283 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
4284 VkBuffer firstBuf;
4285 VmaAllocation firstAlloc;
4286 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004287 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004288
4289 // Test buffers.
4290
4291 for(uint32_t i = 0; i < 2; ++i)
4292 {
4293 const bool persistentlyMapped = i == 0;
4294 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
4295 VkBuffer buf;
4296 VmaAllocation alloc;
4297 VmaAllocationInfo allocInfo;
4298 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004299 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004300
4301 void* pMappedData;
4302 if(!persistentlyMapped)
4303 {
4304 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004305 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004306 }
4307 else
4308 {
4309 pMappedData = allocInfo.pMappedData;
4310 }
4311
4312 // Validate initialized content
4313 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004314 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02004315
4316 if(!persistentlyMapped)
4317 {
4318 vmaUnmapMemory(g_hAllocator, alloc);
4319 }
4320
4321 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4322
4323 // Validate freed content
4324 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004325 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02004326 }
4327
4328 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
4329 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
4330}
4331
Adam Sawickib8333fb2018-03-13 16:15:53 +01004332static void TestPool_Benchmark(
4333 PoolTestResult& outResult,
4334 const PoolTestConfig& config)
4335{
Adam Sawickib8d34d52018-10-03 17:41:20 +02004336 TEST(config.ThreadCount > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004337
4338 RandomNumberGenerator mainRand{config.RandSeed};
4339
4340 uint32_t allocationSizeProbabilitySum = std::accumulate(
4341 config.AllocationSizes.begin(),
4342 config.AllocationSizes.end(),
4343 0u,
4344 [](uint32_t sum, const AllocationSize& allocSize) {
4345 return sum + allocSize.Probability;
4346 });
4347
Adam Sawicki41b41112021-03-02 15:11:18 +01004348 VkBufferCreateInfo bufferTemplateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4349 bufferTemplateInfo.size = 256; // Whatever.
4350 bufferTemplateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004351
Adam Sawicki41b41112021-03-02 15:11:18 +01004352 VkImageCreateInfo imageTemplateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4353 imageTemplateInfo.imageType = VK_IMAGE_TYPE_2D;
4354 imageTemplateInfo.extent.width = 256; // Whatever.
4355 imageTemplateInfo.extent.height = 256; // Whatever.
4356 imageTemplateInfo.extent.depth = 1;
4357 imageTemplateInfo.mipLevels = 1;
4358 imageTemplateInfo.arrayLayers = 1;
4359 imageTemplateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4360 imageTemplateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
4361 imageTemplateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
4362 imageTemplateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
4363 imageTemplateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004364
4365 uint32_t bufferMemoryTypeBits = UINT32_MAX;
4366 {
4367 VkBuffer dummyBuffer;
Adam Sawicki41b41112021-03-02 15:11:18 +01004368 VkResult res = vkCreateBuffer(g_hDevice, &bufferTemplateInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004369 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004370
4371 VkMemoryRequirements memReq;
4372 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
4373 bufferMemoryTypeBits = memReq.memoryTypeBits;
4374
Adam Sawicki1f84f622019-07-02 13:40:01 +02004375 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004376 }
4377
4378 uint32_t imageMemoryTypeBits = UINT32_MAX;
4379 {
4380 VkImage dummyImage;
Adam Sawicki41b41112021-03-02 15:11:18 +01004381 VkResult res = vkCreateImage(g_hDevice, &imageTemplateInfo, g_Allocs, &dummyImage);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004382 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004383
4384 VkMemoryRequirements memReq;
4385 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
4386 imageMemoryTypeBits = memReq.memoryTypeBits;
4387
Adam Sawicki1f84f622019-07-02 13:40:01 +02004388 vkDestroyImage(g_hDevice, dummyImage, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004389 }
4390
4391 uint32_t memoryTypeBits = 0;
4392 if(config.UsesBuffers() && config.UsesImages())
4393 {
4394 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
4395 if(memoryTypeBits == 0)
4396 {
4397 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
4398 return;
4399 }
4400 }
4401 else if(config.UsesBuffers())
4402 memoryTypeBits = bufferMemoryTypeBits;
4403 else if(config.UsesImages())
4404 memoryTypeBits = imageMemoryTypeBits;
4405 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02004406 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004407
4408 VmaPoolCreateInfo poolCreateInfo = {};
Adam Sawickib8333fb2018-03-13 16:15:53 +01004409 poolCreateInfo.minBlockCount = 1;
4410 poolCreateInfo.maxBlockCount = 1;
4411 poolCreateInfo.blockSize = config.PoolSize;
4412 poolCreateInfo.frameInUseCount = 1;
4413
Adam Sawicki7e56c482021-02-23 15:27:24 +01004414 const VkPhysicalDeviceMemoryProperties* memProps = nullptr;
4415 vmaGetMemoryProperties(g_hAllocator, &memProps);
4416
Adam Sawicki26eaa3b2021-02-18 15:53:18 +01004417 VmaPool pool = VK_NULL_HANDLE;
4418 VkResult res;
4419 // Loop over memory types because we sometimes allocate a big block here,
4420 // while the most eligible DEVICE_LOCAL heap may be only 256 MB on some GPUs.
4421 while(memoryTypeBits)
4422 {
4423 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
4424 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4425 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004426
Adam Sawicki7e56c482021-02-23 15:27:24 +01004427 const uint32_t heapIndex = memProps->memoryTypes[poolCreateInfo.memoryTypeIndex].heapIndex;
4428 // Protection against validation layer error when trying to allocate a block larger than entire heap size,
4429 // which may be only 256 MB on some platforms.
4430 if(poolCreateInfo.blockSize * poolCreateInfo.minBlockCount < memProps->memoryHeaps[heapIndex].size)
4431 {
4432 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
4433 if(res == VK_SUCCESS)
4434 break;
4435 }
Adam Sawicki26eaa3b2021-02-18 15:53:18 +01004436 memoryTypeBits &= ~(1u << poolCreateInfo.memoryTypeIndex);
4437 }
4438 TEST(pool);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004439
4440 // Start time measurement - after creating pool and initializing data structures.
4441 time_point timeBeg = std::chrono::high_resolution_clock::now();
4442
4443 ////////////////////////////////////////////////////////////////////////////////
4444 // ThreadProc
Adam Sawicki41b41112021-03-02 15:11:18 +01004445 auto ThreadProc = [&config, allocationSizeProbabilitySum, pool](
Adam Sawickib8333fb2018-03-13 16:15:53 +01004446 PoolTestThreadResult* outThreadResult,
4447 uint32_t randSeed,
4448 HANDLE frameStartEvent,
4449 HANDLE frameEndEvent) -> void
4450 {
4451 RandomNumberGenerator threadRand{randSeed};
Adam Sawicki41b41112021-03-02 15:11:18 +01004452 VkResult res = VK_SUCCESS;
4453
4454 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4455 bufferInfo.size = 256; // Whatever.
4456 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4457
4458 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4459 imageInfo.imageType = VK_IMAGE_TYPE_2D;
4460 imageInfo.extent.width = 256; // Whatever.
4461 imageInfo.extent.height = 256; // Whatever.
4462 imageInfo.extent.depth = 1;
4463 imageInfo.mipLevels = 1;
4464 imageInfo.arrayLayers = 1;
4465 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4466 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
4467 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
4468 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
4469 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004470
4471 outThreadResult->AllocationTimeMin = duration::max();
4472 outThreadResult->AllocationTimeSum = duration::zero();
4473 outThreadResult->AllocationTimeMax = duration::min();
4474 outThreadResult->DeallocationTimeMin = duration::max();
4475 outThreadResult->DeallocationTimeSum = duration::zero();
4476 outThreadResult->DeallocationTimeMax = duration::min();
4477 outThreadResult->AllocationCount = 0;
4478 outThreadResult->DeallocationCount = 0;
4479 outThreadResult->LostAllocationCount = 0;
4480 outThreadResult->LostAllocationTotalSize = 0;
4481 outThreadResult->FailedAllocationCount = 0;
4482 outThreadResult->FailedAllocationTotalSize = 0;
4483
4484 struct Item
4485 {
Adam Sawicki41b41112021-03-02 15:11:18 +01004486 VkDeviceSize BufferSize = 0;
4487 VkExtent2D ImageSize = { 0, 0 };
4488 VkBuffer Buf = VK_NULL_HANDLE;
4489 VkImage Image = VK_NULL_HANDLE;
4490 VmaAllocation Alloc = VK_NULL_HANDLE;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004491
Adam Sawicki41b41112021-03-02 15:11:18 +01004492 Item() { }
4493 Item(Item&& src) :
4494 BufferSize(src.BufferSize), ImageSize(src.ImageSize), Buf(src.Buf), Image(src.Image), Alloc(src.Alloc)
4495 {
4496 src.BufferSize = 0;
4497 src.ImageSize = {0, 0};
4498 src.Buf = VK_NULL_HANDLE;
4499 src.Image = VK_NULL_HANDLE;
4500 src.Alloc = VK_NULL_HANDLE;
4501 }
4502 Item(const Item& src) = delete;
4503 ~Item()
4504 {
4505 DestroyResources();
4506 }
4507 Item& operator=(Item&& src)
4508 {
4509 if(&src != this)
4510 {
4511 DestroyResources();
4512 BufferSize = src.BufferSize; ImageSize = src.ImageSize;
4513 Buf = src.Buf; Image = src.Image; Alloc = src.Alloc;
4514 src.BufferSize = 0;
4515 src.ImageSize = {0, 0};
4516 src.Buf = VK_NULL_HANDLE;
4517 src.Image = VK_NULL_HANDLE;
4518 src.Alloc = VK_NULL_HANDLE;
4519 }
4520 return *this;
4521 }
4522 Item& operator=(const Item& src) = delete;
4523 void DestroyResources()
4524 {
4525 if(Buf)
4526 {
4527 assert(Image == VK_NULL_HANDLE);
4528 vmaDestroyBuffer(g_hAllocator, Buf, Alloc);
4529 Buf = VK_NULL_HANDLE;
4530 }
4531 else
4532 {
4533 vmaDestroyImage(g_hAllocator, Image, Alloc);
4534 Image = VK_NULL_HANDLE;
4535 }
4536 Alloc = VK_NULL_HANDLE;
4537 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01004538 VkDeviceSize CalcSizeBytes() const
4539 {
4540 return BufferSize +
Adam Sawicki41b41112021-03-02 15:11:18 +01004541 4ull * ImageSize.width * ImageSize.height;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004542 }
4543 };
4544 std::vector<Item> unusedItems, usedItems;
4545
4546 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
4547
4548 // Create all items - all unused, not yet allocated.
4549 for(size_t i = 0; i < threadTotalItemCount; ++i)
4550 {
4551 Item item = {};
4552
4553 uint32_t allocSizeIndex = 0;
4554 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
4555 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
4556 r -= config.AllocationSizes[allocSizeIndex++].Probability;
4557
4558 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
4559 if(allocSize.BufferSizeMax > 0)
4560 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004561 TEST(allocSize.BufferSizeMin > 0);
4562 TEST(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004563 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
4564 item.BufferSize = allocSize.BufferSizeMin;
4565 else
4566 {
4567 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
4568 item.BufferSize = item.BufferSize / 16 * 16;
4569 }
4570 }
4571 else
4572 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004573 TEST(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004574 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
4575 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
4576 else
4577 {
4578 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
4579 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
4580 }
4581 }
4582
Adam Sawicki41b41112021-03-02 15:11:18 +01004583 unusedItems.push_back(std::move(item));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004584 }
4585
4586 auto Allocate = [&](Item& item) -> VkResult
4587 {
Adam Sawicki41b41112021-03-02 15:11:18 +01004588 assert(item.Buf == VK_NULL_HANDLE && item.Image == VK_NULL_HANDLE && item.Alloc == VK_NULL_HANDLE);
4589
Adam Sawickib8333fb2018-03-13 16:15:53 +01004590 VmaAllocationCreateInfo allocCreateInfo = {};
4591 allocCreateInfo.pool = pool;
4592 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
4593 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
4594
4595 if(item.BufferSize)
4596 {
4597 bufferInfo.size = item.BufferSize;
Adam Sawicki41b41112021-03-02 15:11:18 +01004598 VkResult res = VK_SUCCESS;
4599 {
4600 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4601 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
4602 }
4603 if(res == VK_SUCCESS)
4604 SetDebugUtilsObjectName(VK_OBJECT_TYPE_BUFFER, (uint64_t)item.Buf, "TestPool_Benchmark_Buffer");
4605 return res;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004606 }
4607 else
4608 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004609 TEST(item.ImageSize.width && item.ImageSize.height);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004610
4611 imageInfo.extent.width = item.ImageSize.width;
4612 imageInfo.extent.height = item.ImageSize.height;
Adam Sawicki41b41112021-03-02 15:11:18 +01004613 VkResult res = VK_SUCCESS;
4614 {
4615 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4616 res = vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
4617 }
4618 if(res == VK_SUCCESS)
4619 SetDebugUtilsObjectName(VK_OBJECT_TYPE_IMAGE, (uint64_t)item.Image, "TestPool_Benchmark_Image");
4620 return res;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004621 }
4622 };
4623
4624 ////////////////////////////////////////////////////////////////////////////////
4625 // Frames
4626 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
4627 {
4628 WaitForSingleObject(frameStartEvent, INFINITE);
4629
4630 // Always make some percent of used bufs unused, to choose different used ones.
4631 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
4632 for(size_t i = 0; i < bufsToMakeUnused; ++i)
4633 {
4634 size_t index = threadRand.Generate() % usedItems.size();
Adam Sawicki41b41112021-03-02 15:11:18 +01004635 auto it = usedItems.begin() + index;
4636 Item item = std::move(*it);
4637 usedItems.erase(it);
4638 unusedItems.push_back(std::move(item));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004639 }
4640
4641 // Determine which bufs we want to use in this frame.
4642 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
4643 / config.ThreadCount;
Adam Sawickib8d34d52018-10-03 17:41:20 +02004644 TEST(usedBufCount < usedItems.size() + unusedItems.size());
Adam Sawickib8333fb2018-03-13 16:15:53 +01004645 // Move some used to unused.
4646 while(usedBufCount < usedItems.size())
4647 {
4648 size_t index = threadRand.Generate() % usedItems.size();
Adam Sawicki41b41112021-03-02 15:11:18 +01004649 auto it = usedItems.begin() + index;
4650 Item item = std::move(*it);
4651 usedItems.erase(it);
4652 unusedItems.push_back(std::move(item));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004653 }
4654 // Move some unused to used.
4655 while(usedBufCount > usedItems.size())
4656 {
4657 size_t index = threadRand.Generate() % unusedItems.size();
Adam Sawicki41b41112021-03-02 15:11:18 +01004658 auto it = unusedItems.begin() + index;
4659 Item item = std::move(*it);
4660 unusedItems.erase(it);
4661 usedItems.push_back(std::move(item));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004662 }
4663
4664 uint32_t touchExistingCount = 0;
4665 uint32_t touchLostCount = 0;
4666 uint32_t createSucceededCount = 0;
4667 uint32_t createFailedCount = 0;
4668
4669 // Touch all used bufs. If not created or lost, allocate.
4670 for(size_t i = 0; i < usedItems.size(); ++i)
4671 {
4672 Item& item = usedItems[i];
4673 // Not yet created.
4674 if(item.Alloc == VK_NULL_HANDLE)
4675 {
4676 res = Allocate(item);
4677 ++outThreadResult->AllocationCount;
4678 if(res != VK_SUCCESS)
4679 {
Adam Sawicki41b41112021-03-02 15:11:18 +01004680 assert(item.Alloc == VK_NULL_HANDLE && item.Buf == VK_NULL_HANDLE && item.Image == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004681 ++outThreadResult->FailedAllocationCount;
4682 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
4683 ++createFailedCount;
4684 }
4685 else
4686 ++createSucceededCount;
4687 }
4688 else
4689 {
4690 // Touch.
4691 VmaAllocationInfo allocInfo;
4692 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
4693 // Lost.
4694 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
4695 {
4696 ++touchLostCount;
4697
4698 // Destroy.
4699 {
4700 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
Adam Sawicki41b41112021-03-02 15:11:18 +01004701 item.DestroyResources();
Adam Sawickib8333fb2018-03-13 16:15:53 +01004702 ++outThreadResult->DeallocationCount;
4703 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01004704
4705 ++outThreadResult->LostAllocationCount;
4706 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
4707
4708 // Recreate.
4709 res = Allocate(item);
4710 ++outThreadResult->AllocationCount;
4711 // Creation failed.
4712 if(res != VK_SUCCESS)
4713 {
Adam Sawicki41b41112021-03-02 15:11:18 +01004714 TEST(item.Alloc == VK_NULL_HANDLE && item.Buf == VK_NULL_HANDLE && item.Image == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004715 ++outThreadResult->FailedAllocationCount;
4716 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
4717 ++createFailedCount;
4718 }
4719 else
4720 ++createSucceededCount;
4721 }
4722 else
4723 ++touchExistingCount;
4724 }
4725 }
4726
4727 /*
4728 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
4729 randSeed, frameIndex,
4730 touchExistingCount, touchLostCount,
4731 createSucceededCount, createFailedCount);
4732 */
4733
4734 SetEvent(frameEndEvent);
4735 }
4736
4737 // Free all remaining items.
4738 for(size_t i = usedItems.size(); i--; )
4739 {
4740 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
Adam Sawicki41b41112021-03-02 15:11:18 +01004741 usedItems[i].DestroyResources();
Adam Sawickib8333fb2018-03-13 16:15:53 +01004742 ++outThreadResult->DeallocationCount;
4743 }
4744 for(size_t i = unusedItems.size(); i--; )
4745 {
4746 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
Adam Sawicki41b41112021-03-02 15:11:18 +01004747 unusedItems[i].DestroyResources();
Adam Sawickib8333fb2018-03-13 16:15:53 +01004748 ++outThreadResult->DeallocationCount;
4749 }
4750 };
4751
4752 // Launch threads.
4753 uint32_t threadRandSeed = mainRand.Generate();
4754 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
4755 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
4756 std::vector<std::thread> bkgThreads;
4757 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
4758 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4759 {
4760 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
4761 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
4762 bkgThreads.emplace_back(std::bind(
4763 ThreadProc,
4764 &threadResults[threadIndex],
4765 threadRandSeed + threadIndex,
4766 frameStartEvents[threadIndex],
4767 frameEndEvents[threadIndex]));
4768 }
4769
4770 // Execute frames.
Adam Sawickib8d34d52018-10-03 17:41:20 +02004771 TEST(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004772 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
4773 {
4774 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
4775 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4776 SetEvent(frameStartEvents[threadIndex]);
4777 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
4778 }
4779
4780 // Wait for threads finished
4781 for(size_t i = 0; i < bkgThreads.size(); ++i)
4782 {
4783 bkgThreads[i].join();
4784 CloseHandle(frameEndEvents[i]);
4785 CloseHandle(frameStartEvents[i]);
4786 }
4787 bkgThreads.clear();
4788
4789 // Finish time measurement - before destroying pool.
4790 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
4791
4792 vmaDestroyPool(g_hAllocator, pool);
4793
4794 outResult.AllocationTimeMin = duration::max();
4795 outResult.AllocationTimeAvg = duration::zero();
4796 outResult.AllocationTimeMax = duration::min();
4797 outResult.DeallocationTimeMin = duration::max();
4798 outResult.DeallocationTimeAvg = duration::zero();
4799 outResult.DeallocationTimeMax = duration::min();
4800 outResult.LostAllocationCount = 0;
4801 outResult.LostAllocationTotalSize = 0;
4802 outResult.FailedAllocationCount = 0;
4803 outResult.FailedAllocationTotalSize = 0;
4804 size_t allocationCount = 0;
4805 size_t deallocationCount = 0;
4806 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4807 {
4808 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
4809 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
4810 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
4811 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
4812 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
4813 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
4814 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
4815 allocationCount += threadResult.AllocationCount;
4816 deallocationCount += threadResult.DeallocationCount;
4817 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
4818 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
4819 outResult.LostAllocationCount += threadResult.LostAllocationCount;
4820 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
4821 }
4822 if(allocationCount)
4823 outResult.AllocationTimeAvg /= allocationCount;
4824 if(deallocationCount)
4825 outResult.DeallocationTimeAvg /= deallocationCount;
4826}
4827
4828static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
4829{
4830 if(ptr1 < ptr2)
4831 return ptr1 + size1 > ptr2;
4832 else if(ptr2 < ptr1)
4833 return ptr2 + size2 > ptr1;
4834 else
4835 return true;
4836}
4837
Adam Sawickiefa88c42019-11-18 16:33:56 +01004838static void TestMemoryUsage()
4839{
4840 wprintf(L"Testing memory usage:\n");
4841
Adam Sawicki69185552019-11-18 17:03:34 +01004842 static const VmaMemoryUsage lastUsage = VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED;
Adam Sawickiefa88c42019-11-18 16:33:56 +01004843 for(uint32_t usage = 0; usage <= lastUsage; ++usage)
4844 {
4845 switch(usage)
4846 {
4847 case VMA_MEMORY_USAGE_UNKNOWN: printf(" VMA_MEMORY_USAGE_UNKNOWN:\n"); break;
4848 case VMA_MEMORY_USAGE_GPU_ONLY: printf(" VMA_MEMORY_USAGE_GPU_ONLY:\n"); break;
4849 case VMA_MEMORY_USAGE_CPU_ONLY: printf(" VMA_MEMORY_USAGE_CPU_ONLY:\n"); break;
4850 case VMA_MEMORY_USAGE_CPU_TO_GPU: printf(" VMA_MEMORY_USAGE_CPU_TO_GPU:\n"); break;
4851 case VMA_MEMORY_USAGE_GPU_TO_CPU: printf(" VMA_MEMORY_USAGE_GPU_TO_CPU:\n"); break;
4852 case VMA_MEMORY_USAGE_CPU_COPY: printf(" VMA_MEMORY_USAGE_CPU_COPY:\n"); break;
Adam Sawicki69185552019-11-18 17:03:34 +01004853 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED: printf(" VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:\n"); break;
Adam Sawickiefa88c42019-11-18 16:33:56 +01004854 default: assert(0);
4855 }
4856
4857 auto printResult = [](const char* testName, VkResult res, uint32_t memoryTypeBits, uint32_t memoryTypeIndex)
4858 {
4859 if(res == VK_SUCCESS)
4860 printf(" %s: memoryTypeBits=0x%X, memoryTypeIndex=%u\n", testName, memoryTypeBits, memoryTypeIndex);
4861 else
4862 printf(" %s: memoryTypeBits=0x%X, FAILED with res=%d\n", testName, memoryTypeBits, (int32_t)res);
4863 };
4864
4865 // 1: Buffer for copy
4866 {
4867 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4868 bufCreateInfo.size = 65536;
4869 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4870
4871 VkBuffer buf = VK_NULL_HANDLE;
4872 VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
4873 TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
4874
4875 VkMemoryRequirements memReq = {};
4876 vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
4877
4878 VmaAllocationCreateInfo allocCreateInfo = {};
4879 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4880 VmaAllocation alloc = VK_NULL_HANDLE;
4881 VmaAllocationInfo allocInfo = {};
4882 res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
4883 if(res == VK_SUCCESS)
4884 {
4885 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4886 res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
4887 TEST(res == VK_SUCCESS);
4888 }
4889 printResult("Buffer TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
4890 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4891 }
4892
4893 // 2: Vertex buffer
4894 {
4895 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4896 bufCreateInfo.size = 65536;
4897 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4898
4899 VkBuffer buf = VK_NULL_HANDLE;
4900 VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
4901 TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
4902
4903 VkMemoryRequirements memReq = {};
4904 vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
4905
4906 VmaAllocationCreateInfo allocCreateInfo = {};
4907 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4908 VmaAllocation alloc = VK_NULL_HANDLE;
4909 VmaAllocationInfo allocInfo = {};
4910 res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
4911 if(res == VK_SUCCESS)
4912 {
4913 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4914 res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
4915 TEST(res == VK_SUCCESS);
4916 }
4917 printResult("Buffer TRANSFER_DST + VERTEX_BUFFER", res, memReq.memoryTypeBits, allocInfo.memoryType);
4918 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4919 }
4920
4921 // 3: Image for copy, OPTIMAL
4922 {
4923 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4924 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4925 imgCreateInfo.extent.width = 256;
4926 imgCreateInfo.extent.height = 256;
4927 imgCreateInfo.extent.depth = 1;
4928 imgCreateInfo.mipLevels = 1;
4929 imgCreateInfo.arrayLayers = 1;
4930 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4931 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4932 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4933 imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
4934 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4935
4936 VkImage img = VK_NULL_HANDLE;
4937 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4938 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4939
4940 VkMemoryRequirements memReq = {};
4941 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4942
4943 VmaAllocationCreateInfo allocCreateInfo = {};
4944 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4945 VmaAllocation alloc = VK_NULL_HANDLE;
4946 VmaAllocationInfo allocInfo = {};
4947 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4948 if(res == VK_SUCCESS)
4949 {
4950 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4951 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4952 TEST(res == VK_SUCCESS);
4953 }
4954 printResult("Image OPTIMAL TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
4955
4956 vmaDestroyImage(g_hAllocator, img, alloc);
4957 }
4958
4959 // 4: Image SAMPLED, OPTIMAL
4960 {
4961 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4962 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4963 imgCreateInfo.extent.width = 256;
4964 imgCreateInfo.extent.height = 256;
4965 imgCreateInfo.extent.depth = 1;
4966 imgCreateInfo.mipLevels = 1;
4967 imgCreateInfo.arrayLayers = 1;
4968 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4969 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4970 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4971 imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
4972 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4973
4974 VkImage img = VK_NULL_HANDLE;
4975 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4976 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4977
4978 VkMemoryRequirements memReq = {};
4979 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4980
4981 VmaAllocationCreateInfo allocCreateInfo = {};
4982 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4983 VmaAllocation alloc = VK_NULL_HANDLE;
4984 VmaAllocationInfo allocInfo = {};
4985 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4986 if(res == VK_SUCCESS)
4987 {
4988 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4989 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4990 TEST(res == VK_SUCCESS);
4991 }
4992 printResult("Image OPTIMAL TRANSFER_DST + SAMPLED", res, memReq.memoryTypeBits, allocInfo.memoryType);
4993 vmaDestroyImage(g_hAllocator, img, alloc);
4994 }
4995
4996 // 5: Image COLOR_ATTACHMENT, OPTIMAL
4997 {
4998 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4999 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
5000 imgCreateInfo.extent.width = 256;
5001 imgCreateInfo.extent.height = 256;
5002 imgCreateInfo.extent.depth = 1;
5003 imgCreateInfo.mipLevels = 1;
5004 imgCreateInfo.arrayLayers = 1;
5005 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
5006 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
5007 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
5008 imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
5009 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
5010
5011 VkImage img = VK_NULL_HANDLE;
5012 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
5013 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
5014
5015 VkMemoryRequirements memReq = {};
5016 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
5017
5018 VmaAllocationCreateInfo allocCreateInfo = {};
5019 allocCreateInfo.usage = (VmaMemoryUsage)usage;
5020 VmaAllocation alloc = VK_NULL_HANDLE;
5021 VmaAllocationInfo allocInfo = {};
5022 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
5023 if(res == VK_SUCCESS)
5024 {
5025 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
5026 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
5027 TEST(res == VK_SUCCESS);
5028 }
5029 printResult("Image OPTIMAL SAMPLED + COLOR_ATTACHMENT", res, memReq.memoryTypeBits, allocInfo.memoryType);
5030 vmaDestroyImage(g_hAllocator, img, alloc);
5031 }
5032 }
5033}
5034
Adam Sawicki50882502020-02-07 16:51:31 +01005035static uint32_t FindDeviceCoherentMemoryTypeBits()
5036{
5037 VkPhysicalDeviceMemoryProperties memProps;
5038 vkGetPhysicalDeviceMemoryProperties(g_hPhysicalDevice, &memProps);
5039
5040 uint32_t memTypeBits = 0;
5041 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
5042 {
5043 if(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD)
5044 memTypeBits |= 1u << i;
5045 }
5046 return memTypeBits;
5047}
5048
5049static void TestDeviceCoherentMemory()
5050{
5051 if(!VK_AMD_device_coherent_memory_enabled)
5052 return;
5053
5054 uint32_t deviceCoherentMemoryTypeBits = FindDeviceCoherentMemoryTypeBits();
5055 // Extension is enabled, feature is enabled, and the device still doesn't support any such memory type?
5056 // OK then, so it's just fake!
5057 if(deviceCoherentMemoryTypeBits == 0)
5058 return;
5059
5060 wprintf(L"Testing device coherent memory...\n");
5061
5062 // 1. Try to allocate buffer from a memory type that is DEVICE_COHERENT.
5063
5064 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5065 bufCreateInfo.size = 0x10000;
5066 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5067
5068 VmaAllocationCreateInfo allocCreateInfo = {};
5069 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5070 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD;
5071
5072 AllocInfo alloc = {};
5073 VmaAllocationInfo allocInfo = {};
5074 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &alloc.m_Buffer, &alloc.m_Allocation, &allocInfo);
5075
5076 // Make sure it succeeded and was really created in such memory type.
5077 TEST(res == VK_SUCCESS);
5078 TEST((1u << allocInfo.memoryType) & deviceCoherentMemoryTypeBits);
5079
5080 alloc.Destroy();
5081
5082 // 2. Try to create a pool in such memory type.
5083 {
5084 VmaPoolCreateInfo poolCreateInfo = {};
5085
5086 res = vmaFindMemoryTypeIndex(g_hAllocator, UINT32_MAX, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
5087 TEST(res == VK_SUCCESS);
5088 TEST((1u << poolCreateInfo.memoryTypeIndex) & deviceCoherentMemoryTypeBits);
5089
5090 VmaPool pool = VK_NULL_HANDLE;
5091 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
5092 TEST(res == VK_SUCCESS);
5093
5094 vmaDestroyPool(g_hAllocator, pool);
5095 }
5096
5097 // 3. Try the same with a local allocator created without VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT.
5098
5099 VmaAllocatorCreateInfo allocatorCreateInfo = {};
5100 SetAllocatorCreateInfo(allocatorCreateInfo);
5101 allocatorCreateInfo.flags &= ~VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT;
5102
5103 VmaAllocator localAllocator = VK_NULL_HANDLE;
5104 res = vmaCreateAllocator(&allocatorCreateInfo, &localAllocator);
5105 TEST(res == VK_SUCCESS && localAllocator);
5106
5107 res = vmaCreateBuffer(localAllocator, &bufCreateInfo, &allocCreateInfo, &alloc.m_Buffer, &alloc.m_Allocation, &allocInfo);
5108
5109 // Make sure it failed.
5110 TEST(res != VK_SUCCESS && !alloc.m_Buffer && !alloc.m_Allocation);
5111
5112 // 4. Try to find memory type.
5113 {
5114 uint32_t memTypeIndex = UINT_MAX;
5115 res = vmaFindMemoryTypeIndex(localAllocator, UINT32_MAX, &allocCreateInfo, &memTypeIndex);
5116 TEST(res != VK_SUCCESS);
5117 }
5118
5119 vmaDestroyAllocator(localAllocator);
5120}
5121
Adam Sawicki40ffe982019-10-11 15:56:02 +02005122static void TestBudget()
5123{
5124 wprintf(L"Testing budget...\n");
5125
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005126 static const VkDeviceSize BUF_SIZE = 10ull * 1024 * 1024;
Adam Sawicki353e3672019-11-02 14:12:05 +01005127 static const uint32_t BUF_COUNT = 4;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005128
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005129 const VkPhysicalDeviceMemoryProperties* memProps = {};
5130 vmaGetMemoryProperties(g_hAllocator, &memProps);
5131
Adam Sawicki40ffe982019-10-11 15:56:02 +02005132 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
5133 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005134 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
5135
5136 VmaBudget budgetBeg[VK_MAX_MEMORY_HEAPS] = {};
5137 vmaGetBudget(g_hAllocator, budgetBeg);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005138
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005139 for(uint32_t i = 0; i < memProps->memoryHeapCount; ++i)
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01005140 {
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005141 TEST(budgetBeg[i].budget > 0);
5142 TEST(budgetBeg[i].budget <= memProps->memoryHeaps[i].size);
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01005143 TEST(budgetBeg[i].allocationBytes <= budgetBeg[i].blockBytes);
5144 }
5145
Adam Sawicki40ffe982019-10-11 15:56:02 +02005146 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5147 bufInfo.size = BUF_SIZE;
5148 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5149
5150 VmaAllocationCreateInfo allocCreateInfo = {};
5151 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
5152 if(testIndex == 0)
5153 {
5154 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5155 }
5156
5157 // CREATE BUFFERS
5158 uint32_t heapIndex = 0;
5159 BufferInfo bufInfos[BUF_COUNT] = {};
5160 for(uint32_t bufIndex = 0; bufIndex < BUF_COUNT; ++bufIndex)
5161 {
5162 VmaAllocationInfo allocInfo;
5163 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5164 &bufInfos[bufIndex].Buffer, &bufInfos[bufIndex].Allocation, &allocInfo);
5165 TEST(res == VK_SUCCESS);
5166 if(bufIndex == 0)
5167 {
5168 heapIndex = MemoryTypeToHeap(allocInfo.memoryType);
5169 }
5170 else
5171 {
5172 // All buffers need to fall into the same heap.
5173 TEST(MemoryTypeToHeap(allocInfo.memoryType) == heapIndex);
5174 }
5175 }
5176
Adam Sawicki353e3672019-11-02 14:12:05 +01005177 VmaBudget budgetWithBufs[VK_MAX_MEMORY_HEAPS] = {};
5178 vmaGetBudget(g_hAllocator, budgetWithBufs);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005179
5180 // DESTROY BUFFERS
5181 for(size_t bufIndex = BUF_COUNT; bufIndex--; )
5182 {
5183 vmaDestroyBuffer(g_hAllocator, bufInfos[bufIndex].Buffer, bufInfos[bufIndex].Allocation);
5184 }
5185
Adam Sawicki353e3672019-11-02 14:12:05 +01005186 VmaBudget budgetEnd[VK_MAX_MEMORY_HEAPS] = {};
5187 vmaGetBudget(g_hAllocator, budgetEnd);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005188
5189 // CHECK
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005190 for(uint32_t i = 0; i < memProps->memoryHeapCount; ++i)
Adam Sawicki40ffe982019-10-11 15:56:02 +02005191 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005192 TEST(budgetEnd[i].allocationBytes <= budgetEnd[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005193 if(i == heapIndex)
5194 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005195 TEST(budgetEnd[i].allocationBytes == budgetBeg[i].allocationBytes);
5196 TEST(budgetWithBufs[i].allocationBytes == budgetBeg[i].allocationBytes + BUF_SIZE * BUF_COUNT);
5197 TEST(budgetWithBufs[i].blockBytes >= budgetEnd[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005198 }
5199 else
5200 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005201 TEST(budgetEnd[i].allocationBytes == budgetEnd[i].allocationBytes &&
5202 budgetEnd[i].allocationBytes == budgetWithBufs[i].allocationBytes);
5203 TEST(budgetEnd[i].blockBytes == budgetEnd[i].blockBytes &&
5204 budgetEnd[i].blockBytes == budgetWithBufs[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005205 }
5206 }
5207 }
5208}
5209
Adam Sawicki0620c8e2020-08-18 16:43:44 +02005210static void TestAliasing()
5211{
5212 wprintf(L"Testing aliasing...\n");
5213
5214 /*
5215 This is just a simple test, more like a code sample to demonstrate it's possible.
5216 */
5217
5218 // A 512x512 texture to be sampled.
5219 VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
5220 img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;
5221 img1CreateInfo.extent.width = 512;
5222 img1CreateInfo.extent.height = 512;
5223 img1CreateInfo.extent.depth = 1;
5224 img1CreateInfo.mipLevels = 10;
5225 img1CreateInfo.arrayLayers = 1;
5226 img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
5227 img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
5228 img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
5229 img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
5230 img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
5231
5232 // A full screen texture to be used as color attachment.
5233 VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
5234 img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;
5235 img2CreateInfo.extent.width = 1920;
5236 img2CreateInfo.extent.height = 1080;
5237 img2CreateInfo.extent.depth = 1;
5238 img2CreateInfo.mipLevels = 1;
5239 img2CreateInfo.arrayLayers = 1;
5240 img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
5241 img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
5242 img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
5243 img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
5244 img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
5245
5246 VkImage img1 = VK_NULL_HANDLE;
5247 ERR_GUARD_VULKAN(vkCreateImage(g_hDevice, &img1CreateInfo, g_Allocs, &img1));
5248 VkImage img2 = VK_NULL_HANDLE;
5249 ERR_GUARD_VULKAN(vkCreateImage(g_hDevice, &img2CreateInfo, g_Allocs, &img2));
5250
5251 VkMemoryRequirements img1MemReq = {};
5252 vkGetImageMemoryRequirements(g_hDevice, img1, &img1MemReq);
5253 VkMemoryRequirements img2MemReq = {};
5254 vkGetImageMemoryRequirements(g_hDevice, img2, &img2MemReq);
5255
5256 VkMemoryRequirements finalMemReq = {};
5257 finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);
5258 finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);
5259 finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;
5260 if(finalMemReq.memoryTypeBits != 0)
5261 {
5262 wprintf(L" size: max(%llu, %llu) = %llu\n",
5263 img1MemReq.size, img2MemReq.size, finalMemReq.size);
5264 wprintf(L" alignment: max(%llu, %llu) = %llu\n",
5265 img1MemReq.alignment, img2MemReq.alignment, finalMemReq.alignment);
5266 wprintf(L" memoryTypeBits: %u & %u = %u\n",
5267 img1MemReq.memoryTypeBits, img2MemReq.memoryTypeBits, finalMemReq.memoryTypeBits);
5268
5269 VmaAllocationCreateInfo allocCreateInfo = {};
5270 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
5271
5272 VmaAllocation alloc = VK_NULL_HANDLE;
5273 ERR_GUARD_VULKAN(vmaAllocateMemory(g_hAllocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr));
5274
5275 ERR_GUARD_VULKAN(vmaBindImageMemory(g_hAllocator, alloc, img1));
5276 ERR_GUARD_VULKAN(vmaBindImageMemory(g_hAllocator, alloc, img2));
5277
5278 // You can use img1, img2 here, but not at the same time!
5279
5280 vmaFreeMemory(g_hAllocator, alloc);
5281 }
5282 else
5283 {
5284 wprintf(L" Textures cannot alias!\n");
5285 }
5286
5287 vkDestroyImage(g_hDevice, img2, g_Allocs);
5288 vkDestroyImage(g_hDevice, img1, g_Allocs);
5289}
5290
Adam Sawickib8333fb2018-03-13 16:15:53 +01005291static void TestMapping()
5292{
5293 wprintf(L"Testing mapping...\n");
5294
5295 VkResult res;
5296 uint32_t memTypeIndex = UINT32_MAX;
5297
5298 enum TEST
5299 {
5300 TEST_NORMAL,
5301 TEST_POOL,
5302 TEST_DEDICATED,
5303 TEST_COUNT
5304 };
5305 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
5306 {
5307 VmaPool pool = nullptr;
5308 if(testIndex == TEST_POOL)
5309 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005310 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005311 VmaPoolCreateInfo poolInfo = {};
5312 poolInfo.memoryTypeIndex = memTypeIndex;
5313 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005314 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005315 }
5316
5317 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5318 bufInfo.size = 0x10000;
5319 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005320
Adam Sawickib8333fb2018-03-13 16:15:53 +01005321 VmaAllocationCreateInfo allocCreateInfo = {};
5322 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
5323 allocCreateInfo.pool = pool;
5324 if(testIndex == TEST_DEDICATED)
5325 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005326
Adam Sawickib8333fb2018-03-13 16:15:53 +01005327 VmaAllocationInfo allocInfo;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005328
Adam Sawickib8333fb2018-03-13 16:15:53 +01005329 // Mapped manually
5330
5331 // Create 2 buffers.
5332 BufferInfo bufferInfos[3];
5333 for(size_t i = 0; i < 2; ++i)
5334 {
5335 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5336 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005337 TEST(res == VK_SUCCESS);
5338 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005339 memTypeIndex = allocInfo.memoryType;
5340 }
Adam Sawicki40ffe982019-10-11 15:56:02 +02005341
Adam Sawickib8333fb2018-03-13 16:15:53 +01005342 // Map buffer 0.
5343 char* data00 = nullptr;
5344 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005345 TEST(res == VK_SUCCESS && data00 != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005346 data00[0xFFFF] = data00[0];
5347
5348 // Map buffer 0 second time.
5349 char* data01 = nullptr;
5350 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005351 TEST(res == VK_SUCCESS && data01 == data00);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005352
5353 // Map buffer 1.
5354 char* data1 = nullptr;
5355 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005356 TEST(res == VK_SUCCESS && data1 != nullptr);
5357 TEST(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
Adam Sawickib8333fb2018-03-13 16:15:53 +01005358 data1[0xFFFF] = data1[0];
5359
5360 // Unmap buffer 0 two times.
5361 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
5362 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
5363 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005364 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005365
5366 // Unmap buffer 1.
5367 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
5368 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005369 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005370
5371 // Create 3rd buffer - persistently mapped.
5372 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
5373 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5374 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005375 TEST(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005376
5377 // Map buffer 2.
5378 char* data2 = nullptr;
5379 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005380 TEST(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005381 data2[0xFFFF] = data2[0];
5382
5383 // Unmap buffer 2.
5384 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
5385 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005386 TEST(allocInfo.pMappedData == data2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005387
5388 // Destroy all buffers.
5389 for(size_t i = 3; i--; )
5390 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
5391
5392 vmaDestroyPool(g_hAllocator, pool);
5393 }
5394}
5395
Adam Sawickidaa6a552019-06-25 15:26:37 +02005396// Test CREATE_MAPPED with required DEVICE_LOCAL. There was a bug with it.
5397static void TestDeviceLocalMapped()
5398{
5399 VkResult res;
5400
5401 for(uint32_t testIndex = 0; testIndex < 3; ++testIndex)
5402 {
5403 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5404 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5405 bufCreateInfo.size = 4096;
5406
5407 VmaPool pool = VK_NULL_HANDLE;
5408 VmaAllocationCreateInfo allocCreateInfo = {};
5409 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
5410 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
5411 if(testIndex == 2)
5412 {
5413 VmaPoolCreateInfo poolCreateInfo = {};
5414 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
5415 TEST(res == VK_SUCCESS);
5416 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
5417 TEST(res == VK_SUCCESS);
5418 allocCreateInfo.pool = pool;
5419 }
5420 else if(testIndex == 1)
5421 {
5422 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
5423 }
5424
5425 VkBuffer buf = VK_NULL_HANDLE;
5426 VmaAllocation alloc = VK_NULL_HANDLE;
5427 VmaAllocationInfo allocInfo = {};
5428 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
5429 TEST(res == VK_SUCCESS && alloc);
5430
5431 VkMemoryPropertyFlags memTypeFlags = 0;
5432 vmaGetMemoryTypeProperties(g_hAllocator, allocInfo.memoryType, &memTypeFlags);
5433 const bool shouldBeMapped = (memTypeFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
5434 TEST((allocInfo.pMappedData != nullptr) == shouldBeMapped);
5435
5436 vmaDestroyBuffer(g_hAllocator, buf, alloc);
5437 vmaDestroyPool(g_hAllocator, pool);
5438 }
5439}
5440
Adam Sawickib8333fb2018-03-13 16:15:53 +01005441static void TestMappingMultithreaded()
5442{
5443 wprintf(L"Testing mapping multithreaded...\n");
5444
5445 static const uint32_t threadCount = 16;
5446 static const uint32_t bufferCount = 1024;
5447 static const uint32_t threadBufferCount = bufferCount / threadCount;
5448
5449 VkResult res;
5450 volatile uint32_t memTypeIndex = UINT32_MAX;
5451
5452 enum TEST
5453 {
5454 TEST_NORMAL,
5455 TEST_POOL,
5456 TEST_DEDICATED,
5457 TEST_COUNT
5458 };
5459 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
5460 {
5461 VmaPool pool = nullptr;
5462 if(testIndex == TEST_POOL)
5463 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005464 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005465 VmaPoolCreateInfo poolInfo = {};
5466 poolInfo.memoryTypeIndex = memTypeIndex;
5467 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005468 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005469 }
5470
5471 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5472 bufCreateInfo.size = 0x10000;
5473 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
5474
5475 VmaAllocationCreateInfo allocCreateInfo = {};
5476 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
5477 allocCreateInfo.pool = pool;
5478 if(testIndex == TEST_DEDICATED)
5479 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5480
5481 std::thread threads[threadCount];
5482 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
5483 {
5484 threads[threadIndex] = std::thread([=, &memTypeIndex](){
5485 // ======== THREAD FUNCTION ========
5486
5487 RandomNumberGenerator rand{threadIndex};
5488
5489 enum class MODE
5490 {
5491 // Don't map this buffer at all.
5492 DONT_MAP,
5493 // Map and quickly unmap.
5494 MAP_FOR_MOMENT,
5495 // Map and unmap before destruction.
5496 MAP_FOR_LONGER,
5497 // Map two times. Quickly unmap, second unmap before destruction.
5498 MAP_TWO_TIMES,
5499 // Create this buffer as persistently mapped.
5500 PERSISTENTLY_MAPPED,
5501 COUNT
5502 };
5503 std::vector<BufferInfo> bufInfos{threadBufferCount};
5504 std::vector<MODE> bufModes{threadBufferCount};
5505
5506 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
5507 {
5508 BufferInfo& bufInfo = bufInfos[bufferIndex];
5509 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
5510 bufModes[bufferIndex] = mode;
5511
5512 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
5513 if(mode == MODE::PERSISTENTLY_MAPPED)
5514 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
5515
5516 VmaAllocationInfo allocInfo;
5517 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
5518 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005519 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005520
5521 if(memTypeIndex == UINT32_MAX)
5522 memTypeIndex = allocInfo.memoryType;
5523
5524 char* data = nullptr;
5525
5526 if(mode == MODE::PERSISTENTLY_MAPPED)
5527 {
5528 data = (char*)allocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02005529 TEST(data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005530 }
5531 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
5532 mode == MODE::MAP_TWO_TIMES)
5533 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005534 TEST(data == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005535 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005536 TEST(res == VK_SUCCESS && data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005537
5538 if(mode == MODE::MAP_TWO_TIMES)
5539 {
5540 char* data2 = nullptr;
5541 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005542 TEST(res == VK_SUCCESS && data2 == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005543 }
5544 }
5545 else if(mode == MODE::DONT_MAP)
5546 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005547 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005548 }
5549 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02005550 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005551
5552 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
5553 if(data)
5554 data[0xFFFF] = data[0];
5555
5556 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
5557 {
5558 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
5559
5560 VmaAllocationInfo allocInfo;
5561 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
5562 if(mode == MODE::MAP_FOR_MOMENT)
Adam Sawickib8d34d52018-10-03 17:41:20 +02005563 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005564 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02005565 TEST(allocInfo.pMappedData == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005566 }
5567
5568 switch(rand.Generate() % 3)
5569 {
5570 case 0: Sleep(0); break; // Yield.
5571 case 1: Sleep(10); break; // 10 ms
5572 // default: No sleep.
5573 }
5574
5575 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
5576 if(data)
5577 data[0xFFFF] = data[0];
5578 }
5579
5580 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
5581 {
5582 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
5583 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
5584 {
5585 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
5586
5587 VmaAllocationInfo allocInfo;
5588 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005589 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005590 }
5591
5592 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
5593 }
5594 });
5595 }
5596
5597 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
5598 threads[threadIndex].join();
5599
5600 vmaDestroyPool(g_hAllocator, pool);
5601 }
5602}
5603
5604static void WriteMainTestResultHeader(FILE* file)
5605{
5606 fprintf(file,
Adam Sawicki740b08f2018-08-27 13:42:07 +02005607 "Code,Time,"
5608 "Threads,Buffers and images,Sizes,Operations,Allocation strategy,Free order,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01005609 "Total Time (us),"
5610 "Allocation Time Min (us),"
5611 "Allocation Time Avg (us),"
5612 "Allocation Time Max (us),"
5613 "Deallocation Time Min (us),"
5614 "Deallocation Time Avg (us),"
5615 "Deallocation Time Max (us),"
5616 "Total Memory Allocated (B),"
5617 "Free Range Size Avg (B),"
5618 "Free Range Size Max (B)\n");
5619}
5620
5621static void WriteMainTestResult(
5622 FILE* file,
5623 const char* codeDescription,
5624 const char* testDescription,
5625 const Config& config, const Result& result)
5626{
5627 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
5628 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
5629 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
5630 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
5631 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
5632 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
5633 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
5634
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005635 std::string currTime;
5636 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005637
5638 fprintf(file,
5639 "%s,%s,%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01005640 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
5641 codeDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005642 currTime.c_str(),
Adam Sawicki740b08f2018-08-27 13:42:07 +02005643 testDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01005644 totalTimeSeconds * 1e6f,
5645 allocationTimeMinSeconds * 1e6f,
5646 allocationTimeAvgSeconds * 1e6f,
5647 allocationTimeMaxSeconds * 1e6f,
5648 deallocationTimeMinSeconds * 1e6f,
5649 deallocationTimeAvgSeconds * 1e6f,
5650 deallocationTimeMaxSeconds * 1e6f,
5651 result.TotalMemoryAllocated,
5652 result.FreeRangeSizeAvg,
5653 result.FreeRangeSizeMax);
5654}
5655
5656static void WritePoolTestResultHeader(FILE* file)
5657{
5658 fprintf(file,
5659 "Code,Test,Time,"
5660 "Config,"
5661 "Total Time (us),"
5662 "Allocation Time Min (us),"
5663 "Allocation Time Avg (us),"
5664 "Allocation Time Max (us),"
5665 "Deallocation Time Min (us),"
5666 "Deallocation Time Avg (us),"
5667 "Deallocation Time Max (us),"
5668 "Lost Allocation Count,"
5669 "Lost Allocation Total Size (B),"
5670 "Failed Allocation Count,"
5671 "Failed Allocation Total Size (B)\n");
5672}
5673
5674static void WritePoolTestResult(
5675 FILE* file,
5676 const char* codeDescription,
5677 const char* testDescription,
5678 const PoolTestConfig& config,
5679 const PoolTestResult& result)
5680{
5681 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
5682 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
5683 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
5684 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
5685 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
5686 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
5687 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
5688
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005689 std::string currTime;
5690 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005691
5692 fprintf(file,
5693 "%s,%s,%s,"
5694 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
5695 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
5696 // General
5697 codeDescription,
5698 testDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005699 currTime.c_str(),
Adam Sawickib8333fb2018-03-13 16:15:53 +01005700 // Config
5701 config.ThreadCount,
5702 (unsigned long long)config.PoolSize,
5703 config.FrameCount,
5704 config.TotalItemCount,
5705 config.UsedItemCountMin,
5706 config.UsedItemCountMax,
5707 config.ItemsToMakeUnusedPercent,
5708 // Results
5709 totalTimeSeconds * 1e6f,
5710 allocationTimeMinSeconds * 1e6f,
5711 allocationTimeAvgSeconds * 1e6f,
5712 allocationTimeMaxSeconds * 1e6f,
5713 deallocationTimeMinSeconds * 1e6f,
5714 deallocationTimeAvgSeconds * 1e6f,
5715 deallocationTimeMaxSeconds * 1e6f,
5716 result.LostAllocationCount,
5717 result.LostAllocationTotalSize,
5718 result.FailedAllocationCount,
5719 result.FailedAllocationTotalSize);
5720}
5721
5722static void PerformCustomMainTest(FILE* file)
5723{
5724 Config config{};
5725 config.RandSeed = 65735476;
5726 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
5727 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
5728 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
5729 config.FreeOrder = FREE_ORDER::FORWARD;
5730 config.ThreadCount = 16;
5731 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02005732 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005733
5734 // Buffers
5735 //config.AllocationSizes.push_back({4, 16, 1024});
5736 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5737
5738 // Images
5739 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5740 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5741
5742 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
5743 config.AdditionalOperationCount = 1024;
5744
5745 Result result{};
5746 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005747 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005748 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
5749}
5750
5751static void PerformCustomPoolTest(FILE* file)
5752{
5753 PoolTestConfig config;
5754 config.PoolSize = 100 * 1024 * 1024;
5755 config.RandSeed = 2345764;
5756 config.ThreadCount = 1;
5757 config.FrameCount = 200;
5758 config.ItemsToMakeUnusedPercent = 2;
5759
5760 AllocationSize allocSize = {};
5761 allocSize.BufferSizeMin = 1024;
5762 allocSize.BufferSizeMax = 1024 * 1024;
5763 allocSize.Probability = 1;
5764 config.AllocationSizes.push_back(allocSize);
5765
5766 allocSize.BufferSizeMin = 0;
5767 allocSize.BufferSizeMax = 0;
5768 allocSize.ImageSizeMin = 128;
5769 allocSize.ImageSizeMax = 1024;
5770 allocSize.Probability = 1;
5771 config.AllocationSizes.push_back(allocSize);
5772
5773 config.PoolSize = config.CalcAvgResourceSize() * 200;
5774 config.UsedItemCountMax = 160;
5775 config.TotalItemCount = config.UsedItemCountMax * 10;
5776 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
5777
Adam Sawickib8333fb2018-03-13 16:15:53 +01005778 PoolTestResult result = {};
5779 TestPool_Benchmark(result, config);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005780
5781 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
5782}
5783
Adam Sawickib8333fb2018-03-13 16:15:53 +01005784static void PerformMainTests(FILE* file)
5785{
Adam Sawicki7e56c482021-02-23 15:27:24 +01005786 wprintf(L"MAIN TESTS:\n");
5787
Adam Sawickib8333fb2018-03-13 16:15:53 +01005788 uint32_t repeatCount = 1;
5789 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
5790
5791 Config config{};
5792 config.RandSeed = 65735476;
5793 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
5794 config.FreeOrder = FREE_ORDER::FORWARD;
5795
5796 size_t threadCountCount = 1;
5797 switch(ConfigType)
5798 {
5799 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
5800 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
5801 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
5802 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
5803 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
5804 default: assert(0);
5805 }
Adam Sawicki0667e332018-08-24 17:26:44 +02005806
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02005807 const size_t strategyCount = GetAllocationStrategyCount();
Adam Sawicki0667e332018-08-24 17:26:44 +02005808
Adam Sawickib8333fb2018-03-13 16:15:53 +01005809 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
5810 {
5811 std::string desc1;
5812
5813 switch(threadCountIndex)
5814 {
5815 case 0:
5816 desc1 += "1_thread";
5817 config.ThreadCount = 1;
5818 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5819 break;
5820 case 1:
5821 desc1 += "16_threads+0%_common";
5822 config.ThreadCount = 16;
5823 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5824 break;
5825 case 2:
5826 desc1 += "16_threads+50%_common";
5827 config.ThreadCount = 16;
5828 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
5829 break;
5830 case 3:
5831 desc1 += "16_threads+100%_common";
5832 config.ThreadCount = 16;
5833 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
5834 break;
5835 case 4:
5836 desc1 += "2_threads+0%_common";
5837 config.ThreadCount = 2;
5838 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5839 break;
5840 case 5:
5841 desc1 += "2_threads+50%_common";
5842 config.ThreadCount = 2;
5843 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
5844 break;
5845 case 6:
5846 desc1 += "2_threads+100%_common";
5847 config.ThreadCount = 2;
5848 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
5849 break;
5850 default:
5851 assert(0);
5852 }
5853
5854 // 0 = buffers, 1 = images, 2 = buffers and images
5855 size_t buffersVsImagesCount = 2;
5856 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
5857 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
5858 {
5859 std::string desc2 = desc1;
5860 switch(buffersVsImagesIndex)
5861 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005862 case 0: desc2 += ",Buffers"; break;
5863 case 1: desc2 += ",Images"; break;
5864 case 2: desc2 += ",Buffers+Images"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005865 default: assert(0);
5866 }
5867
5868 // 0 = small, 1 = large, 2 = small and large
5869 size_t smallVsLargeCount = 2;
5870 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
5871 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
5872 {
5873 std::string desc3 = desc2;
5874 switch(smallVsLargeIndex)
5875 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005876 case 0: desc3 += ",Small"; break;
5877 case 1: desc3 += ",Large"; break;
5878 case 2: desc3 += ",Small+Large"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005879 default: assert(0);
5880 }
5881
5882 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5883 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
5884 else
5885 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
5886
5887 // 0 = varying sizes min...max, 1 = set of constant sizes
5888 size_t constantSizesCount = 1;
5889 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
5890 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
5891 {
5892 std::string desc4 = desc3;
5893 switch(constantSizesIndex)
5894 {
5895 case 0: desc4 += " Varying_sizes"; break;
5896 case 1: desc4 += " Constant_sizes"; break;
5897 default: assert(0);
5898 }
5899
5900 config.AllocationSizes.clear();
5901 // Buffers present
5902 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
5903 {
5904 // Small
5905 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5906 {
5907 // Varying size
5908 if(constantSizesIndex == 0)
5909 config.AllocationSizes.push_back({4, 16, 1024});
5910 // Constant sizes
5911 else
5912 {
5913 config.AllocationSizes.push_back({1, 16, 16});
5914 config.AllocationSizes.push_back({1, 64, 64});
5915 config.AllocationSizes.push_back({1, 256, 256});
5916 config.AllocationSizes.push_back({1, 1024, 1024});
5917 }
5918 }
5919 // Large
5920 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5921 {
5922 // Varying size
5923 if(constantSizesIndex == 0)
5924 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5925 // Constant sizes
5926 else
5927 {
5928 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
5929 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
5930 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
5931 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
5932 }
5933 }
5934 }
5935 // Images present
5936 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
5937 {
5938 // Small
5939 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5940 {
5941 // Varying size
5942 if(constantSizesIndex == 0)
5943 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5944 // Constant sizes
5945 else
5946 {
5947 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
5948 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
5949 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
5950 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
5951 }
5952 }
5953 // Large
5954 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5955 {
5956 // Varying size
5957 if(constantSizesIndex == 0)
5958 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5959 // Constant sizes
5960 else
5961 {
5962 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
5963 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
5964 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
5965 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
5966 }
5967 }
5968 }
5969
5970 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
5971 size_t beginBytesToAllocateCount = 1;
5972 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
5973 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
5974 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
5975 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
5976 {
5977 std::string desc5 = desc4;
5978
5979 switch(beginBytesToAllocateIndex)
5980 {
5981 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005982 desc5 += ",Allocate_100%";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005983 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
5984 config.AdditionalOperationCount = 0;
5985 break;
5986 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005987 desc5 += ",Allocate_50%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005988 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
5989 config.AdditionalOperationCount = 1024;
5990 break;
5991 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005992 desc5 += ",Allocate_5%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005993 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
5994 config.AdditionalOperationCount = 1024;
5995 break;
5996 case 3:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005997 desc5 += ",Allocate_95%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005998 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
5999 config.AdditionalOperationCount = 1024;
6000 break;
6001 default:
6002 assert(0);
6003 }
6004
Adam Sawicki0667e332018-08-24 17:26:44 +02006005 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01006006 {
Adam Sawicki0667e332018-08-24 17:26:44 +02006007 std::string desc6 = desc5;
6008 switch(strategyIndex)
6009 {
6010 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02006011 desc6 += ",BestFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02006012 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
6013 break;
6014 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02006015 desc6 += ",WorstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02006016 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
6017 break;
6018 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02006019 desc6 += ",FirstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02006020 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
6021 break;
6022 default:
6023 assert(0);
6024 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01006025
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006026 desc6 += ',';
6027 desc6 += FREE_ORDER_NAMES[(uint32_t)config.FreeOrder];
Adam Sawicki740b08f2018-08-27 13:42:07 +02006028
6029 const char* testDescription = desc6.c_str();
Adam Sawicki0667e332018-08-24 17:26:44 +02006030
6031 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
6032 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02006033 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawicki0667e332018-08-24 17:26:44 +02006034
6035 Result result{};
6036 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006037 TEST(res == VK_SUCCESS);
Adam Sawicki740b08f2018-08-27 13:42:07 +02006038 if(file)
6039 {
6040 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
6041 }
Adam Sawicki0667e332018-08-24 17:26:44 +02006042 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01006043 }
6044 }
6045 }
6046 }
6047 }
6048 }
6049}
6050
6051static void PerformPoolTests(FILE* file)
6052{
Adam Sawicki7e56c482021-02-23 15:27:24 +01006053 wprintf(L"POOL TESTS:\n");
6054
Adam Sawickib8333fb2018-03-13 16:15:53 +01006055 const size_t AVG_RESOURCES_PER_POOL = 300;
6056
6057 uint32_t repeatCount = 1;
6058 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
6059
6060 PoolTestConfig config{};
6061 config.RandSeed = 2346343;
6062 config.FrameCount = 200;
6063 config.ItemsToMakeUnusedPercent = 2;
6064
6065 size_t threadCountCount = 1;
6066 switch(ConfigType)
6067 {
6068 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
6069 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
6070 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
6071 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
6072 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
6073 default: assert(0);
6074 }
6075 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
6076 {
6077 std::string desc1;
6078
6079 switch(threadCountIndex)
6080 {
6081 case 0:
6082 desc1 += "1_thread";
6083 config.ThreadCount = 1;
6084 break;
6085 case 1:
6086 desc1 += "16_threads";
6087 config.ThreadCount = 16;
6088 break;
6089 case 2:
6090 desc1 += "2_threads";
6091 config.ThreadCount = 2;
6092 break;
6093 default:
6094 assert(0);
6095 }
6096
6097 // 0 = buffers, 1 = images, 2 = buffers and images
6098 size_t buffersVsImagesCount = 2;
6099 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
6100 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
6101 {
6102 std::string desc2 = desc1;
6103 switch(buffersVsImagesIndex)
6104 {
6105 case 0: desc2 += " Buffers"; break;
6106 case 1: desc2 += " Images"; break;
6107 case 2: desc2 += " Buffers+Images"; break;
6108 default: assert(0);
6109 }
6110
6111 // 0 = small, 1 = large, 2 = small and large
6112 size_t smallVsLargeCount = 2;
6113 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
6114 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
6115 {
6116 std::string desc3 = desc2;
6117 switch(smallVsLargeIndex)
6118 {
6119 case 0: desc3 += " Small"; break;
6120 case 1: desc3 += " Large"; break;
6121 case 2: desc3 += " Small+Large"; break;
6122 default: assert(0);
6123 }
6124
6125 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
6126 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
6127 else
6128 config.PoolSize = 4ull * 1024 * 1024;
6129
6130 // 0 = varying sizes min...max, 1 = set of constant sizes
6131 size_t constantSizesCount = 1;
6132 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
6133 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
6134 {
6135 std::string desc4 = desc3;
6136 switch(constantSizesIndex)
6137 {
6138 case 0: desc4 += " Varying_sizes"; break;
6139 case 1: desc4 += " Constant_sizes"; break;
6140 default: assert(0);
6141 }
6142
6143 config.AllocationSizes.clear();
6144 // Buffers present
6145 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
6146 {
6147 // Small
6148 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
6149 {
6150 // Varying size
6151 if(constantSizesIndex == 0)
6152 config.AllocationSizes.push_back({4, 16, 1024});
6153 // Constant sizes
6154 else
6155 {
6156 config.AllocationSizes.push_back({1, 16, 16});
6157 config.AllocationSizes.push_back({1, 64, 64});
6158 config.AllocationSizes.push_back({1, 256, 256});
6159 config.AllocationSizes.push_back({1, 1024, 1024});
6160 }
6161 }
6162 // Large
6163 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
6164 {
6165 // Varying size
6166 if(constantSizesIndex == 0)
6167 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
6168 // Constant sizes
6169 else
6170 {
6171 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
6172 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
6173 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
6174 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
6175 }
6176 }
6177 }
6178 // Images present
6179 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
6180 {
6181 // Small
6182 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
6183 {
6184 // Varying size
6185 if(constantSizesIndex == 0)
6186 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
6187 // Constant sizes
6188 else
6189 {
6190 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
6191 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
6192 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
6193 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
6194 }
6195 }
6196 // Large
6197 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
6198 {
6199 // Varying size
6200 if(constantSizesIndex == 0)
6201 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
6202 // Constant sizes
6203 else
6204 {
6205 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
6206 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
6207 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
6208 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
6209 }
6210 }
6211 }
6212
6213 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
6214 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
6215
6216 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
6217 size_t subscriptionModeCount;
6218 switch(ConfigType)
6219 {
6220 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
6221 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
6222 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
6223 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
6224 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
6225 default: assert(0);
6226 }
6227 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
6228 {
6229 std::string desc5 = desc4;
6230
6231 switch(subscriptionModeIndex)
6232 {
6233 case 0:
6234 desc5 += " Subscription_66%";
6235 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
6236 break;
6237 case 1:
6238 desc5 += " Subscription_133%";
6239 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
6240 break;
6241 case 2:
6242 desc5 += " Subscription_100%";
6243 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
6244 break;
6245 case 3:
6246 desc5 += " Subscription_33%";
6247 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
6248 break;
6249 case 4:
6250 desc5 += " Subscription_166%";
6251 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
6252 break;
6253 default:
6254 assert(0);
6255 }
6256
6257 config.TotalItemCount = config.UsedItemCountMax * 5;
6258 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
6259
6260 const char* testDescription = desc5.c_str();
6261
6262 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
6263 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02006264 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawickib8333fb2018-03-13 16:15:53 +01006265
6266 PoolTestResult result{};
Adam Sawickib8333fb2018-03-13 16:15:53 +01006267 TestPool_Benchmark(result, config);
Adam Sawickib8333fb2018-03-13 16:15:53 +01006268 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
6269 }
6270 }
6271 }
6272 }
6273 }
6274 }
6275}
6276
Adam Sawickia83793a2018-09-03 13:40:42 +02006277static void BasicTestBuddyAllocator()
6278{
6279 wprintf(L"Basic test buddy allocator\n");
6280
6281 RandomNumberGenerator rand{76543};
6282
6283 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6284 sampleBufCreateInfo.size = 1024; // Whatever.
6285 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6286
6287 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
6288 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6289
6290 VmaPoolCreateInfo poolCreateInfo = {};
6291 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006292 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006293
Adam Sawickid6e6d6b2018-09-21 14:07:02 +02006294 // Deliberately adding 1023 to test usable size smaller than memory block size.
6295 poolCreateInfo.blockSize = 1024 * 1024 + 1023;
Adam Sawickia83793a2018-09-03 13:40:42 +02006296 poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
Adam Sawicki80927152018-09-07 17:27:23 +02006297 //poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
Adam Sawickia83793a2018-09-03 13:40:42 +02006298
6299 VmaPool pool = nullptr;
6300 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006301 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006302
6303 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
6304
6305 VmaAllocationCreateInfo allocCreateInfo = {};
6306 allocCreateInfo.pool = pool;
6307
6308 std::vector<BufferInfo> bufInfo;
6309 BufferInfo newBufInfo;
6310 VmaAllocationInfo allocInfo;
6311
6312 bufCreateInfo.size = 1024 * 256;
6313 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6314 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006315 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006316 bufInfo.push_back(newBufInfo);
6317
6318 bufCreateInfo.size = 1024 * 512;
6319 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6320 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006321 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006322 bufInfo.push_back(newBufInfo);
6323
6324 bufCreateInfo.size = 1024 * 128;
6325 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6326 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006327 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006328 bufInfo.push_back(newBufInfo);
Adam Sawickia01d4582018-09-21 14:22:35 +02006329
6330 // Test very small allocation, smaller than minimum node size.
6331 bufCreateInfo.size = 1;
6332 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6333 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006334 TEST(res == VK_SUCCESS);
Adam Sawickia01d4582018-09-21 14:22:35 +02006335 bufInfo.push_back(newBufInfo);
Adam Sawickia83793a2018-09-03 13:40:42 +02006336
Adam Sawicki9933c5c2018-09-21 14:57:24 +02006337 // Test some small allocation with alignment requirement.
6338 {
6339 VkMemoryRequirements memReq;
6340 memReq.alignment = 256;
6341 memReq.memoryTypeBits = UINT32_MAX;
6342 memReq.size = 32;
6343
6344 newBufInfo.Buffer = VK_NULL_HANDLE;
6345 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo,
6346 &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006347 TEST(res == VK_SUCCESS);
6348 TEST(allocInfo.offset % memReq.alignment == 0);
Adam Sawicki9933c5c2018-09-21 14:57:24 +02006349 bufInfo.push_back(newBufInfo);
6350 }
6351
6352 //SaveAllocatorStatsToFile(L"TEST.json");
6353
Adam Sawicki21017c62018-09-07 15:26:59 +02006354 VmaPoolStats stats = {};
6355 vmaGetPoolStats(g_hAllocator, pool, &stats);
6356 int DBG = 0; // Set breakpoint here to inspect `stats`.
6357
Adam Sawicki80927152018-09-07 17:27:23 +02006358 // Allocate enough new buffers to surely fall into second block.
6359 for(uint32_t i = 0; i < 32; ++i)
6360 {
6361 bufCreateInfo.size = 1024 * (rand.Generate() % 32 + 1);
6362 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6363 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006364 TEST(res == VK_SUCCESS);
Adam Sawicki80927152018-09-07 17:27:23 +02006365 bufInfo.push_back(newBufInfo);
6366 }
6367
6368 SaveAllocatorStatsToFile(L"BuddyTest01.json");
6369
Adam Sawickia83793a2018-09-03 13:40:42 +02006370 // Destroy the buffers in random order.
6371 while(!bufInfo.empty())
6372 {
6373 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
6374 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
6375 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
6376 bufInfo.erase(bufInfo.begin() + indexToDestroy);
6377 }
6378
6379 vmaDestroyPool(g_hAllocator, pool);
6380}
6381
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006382static void BasicTestAllocatePages()
6383{
6384 wprintf(L"Basic test allocate pages\n");
6385
6386 RandomNumberGenerator rand{765461};
6387
6388 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6389 sampleBufCreateInfo.size = 1024; // Whatever.
6390 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
6391
6392 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
6393 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
6394
6395 VmaPoolCreateInfo poolCreateInfo = {};
6396 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickia7d77692018-10-03 16:15:27 +02006397 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006398
6399 // 1 block of 1 MB.
6400 poolCreateInfo.blockSize = 1024 * 1024;
6401 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
6402
6403 // Create pool.
6404 VmaPool pool = nullptr;
6405 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickia7d77692018-10-03 16:15:27 +02006406 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006407
6408 // Make 100 allocations of 4 KB - they should fit into the pool.
6409 VkMemoryRequirements memReq;
6410 memReq.memoryTypeBits = UINT32_MAX;
6411 memReq.alignment = 4 * 1024;
6412 memReq.size = 4 * 1024;
6413
6414 VmaAllocationCreateInfo allocCreateInfo = {};
6415 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
6416 allocCreateInfo.pool = pool;
6417
6418 constexpr uint32_t allocCount = 100;
6419
6420 std::vector<VmaAllocation> alloc{allocCount};
6421 std::vector<VmaAllocationInfo> allocInfo{allocCount};
6422 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006423 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006424 for(uint32_t i = 0; i < allocCount; ++i)
6425 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006426 TEST(alloc[i] != VK_NULL_HANDLE &&
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006427 allocInfo[i].pMappedData != nullptr &&
6428 allocInfo[i].deviceMemory == allocInfo[0].deviceMemory &&
6429 allocInfo[i].memoryType == allocInfo[0].memoryType);
6430 }
6431
6432 // Free the allocations.
6433 vmaFreeMemoryPages(g_hAllocator, allocCount, alloc.data());
6434 std::fill(alloc.begin(), alloc.end(), nullptr);
6435 std::fill(allocInfo.begin(), allocInfo.end(), VmaAllocationInfo{});
6436
6437 // Try to make 100 allocations of 100 KB. This call should fail due to not enough memory.
6438 // Also test optional allocationInfo = null.
6439 memReq.size = 100 * 1024;
6440 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), nullptr);
Adam Sawickia7d77692018-10-03 16:15:27 +02006441 TEST(res != VK_SUCCESS);
6442 TEST(std::find_if(alloc.begin(), alloc.end(), [](VmaAllocation alloc){ return alloc != VK_NULL_HANDLE; }) == alloc.end());
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006443
6444 // Make 100 allocations of 4 KB, but with required alignment of 128 KB. This should also fail.
6445 memReq.size = 4 * 1024;
6446 memReq.alignment = 128 * 1024;
6447 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006448 TEST(res != VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006449
6450 // Make 100 dedicated allocations of 4 KB.
6451 memReq.alignment = 4 * 1024;
6452 memReq.size = 4 * 1024;
6453
6454 VmaAllocationCreateInfo dedicatedAllocCreateInfo = {};
6455 dedicatedAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
6456 dedicatedAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
6457 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &dedicatedAllocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006458 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006459 for(uint32_t i = 0; i < allocCount; ++i)
6460 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006461 TEST(alloc[i] != VK_NULL_HANDLE &&
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006462 allocInfo[i].pMappedData != nullptr &&
6463 allocInfo[i].memoryType == allocInfo[0].memoryType &&
6464 allocInfo[i].offset == 0);
6465 if(i > 0)
6466 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006467 TEST(allocInfo[i].deviceMemory != allocInfo[0].deviceMemory);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006468 }
6469 }
6470
6471 // Free the allocations.
6472 vmaFreeMemoryPages(g_hAllocator, allocCount, alloc.data());
6473 std::fill(alloc.begin(), alloc.end(), nullptr);
6474 std::fill(allocInfo.begin(), allocInfo.end(), VmaAllocationInfo{});
6475
6476 vmaDestroyPool(g_hAllocator, pool);
6477}
6478
Adam Sawickif2975342018-10-16 13:49:02 +02006479// Test the testing environment.
6480static void TestGpuData()
6481{
6482 RandomNumberGenerator rand = { 53434 };
6483
6484 std::vector<AllocInfo> allocInfo;
6485
6486 for(size_t i = 0; i < 100; ++i)
6487 {
6488 AllocInfo info = {};
6489
6490 info.m_BufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
6491 info.m_BufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT |
6492 VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
6493 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6494 info.m_BufferInfo.size = 1024 * 1024 * (rand.Generate() % 9 + 1);
6495
6496 VmaAllocationCreateInfo allocCreateInfo = {};
6497 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6498
6499 VkResult res = vmaCreateBuffer(g_hAllocator, &info.m_BufferInfo, &allocCreateInfo, &info.m_Buffer, &info.m_Allocation, nullptr);
6500 TEST(res == VK_SUCCESS);
6501
6502 info.m_StartValue = rand.Generate();
6503
6504 allocInfo.push_back(std::move(info));
6505 }
6506
6507 UploadGpuData(allocInfo.data(), allocInfo.size());
6508
6509 ValidateGpuData(allocInfo.data(), allocInfo.size());
6510
6511 DestroyAllAllocations(allocInfo);
6512}
6513
Adam Sawickib8333fb2018-03-13 16:15:53 +01006514void Test()
6515{
6516 wprintf(L"TESTING:\n");
6517
Adam Sawicki48b8a332019-11-02 15:24:33 +01006518 if(false)
Adam Sawicki70a683e2018-08-24 15:36:32 +02006519 {
Adam Sawicki1a8424f2018-12-13 11:01:16 +01006520 ////////////////////////////////////////////////////////////////////////////////
6521 // Temporarily insert custom tests here:
Adam Sawicki70a683e2018-08-24 15:36:32 +02006522 return;
6523 }
6524
Adam Sawickib8333fb2018-03-13 16:15:53 +01006525 // # Simple tests
6526
6527 TestBasics();
Adam Sawickiaaa1a562020-06-24 17:41:09 +02006528 TestAllocationVersusResourceSize();
Adam Sawickif2975342018-10-16 13:49:02 +02006529 //TestGpuData(); // Not calling this because it's just testing the testing environment.
Adam Sawicki212a4a62018-06-14 15:44:45 +02006530#if VMA_DEBUG_MARGIN
6531 TestDebugMargin();
6532#else
6533 TestPool_SameSize();
Adam Sawickiddcbf8c2019-11-22 15:22:42 +01006534 TestPool_MinBlockCount();
Adam Sawicki212a4a62018-06-14 15:44:45 +02006535 TestHeapSizeLimit();
6536#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02006537#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
6538 TestAllocationsInitialization();
6539#endif
Adam Sawickiefa88c42019-11-18 16:33:56 +01006540 TestMemoryUsage();
Adam Sawicki50882502020-02-07 16:51:31 +01006541 TestDeviceCoherentMemory();
Adam Sawicki40ffe982019-10-11 15:56:02 +02006542 TestBudget();
Adam Sawicki0620c8e2020-08-18 16:43:44 +02006543 TestAliasing();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006544 TestMapping();
Adam Sawickidaa6a552019-06-25 15:26:37 +02006545 TestDeviceLocalMapped();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006546 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02006547 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02006548 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02006549 TestLinearAllocatorMultiBlock();
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006550
Adam Sawicki4338f662018-09-07 14:12:37 +02006551 BasicTestBuddyAllocator();
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006552 BasicTestAllocatePages();
Adam Sawicki4338f662018-09-07 14:12:37 +02006553
Adam Sawicki0a3c6b52021-03-02 16:48:32 +01006554 if(VK_KHR_buffer_device_address_enabled)
Adam Sawickie73e9882020-03-20 18:05:42 +01006555 TestBufferDeviceAddress();
Adam Sawickif2012052021-01-11 18:04:42 +01006556 if(VK_EXT_memory_priority_enabled)
6557 TestMemoryPriority();
Adam Sawickie73e9882020-03-20 18:05:42 +01006558
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006559 {
6560 FILE* file;
Adam Sawickic6432d12018-09-21 16:44:16 +02006561 fopen_s(&file, "Algorithms.csv", "w");
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006562 assert(file != NULL);
Adam Sawicki80927152018-09-07 17:27:23 +02006563 BenchmarkAlgorithms(file);
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006564 fclose(file);
6565 }
6566
Adam Sawickib8333fb2018-03-13 16:15:53 +01006567 TestDefragmentationSimple();
6568 TestDefragmentationFull();
Adam Sawicki52076eb2018-11-22 16:14:50 +01006569 TestDefragmentationWholePool();
Adam Sawicki9a4f5082018-11-23 17:26:05 +01006570 TestDefragmentationGpu();
Adam Sawickia52012d2019-12-23 15:28:51 +01006571 TestDefragmentationIncrementalBasic();
6572 TestDefragmentationIncrementalComplex();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006573
6574 // # Detailed tests
6575 FILE* file;
6576 fopen_s(&file, "Results.csv", "w");
6577 assert(file != NULL);
6578
6579 WriteMainTestResultHeader(file);
6580 PerformMainTests(file);
6581 //PerformCustomMainTest(file);
6582
6583 WritePoolTestResultHeader(file);
6584 PerformPoolTests(file);
6585 //PerformCustomPoolTest(file);
6586
6587 fclose(file);
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01006588
Adam Sawicki7e56c482021-02-23 15:27:24 +01006589 wprintf(L"Done, all PASSED.\n");
Adam Sawickib8333fb2018-03-13 16:15:53 +01006590}
6591
Adam Sawickif1a793c2018-03-13 15:42:22 +01006592#endif // #ifdef _WIN32