1 //
2 // Copyright (c) 2018-2020 Advanced Micro Devices, Inc. All rights reserved.
3 //
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 
23 #include "VmaUsage.h"
24 #include "Common.h"
25 #include "Constants.h"
26 #include <unordered_map>
27 #include <map>
28 #include <algorithm>
29 
30 static VERBOSITY g_Verbosity = VERBOSITY::DEFAULT;
31 
32 static const uint32_t VULKAN_API_VERSION = VK_API_VERSION_1_1;
33 
34 namespace DetailedStats
35 {
36 
37 struct Flag
38 {
39     uint32_t setCount = 0;
40 
PostValueDetailedStats::Flag41     void PostValue(bool v)
42     {
43         if(v)
44         {
45             ++setCount;
46         }
47     }
48 
PrintDetailedStats::Flag49     void Print(uint32_t totalCount) const
50     {
51         if(setCount)
52         {
53             printf(" %u (%.2f%%)\n", setCount, (double)setCount * 100.0 / (double)totalCount);
54         }
55         else
56         {
57             printf(" 0\n");
58         }
59     }
60 };
61 
62 struct Enum
63 {
EnumDetailedStats::Enum64     Enum(size_t itemCount, const char* const* itemNames, const uint32_t* itemValues = nullptr) :
65         m_ItemCount(itemCount),
66         m_ItemNames(itemNames),
67         m_ItemValues(itemValues)
68     {
69     }
70 
PostValueDetailedStats::Enum71     void PostValue(uint32_t v)
72     {
73         if(v < _countof(m_BaseCount))
74         {
75             ++m_BaseCount[v];
76         }
77         else
78         {
79             auto it = m_ExtendedCount.find(v);
80             if(it != m_ExtendedCount.end())
81             {
82                 ++it->second;
83             }
84             else
85             {
86                 m_ExtendedCount.insert(std::make_pair(v, 1u));
87             }
88         }
89     }
90 
PrintDetailedStats::Enum91     void Print(uint32_t totalCount) const
92     {
93         if(totalCount &&
94             (!m_ExtendedCount.empty() || std::count_if(m_BaseCount, m_BaseCount + _countof(m_BaseCount), [](uint32_t v) { return v > 0; })))
95         {
96             printf("\n");
97 
98             for(size_t i = 0; i < _countof(m_BaseCount); ++i)
99             {
100                 const uint32_t currCount = m_BaseCount[i];
101                 if(currCount)
102                 {
103                     PrintItem((uint32_t)i, currCount, totalCount);
104                 }
105             }
106 
107             for(const auto& it : m_ExtendedCount)
108             {
109                 PrintItem(it.first, it.second, totalCount);
110             }
111         }
112         else
113         {
114             printf(" 0\n");
115         }
116     }
117 
118 private:
119     const size_t m_ItemCount;
120     const char* const* const m_ItemNames;
121     const uint32_t* const m_ItemValues;
122 
123     uint32_t m_BaseCount[32] = {};
124     std::map<uint32_t, uint32_t> m_ExtendedCount;
125 
PrintItemDetailedStats::Enum126     void PrintItem(uint32_t value, uint32_t count, uint32_t totalCount) const
127     {
128         size_t itemIndex = m_ItemCount;
129         if(m_ItemValues)
130         {
131             for(itemIndex = 0; itemIndex < m_ItemCount; ++itemIndex)
132             {
133                 if(m_ItemValues[itemIndex] == value)
134                 {
135                     break;
136                 }
137             }
138         }
139         else
140         {
141             if(value < m_ItemCount)
142             {
143                 itemIndex = value;
144             }
145         }
146 
147         if(itemIndex < m_ItemCount)
148         {
149             printf("        %s: ", m_ItemNames[itemIndex]);
150         }
151         else
152         {
153             printf("        0x%X: ", value);
154         }
155 
156         printf("%u (%.2f%%)\n", count, (double)count * 100.0 / (double)totalCount);
157     }
158 };
159 
160 struct FlagSet
161 {
162     uint32_t count[32] = {};
163 
FlagSetDetailedStats::FlagSet164     FlagSet(size_t count, const char* const* names, const uint32_t* values = nullptr) :
165         m_Count(count),
166         m_Names(names),
167         m_Values(values)
168     {
169     }
170 
PostValueDetailedStats::FlagSet171     void PostValue(uint32_t v)
172     {
173         for(size_t i = 0; i < 32; ++i)
174         {
175             if((v & (1u << i)) != 0)
176             {
177                 ++count[i];
178             }
179         }
180     }
181 
PrintDetailedStats::FlagSet182     void Print(uint32_t totalCount) const
183     {
184         if(totalCount &&
185             std::count_if(count, count + _countof(count), [](uint32_t v) { return v > 0; }))
186         {
187             printf("\n");
188             for(uint32_t bitIndex = 0; bitIndex < 32; ++bitIndex)
189             {
190                 const uint32_t currCount = count[bitIndex];
191                 if(currCount)
192                 {
193                     size_t itemIndex = m_Count;
194                     if(m_Values)
195                     {
196                         for(itemIndex = 0; itemIndex < m_Count; ++itemIndex)
197                         {
198                             if(m_Values[itemIndex] == (1u << bitIndex))
199                             {
200                                 break;
201                             }
202                         }
203                     }
204                     else
205                     {
206                         if(bitIndex < m_Count)
207                         {
208                             itemIndex = bitIndex;
209                         }
210                     }
211 
212                     if(itemIndex < m_Count)
213                     {
214                         printf("        %s: ", m_Names[itemIndex]);
215                     }
216                     else
217                     {
218                         printf("        0x%X: ", 1u << bitIndex);
219                     }
220 
221                     printf("%u (%.2f%%)\n", currCount, (double)currCount * 100.0 / (double)totalCount);
222                 }
223             }
224         }
225         else
226         {
227             printf(" 0\n");
228         }
229     }
230 
231 private:
232     const size_t m_Count;
233     const char* const* const m_Names;
234     const uint32_t* const m_Values;
235 };
236 
237 // T should be unsigned int
238 template<typename T>
239 struct MinMaxAvg
240 {
241     T min = std::numeric_limits<T>::max();
242     T max = 0;
243     T sum = T();
244 
PostValueDetailedStats::MinMaxAvg245     void PostValue(T v)
246     {
247         this->min = std::min(this->min, v);
248         this->max = std::max(this->max, v);
249         sum += v;
250     }
251 
PrintDetailedStats::MinMaxAvg252     void Print(uint32_t totalCount) const
253     {
254         if(totalCount && sum > T())
255         {
256             if(this->min == this->max)
257             {
258                 printf(" %llu\n", (uint64_t)this->max);
259             }
260             else
261             {
262                 printf("\n        Min: %llu\n        Max: %llu\n        Avg: %llu\n",
263                     (uint64_t)this->min,
264                     (uint64_t)this->max,
265                     round_div<uint64_t>(this->sum, totalCount));
266             }
267         }
268         else
269         {
270             printf(" 0\n");
271         }
272     }
273 };
274 
275 template<typename T>
276 struct BitMask
277 {
278     uint32_t zeroCount = 0;
279     uint32_t maxCount = 0;
280 
PostValueDetailedStats::BitMask281     void PostValue(T v)
282     {
283         if(v)
284         {
285             if(v == std::numeric_limits<T>::max())
286             {
287                 ++maxCount;
288             }
289         }
290         else
291         {
292             ++zeroCount;
293         }
294     }
295 
PrintDetailedStats::BitMask296     void Print(uint32_t totalCount) const
297     {
298         if(totalCount > 0 && zeroCount < totalCount)
299         {
300             const uint32_t otherCount = totalCount - (zeroCount + maxCount);
301 
302             printf("\n        0: %u (%.2f%%)\n        Max: %u (%.2f%%)\n        Other: %u (%.2f%%)\n",
303                 zeroCount,  (double)zeroCount  * 100.0 / (double)totalCount,
304                 maxCount,   (double)maxCount   * 100.0 / (double)totalCount,
305                 otherCount, (double)otherCount * 100.0 / (double)totalCount);
306         }
307         else
308         {
309             printf(" 0\n");
310         }
311     }
312 };
313 
314 struct CountPerMemType
315 {
316     uint32_t count[VK_MAX_MEMORY_TYPES] = {};
317 
PostValueDetailedStats::CountPerMemType318     void PostValue(uint32_t v)
319     {
320         for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
321         {
322             if((v & (1u << i)) != 0)
323             {
324                 ++count[i];
325             }
326         }
327     }
328 
PrintDetailedStats::CountPerMemType329     void Print(uint32_t totalCount) const
330     {
331         if(totalCount)
332         {
333             printf("\n");
334             for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
335             {
336                 if(count[i])
337                 {
338                     printf("        %u: %u (%.2f%%)\n", i, count[i],
339                         (double)count[i] * 100.0 / (double)totalCount);
340                 }
341             }
342         }
343         else
344         {
345             printf(" 0\n");
346         }
347     }
348 };
349 
350 struct StructureStats
351 {
352     uint32_t totalCount = 0;
353 };
354 
355 #define PRINT_FIELD(name) \
356     printf("    " #name ":"); \
357     (name).Print(totalCount);
358 #define PRINT_FIELD_NAMED(name, nameStr) \
359     printf("    " nameStr ":"); \
360     (name).Print(totalCount);
361 
362 struct VmaPoolCreateInfoStats : public StructureStats
363 {
364     CountPerMemType memoryTypeIndex;
365     FlagSet flags;
366     MinMaxAvg<VkDeviceSize> blockSize;
367     MinMaxAvg<size_t> minBlockCount;
368     MinMaxAvg<size_t> maxBlockCount;
369     Flag minMaxBlockCountEqual;
370     MinMaxAvg<uint32_t> frameInUseCount;
371 
VmaPoolCreateInfoStatsDetailedStats::VmaPoolCreateInfoStats372     VmaPoolCreateInfoStats() :
373         flags(VMA_POOL_CREATE_FLAG_COUNT, VMA_POOL_CREATE_FLAG_NAMES, VMA_POOL_CREATE_FLAG_VALUES)
374     {
375     }
376 
PostValueDetailedStats::VmaPoolCreateInfoStats377     void PostValue(const VmaPoolCreateInfo& v)
378     {
379         ++totalCount;
380 
381         memoryTypeIndex.PostValue(v.memoryTypeIndex);
382         flags.PostValue(v.flags);
383         blockSize.PostValue(v.blockSize);
384         minBlockCount.PostValue(v.minBlockCount);
385         maxBlockCount.PostValue(v.maxBlockCount);
386         minMaxBlockCountEqual.PostValue(v.minBlockCount == v.maxBlockCount);
387         frameInUseCount.PostValue(v.frameInUseCount);
388     }
389 
PrintDetailedStats::VmaPoolCreateInfoStats390     void Print() const
391     {
392         if(totalCount == 0)
393         {
394             return;
395         }
396 
397         printf("VmaPoolCreateInfo (%u):\n", totalCount);
398 
399         PRINT_FIELD(memoryTypeIndex);
400         PRINT_FIELD(flags);
401         PRINT_FIELD(blockSize);
402         PRINT_FIELD(minBlockCount);
403         PRINT_FIELD(maxBlockCount);
404         PRINT_FIELD_NAMED(minMaxBlockCountEqual, "minBlockCount == maxBlockCount");
405         PRINT_FIELD(frameInUseCount);
406     }
407 };
408 
409 struct VkBufferCreateInfoStats : public StructureStats
410 {
411     FlagSet flags;
412     MinMaxAvg<VkDeviceSize> size;
413     FlagSet usage;
414     Enum sharingMode;
415 
VkBufferCreateInfoStatsDetailedStats::VkBufferCreateInfoStats416     VkBufferCreateInfoStats() :
417         flags(VK_BUFFER_CREATE_FLAG_COUNT, VK_BUFFER_CREATE_FLAG_NAMES, VK_BUFFER_CREATE_FLAG_VALUES),
418         usage(VK_BUFFER_USAGE_FLAG_COUNT, VK_BUFFER_USAGE_FLAG_NAMES, VK_BUFFER_USAGE_FLAG_VALUES),
419         sharingMode(VK_SHARING_MODE_COUNT, VK_SHARING_MODE_NAMES)
420     {
421     }
422 
PostValueDetailedStats::VkBufferCreateInfoStats423     void PostValue(const VkBufferCreateInfo& v)
424     {
425         ++totalCount;
426 
427         flags.PostValue(v.flags);
428         size.PostValue(v.size);
429         usage.PostValue(v.usage);
430         sharingMode.PostValue(v.sharingMode);
431     }
432 
PrintDetailedStats::VkBufferCreateInfoStats433     void Print() const
434     {
435         if(totalCount == 0)
436         {
437             return;
438         }
439 
440         printf("VkBufferCreateInfo (%u):\n", totalCount);
441 
442         PRINT_FIELD(flags);
443         PRINT_FIELD(size);
444         PRINT_FIELD(usage);
445         PRINT_FIELD(sharingMode);
446     }
447 };
448 
449 struct VkImageCreateInfoStats : public StructureStats
450 {
451     FlagSet flags;
452     Enum imageType;
453     Enum format;
454     MinMaxAvg<uint32_t> width, height, depth, mipLevels, arrayLayers;
455     Flag depthGreaterThanOne, mipLevelsGreaterThanOne, arrayLayersGreaterThanOne;
456     Enum samples;
457     Enum tiling;
458     FlagSet usage;
459     Enum sharingMode;
460     Enum initialLayout;
461 
VkImageCreateInfoStatsDetailedStats::VkImageCreateInfoStats462     VkImageCreateInfoStats() :
463         flags(VK_IMAGE_CREATE_FLAG_COUNT, VK_IMAGE_CREATE_FLAG_NAMES, VK_IMAGE_CREATE_FLAG_VALUES),
464         imageType(VK_IMAGE_TYPE_COUNT, VK_IMAGE_TYPE_NAMES),
465         format(VK_FORMAT_COUNT, VK_FORMAT_NAMES, VK_FORMAT_VALUES),
466         samples(VK_SAMPLE_COUNT_COUNT, VK_SAMPLE_COUNT_NAMES, VK_SAMPLE_COUNT_VALUES),
467         tiling(VK_IMAGE_TILING_COUNT, VK_IMAGE_TILING_NAMES),
468         usage(VK_IMAGE_USAGE_FLAG_COUNT, VK_IMAGE_USAGE_FLAG_NAMES, VK_IMAGE_USAGE_FLAG_VALUES),
469         sharingMode(VK_SHARING_MODE_COUNT, VK_SHARING_MODE_NAMES),
470         initialLayout(VK_IMAGE_LAYOUT_COUNT, VK_IMAGE_LAYOUT_NAMES, VK_IMAGE_LAYOUT_VALUES)
471     {
472     }
473 
PostValueDetailedStats::VkImageCreateInfoStats474     void PostValue(const VkImageCreateInfo& v)
475     {
476         ++totalCount;
477 
478         flags.PostValue(v.flags);
479         imageType.PostValue(v.imageType);
480         format.PostValue(v.format);
481         width.PostValue(v.extent.width);
482         height.PostValue(v.extent.height);
483         depth.PostValue(v.extent.depth);
484         mipLevels.PostValue(v.mipLevels);
485         arrayLayers.PostValue(v.arrayLayers);
486         depthGreaterThanOne.PostValue(v.extent.depth > 1);
487         mipLevelsGreaterThanOne.PostValue(v.mipLevels > 1);
488         arrayLayersGreaterThanOne.PostValue(v.arrayLayers > 1);
489         samples.PostValue(v.samples);
490         tiling.PostValue(v.tiling);
491         usage.PostValue(v.usage);
492         sharingMode.PostValue(v.sharingMode);
493         initialLayout.PostValue(v.initialLayout);
494     }
495 
PrintDetailedStats::VkImageCreateInfoStats496     void Print() const
497     {
498         if(totalCount == 0)
499         {
500             return;
501         }
502 
503         printf("VkImageCreateInfo (%u):\n", totalCount);
504 
505         PRINT_FIELD(flags);
506         PRINT_FIELD(imageType);
507         PRINT_FIELD(format);
508         PRINT_FIELD(width);
509         PRINT_FIELD(height);
510         PRINT_FIELD(depth);
511         PRINT_FIELD(mipLevels);
512         PRINT_FIELD(arrayLayers);
513         PRINT_FIELD_NAMED(depthGreaterThanOne, "depth > 1");
514         PRINT_FIELD_NAMED(mipLevelsGreaterThanOne, "mipLevels > 1");
515         PRINT_FIELD_NAMED(arrayLayersGreaterThanOne, "arrayLayers > 1");
516         PRINT_FIELD(samples);
517         PRINT_FIELD(tiling);
518         PRINT_FIELD(usage);
519         PRINT_FIELD(sharingMode);
520         PRINT_FIELD(initialLayout);
521     }
522 };
523 
524 struct VmaAllocationCreateInfoStats : public StructureStats
525 {
526     FlagSet flags;
527     Enum usage;
528     FlagSet requiredFlags, preferredFlags;
529     Flag requiredFlagsNotZero, preferredFlagsNotZero;
530     BitMask<uint32_t> memoryTypeBits;
531     Flag poolNotNull;
532     Flag userDataNotNull;
533 
VmaAllocationCreateInfoStatsDetailedStats::VmaAllocationCreateInfoStats534     VmaAllocationCreateInfoStats() :
535         flags(VMA_ALLOCATION_CREATE_FLAG_COUNT, VMA_ALLOCATION_CREATE_FLAG_NAMES, VMA_ALLOCATION_CREATE_FLAG_VALUES),
536         usage(VMA_MEMORY_USAGE_COUNT, VMA_MEMORY_USAGE_NAMES),
537         requiredFlags(VK_MEMORY_PROPERTY_FLAG_COUNT, VK_MEMORY_PROPERTY_FLAG_NAMES, VK_MEMORY_PROPERTY_FLAG_VALUES),
538         preferredFlags(VK_MEMORY_PROPERTY_FLAG_COUNT, VK_MEMORY_PROPERTY_FLAG_NAMES, VK_MEMORY_PROPERTY_FLAG_VALUES)
539     {
540     }
541 
PostValueDetailedStats::VmaAllocationCreateInfoStats542     void PostValue(const VmaAllocationCreateInfo& v, size_t count = 1)
543     {
544         totalCount += (uint32_t)count;
545 
546         for(size_t i = 0; i < count; ++i)
547         {
548             flags.PostValue(v.flags);
549             usage.PostValue(v.usage);
550             requiredFlags.PostValue(v.requiredFlags);
551             preferredFlags.PostValue(v.preferredFlags);
552             requiredFlagsNotZero.PostValue(v.requiredFlags != 0);
553             preferredFlagsNotZero.PostValue(v.preferredFlags != 0);
554             memoryTypeBits.PostValue(v.memoryTypeBits);
555             poolNotNull.PostValue(v.pool != VK_NULL_HANDLE);
556             userDataNotNull.PostValue(v.pUserData != nullptr);
557         }
558     }
559 
PrintDetailedStats::VmaAllocationCreateInfoStats560     void Print() const
561     {
562         if(totalCount == 0)
563         {
564             return;
565         }
566 
567         printf("VmaAllocationCreateInfo (%u):\n", totalCount);
568 
569         PRINT_FIELD(flags);
570         PRINT_FIELD(usage);
571         PRINT_FIELD(requiredFlags);
572         PRINT_FIELD(preferredFlags);
573         PRINT_FIELD_NAMED(requiredFlagsNotZero, "requiredFlags != 0");
574         PRINT_FIELD_NAMED(preferredFlagsNotZero, "preferredFlags != 0");
575         PRINT_FIELD(memoryTypeBits);
576         PRINT_FIELD_NAMED(poolNotNull, "pool != VK_NULL_HANDLE");
577         PRINT_FIELD_NAMED(userDataNotNull, "pUserData != nullptr");
578     }
579 };
580 
581 struct VmaAllocateMemoryPagesStats : public StructureStats
582 {
583     MinMaxAvg<size_t> allocationCount;
584 
PostValueDetailedStats::VmaAllocateMemoryPagesStats585     void PostValue(size_t allocationCount)
586     {
587         this->allocationCount.PostValue(allocationCount);
588     }
589 
PrintDetailedStats::VmaAllocateMemoryPagesStats590     void Print() const
591     {
592         if(totalCount == 0)
593         {
594             return;
595         }
596 
597         printf("vmaAllocateMemoryPages (%u):\n", totalCount);
598 
599         PRINT_FIELD(allocationCount);
600     }
601 };
602 
603 struct VmaDefragmentationInfo2Stats : public StructureStats
604 {
605     BitMask<VkDeviceSize> maxCpuBytesToMove;
606     BitMask<uint32_t> maxCpuAllocationsToMove;
607     BitMask<VkDeviceSize> maxGpuBytesToMove;
608     BitMask<uint32_t> maxGpuAllocationsToMove;
609     Flag commandBufferNotNull;
610     MinMaxAvg<uint32_t> allocationCount;
611     Flag allocationCountNotZero;
612     MinMaxAvg<uint32_t> poolCount;
613     Flag poolCountNotZero;
614 
PostValueDetailedStats::VmaDefragmentationInfo2Stats615     void PostValue(const VmaDefragmentationInfo2& info)
616     {
617         ++totalCount;
618 
619         maxCpuBytesToMove.PostValue(info.maxCpuBytesToMove);
620         maxCpuAllocationsToMove.PostValue(info.maxCpuAllocationsToMove);
621         maxGpuBytesToMove.PostValue(info.maxGpuBytesToMove);
622         maxGpuAllocationsToMove.PostValue(info.maxGpuAllocationsToMove);
623         commandBufferNotNull.PostValue(info.commandBuffer != VK_NULL_HANDLE);
624         allocationCount.PostValue(info.allocationCount);
625         allocationCountNotZero.PostValue(info.allocationCount != 0);
626         poolCount.PostValue(info.poolCount);
627         poolCountNotZero.PostValue(info.poolCount != 0);
628     }
629 
PrintDetailedStats::VmaDefragmentationInfo2Stats630     void Print() const
631     {
632         if(totalCount == 0)
633         {
634             return;
635         }
636 
637         printf("VmaDefragmentationInfo2 (%u):\n", totalCount);
638 
639         PRINT_FIELD(maxCpuBytesToMove);
640         PRINT_FIELD(maxCpuAllocationsToMove);
641         PRINT_FIELD(maxGpuBytesToMove);
642         PRINT_FIELD(maxGpuAllocationsToMove);
643         PRINT_FIELD_NAMED(commandBufferNotNull, "commandBuffer != VK_NULL_HANDLE");
644         PRINT_FIELD(allocationCount);
645         PRINT_FIELD_NAMED(allocationCountNotZero, "allocationCount > 0");
646         PRINT_FIELD(poolCount);
647         PRINT_FIELD_NAMED(poolCountNotZero, "poolCount > 0");
648     }
649 };
650 
651 #undef PRINT_FIELD_NAMED
652 #undef PRINT_FIELD
653 
654 } // namespace DetailedStats
655 
656 // Set this to false to disable deleting leaked VmaAllocation, VmaPool objects
657 // and let VMA report asserts about them.
658 static const bool CLEANUP_LEAKED_OBJECTS = true;
659 
660 static std::string g_FilePath;
661 // Most significant 16 bits are major version, least significant 16 bits are minor version.
662 static uint32_t g_FileVersion;
663 
MakeVersion(uint32_t major,uint32_t minor)664 inline uint32_t MakeVersion(uint32_t major, uint32_t minor) { return (major << 16) | minor; }
GetVersionMajor(uint32_t version)665 inline uint32_t GetVersionMajor(uint32_t version) { return version >> 16; }
GetVersionMinor(uint32_t version)666 inline uint32_t GetVersionMinor(uint32_t version) { return version & 0xFFFF; }
667 
668 static size_t g_IterationCount = 1;
669 static uint32_t g_PhysicalDeviceIndex = 0;
670 static RangeSequence<size_t> g_LineRanges;
671 static bool g_UserDataEnabled = true;
672 static bool g_MemStatsEnabled = false;
673 VULKAN_EXTENSION_REQUEST g_VK_LAYER_LUNARG_standard_validation = VULKAN_EXTENSION_REQUEST::DEFAULT;
674 VULKAN_EXTENSION_REQUEST g_VK_EXT_memory_budget_request        = VULKAN_EXTENSION_REQUEST::DEFAULT;
675 VULKAN_EXTENSION_REQUEST g_VK_AMD_device_coherent_memory_request = VULKAN_EXTENSION_REQUEST::DEFAULT;
676 
677 struct StatsAfterLineEntry
678 {
679     size_t line;
680     bool detailed;
681 
operator <StatsAfterLineEntry682     bool operator<(const StatsAfterLineEntry& rhs) const { return line < rhs.line; }
operator ==StatsAfterLineEntry683     bool operator==(const StatsAfterLineEntry& rhs) const { return line == rhs.line; }
684 };
685 static std::vector<StatsAfterLineEntry> g_DumpStatsAfterLine;
686 static std::vector<size_t> g_DefragmentAfterLine;
687 static uint32_t g_DefragmentationFlags = 0;
688 static size_t g_DumpStatsAfterLineNextIndex = 0;
689 static size_t g_DefragmentAfterLineNextIndex = 0;
690 
ValidateFileVersion()691 static bool ValidateFileVersion()
692 {
693     if(GetVersionMajor(g_FileVersion) == 1 &&
694         GetVersionMinor(g_FileVersion) <= 8)
695     {
696         return true;
697     }
698 
699     return false;
700 }
701 
ParseFileVersion(const StrRange & s)702 static bool ParseFileVersion(const StrRange& s)
703 {
704     CsvSplit csvSplit;
705     csvSplit.Set(s, 2);
706     uint32_t major, minor;
707     if(csvSplit.GetCount() == 2 &&
708         StrRangeToUint(csvSplit.GetRange(0), major) &&
709         StrRangeToUint(csvSplit.GetRange(1), minor))
710     {
711         g_FileVersion = (major << 16) | minor;
712         return true;
713     }
714     else
715     {
716         return false;
717     }
718 }
719 
720 ////////////////////////////////////////////////////////////////////////////////
721 // class Statistics
722 
723 class Statistics
724 {
725 public:
726     static uint32_t BufferUsageToClass(uint32_t usage);
727     static uint32_t ImageUsageToClass(uint32_t usage);
728 
729     Statistics();
730     ~Statistics();
731     void Init(uint32_t memHeapCount, uint32_t memTypeCount);
732     void PrintDeviceMemStats() const;
733     void PrintMemStats() const;
734     void PrintDetailedStats() const;
735 
GetFunctionCallCount() const736     const size_t* GetFunctionCallCount() const { return m_FunctionCallCount; }
GetImageCreationCount(uint32_t imgClass) const737     size_t GetImageCreationCount(uint32_t imgClass) const { return m_ImageCreationCount[imgClass]; }
GetLinearImageCreationCount() const738     size_t GetLinearImageCreationCount() const { return m_LinearImageCreationCount; }
GetBufferCreationCount(uint32_t bufClass) const739     size_t GetBufferCreationCount(uint32_t bufClass) const { return m_BufferCreationCount[bufClass]; }
GetAllocationCreationCount() const740     size_t GetAllocationCreationCount() const { return (size_t)m_VmaAllocationCreateInfo.totalCount + m_CreateLostAllocationCount; }
GetPoolCreationCount() const741     size_t GetPoolCreationCount() const { return m_VmaPoolCreateInfo.totalCount; }
GetBufferCreationCount() const742     size_t GetBufferCreationCount() const { return (size_t)m_VkBufferCreateInfo.totalCount; }
743 
744     void RegisterFunctionCall(VMA_FUNCTION func);
745     void RegisterCreateImage(const VkImageCreateInfo& info);
746     void RegisterCreateBuffer(const VkBufferCreateInfo& info);
747     void RegisterCreatePool(const VmaPoolCreateInfo& info);
748     void RegisterCreateAllocation(const VmaAllocationCreateInfo& info, size_t allocCount = 1);
RegisterCreateLostAllocation()749     void RegisterCreateLostAllocation() { ++m_CreateLostAllocationCount; }
RegisterAllocateMemoryPages(size_t allocCount)750     void RegisterAllocateMemoryPages(size_t allocCount) { m_VmaAllocateMemoryPages.PostValue(allocCount); }
751     void RegisterDefragmentation(const VmaDefragmentationInfo2& info);
752 
753     void RegisterDeviceMemoryAllocation(uint32_t memoryType, VkDeviceSize size);
754     void UpdateMemStats(const VmaStats& currStats);
755 
756 private:
757     uint32_t m_MemHeapCount = 0;
758     uint32_t m_MemTypeCount = 0;
759 
760     size_t m_FunctionCallCount[(size_t)VMA_FUNCTION::Count] = {};
761     size_t m_ImageCreationCount[4] = { };
762     size_t m_LinearImageCreationCount = 0;
763     size_t m_BufferCreationCount[4] = { };
764 
765     struct DeviceMemStatInfo
766     {
767         size_t allocationCount;
768         VkDeviceSize allocationTotalSize;
769     };
770     struct DeviceMemStats
771     {
772         DeviceMemStatInfo memoryType[VK_MAX_MEMORY_TYPES];
773         DeviceMemStatInfo total;
774     } m_DeviceMemStats;
775 
776     // Structure similar to VmaStatInfo, but not the same.
777     struct MemStatInfo
778     {
779         uint32_t blockCount;
780         uint32_t allocationCount;
781         uint32_t unusedRangeCount;
782         VkDeviceSize usedBytes;
783         VkDeviceSize unusedBytes;
784         VkDeviceSize totalBytes;
785     };
786     struct MemStats
787     {
788         MemStatInfo memoryType[VK_MAX_MEMORY_TYPES];
789         MemStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
790         MemStatInfo total;
791     } m_PeakMemStats;
792 
793     DetailedStats::VmaPoolCreateInfoStats m_VmaPoolCreateInfo;
794     DetailedStats::VkBufferCreateInfoStats m_VkBufferCreateInfo;
795     DetailedStats::VkImageCreateInfoStats m_VkImageCreateInfo;
796     DetailedStats::VmaAllocationCreateInfoStats m_VmaAllocationCreateInfo;
797     size_t m_CreateLostAllocationCount = 0;
798     DetailedStats::VmaAllocateMemoryPagesStats m_VmaAllocateMemoryPages;
799     DetailedStats::VmaDefragmentationInfo2Stats m_VmaDefragmentationInfo2;
800 
801     void UpdateMemStatInfo(MemStatInfo& inoutPeakInfo, const VmaStatInfo& currInfo);
802     static void PrintMemStatInfo(const MemStatInfo& info);
803 };
804 
805 // Hack for global AllocateDeviceMemoryCallback.
806 static Statistics* g_Statistics;
807 
AllocateDeviceMemoryCallback(VmaAllocator allocator,uint32_t memoryType,VkDeviceMemory memory,VkDeviceSize size,void * pUserData)808 static void VKAPI_CALL AllocateDeviceMemoryCallback(
809     VmaAllocator      allocator,
810     uint32_t          memoryType,
811     VkDeviceMemory    memory,
812     VkDeviceSize      size,
813     void*             pUserData)
814 {
815     g_Statistics->RegisterDeviceMemoryAllocation(memoryType, size);
816 }
817 
818 /// Callback function called before vkFreeMemory.
FreeDeviceMemoryCallback(VmaAllocator allocator,uint32_t memoryType,VkDeviceMemory memory,VkDeviceSize size,void * pUserData)819 static void VKAPI_CALL FreeDeviceMemoryCallback(
820     VmaAllocator      allocator,
821     uint32_t          memoryType,
822     VkDeviceMemory    memory,
823     VkDeviceSize      size,
824     void*             pUserData)
825 {
826     // Nothing.
827 }
828 
BufferUsageToClass(uint32_t usage)829 uint32_t Statistics::BufferUsageToClass(uint32_t usage)
830 {
831     // Buffer is used as source of data for fixed-function stage of graphics pipeline.
832     // It's indirect, vertex, or index buffer.
833     if ((usage & (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT |
834         VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
835         VK_BUFFER_USAGE_INDEX_BUFFER_BIT)) != 0)
836     {
837         return 0;
838     }
839     // Buffer is accessed by shaders for load/store/atomic.
840     // Aka "UAV"
841     else if ((usage & (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
842         VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) != 0)
843     {
844         return 1;
845     }
846     // Buffer is accessed by shaders for reading uniform data.
847     // Aka "constant buffer"
848     else if ((usage & (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
849     VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT)) != 0)
850     {
851         return 2;
852     }
853     // Any other type of buffer.
854     // Notice that VK_BUFFER_USAGE_TRANSFER_SRC_BIT and VK_BUFFER_USAGE_TRANSFER_DST_BIT
855     // flags are intentionally ignored.
856     else
857     {
858         return 3;
859     }
860 }
861 
ImageUsageToClass(uint32_t usage)862 uint32_t Statistics::ImageUsageToClass(uint32_t usage)
863 {
864     // Image is used as depth/stencil "texture/surface".
865     if ((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0)
866     {
867         return 0;
868     }
869     // Image is used as other type of attachment.
870     // Aka "render target"
871     else if ((usage & (VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
872         VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
873         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) != 0)
874     {
875         return 1;
876     }
877     // Image is accessed by shaders for sampling.
878     // Aka "texture"
879     else if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0)
880     {
881         return 2;
882     }
883     // Any other type of image.
884     // Notice that VK_IMAGE_USAGE_TRANSFER_SRC_BIT and VK_IMAGE_USAGE_TRANSFER_DST_BIT
885     // flags are intentionally ignored.
886     else
887     {
888         return 3;
889     }
890 }
891 
Statistics()892 Statistics::Statistics()
893 {
894     ZeroMemory(&m_DeviceMemStats, sizeof(m_DeviceMemStats));
895     ZeroMemory(&m_PeakMemStats, sizeof(m_PeakMemStats));
896 
897     assert(g_Statistics == nullptr);
898     g_Statistics = this;
899 }
900 
~Statistics()901 Statistics::~Statistics()
902 {
903     assert(g_Statistics == this);
904     g_Statistics = nullptr;
905 }
906 
Init(uint32_t memHeapCount,uint32_t memTypeCount)907 void Statistics::Init(uint32_t memHeapCount, uint32_t memTypeCount)
908 {
909     m_MemHeapCount = memHeapCount;
910     m_MemTypeCount = memTypeCount;
911 }
912 
PrintDeviceMemStats() const913 void Statistics::PrintDeviceMemStats() const
914 {
915     printf("Successful device memory allocations:\n");
916     printf("    Total: count = %zu, total size = %llu\n",
917         m_DeviceMemStats.total.allocationCount, m_DeviceMemStats.total.allocationTotalSize);
918     for(uint32_t i = 0; i < m_MemTypeCount; ++i)
919     {
920         printf("    Memory type %u: count = %zu, total size = %llu\n",
921             i, m_DeviceMemStats.memoryType[i].allocationCount, m_DeviceMemStats.memoryType[i].allocationTotalSize);
922     }
923 }
924 
PrintMemStats() const925 void Statistics::PrintMemStats() const
926 {
927     printf("Memory statistics:\n");
928 
929     printf("    Total:\n");
930     PrintMemStatInfo(m_PeakMemStats.total);
931 
932     for(uint32_t i = 0; i < m_MemHeapCount; ++i)
933     {
934         const MemStatInfo& info = m_PeakMemStats.memoryHeap[i];
935         if(info.blockCount > 0 || info.totalBytes > 0)
936         {
937             printf("    Heap %u:\n", i);
938             PrintMemStatInfo(info);
939         }
940     }
941 
942     for(uint32_t i = 0; i < m_MemTypeCount; ++i)
943     {
944         const MemStatInfo& info = m_PeakMemStats.memoryType[i];
945         if(info.blockCount > 0 || info.totalBytes > 0)
946         {
947             printf("    Type %u:\n", i);
948             PrintMemStatInfo(info);
949         }
950     }
951 }
952 
PrintDetailedStats() const953 void Statistics::PrintDetailedStats() const
954 {
955     m_VmaPoolCreateInfo.Print();
956     m_VmaAllocationCreateInfo.Print();
957     m_VmaAllocateMemoryPages.Print();
958     m_VkBufferCreateInfo.Print();
959     m_VkImageCreateInfo.Print();
960     m_VmaDefragmentationInfo2.Print();
961 }
962 
RegisterFunctionCall(VMA_FUNCTION func)963 void Statistics::RegisterFunctionCall(VMA_FUNCTION func)
964 {
965     ++m_FunctionCallCount[(size_t)func];
966 }
967 
RegisterCreateImage(const VkImageCreateInfo & info)968 void Statistics::RegisterCreateImage(const VkImageCreateInfo& info)
969 {
970     if(info.tiling == VK_IMAGE_TILING_LINEAR)
971         ++m_LinearImageCreationCount;
972     else
973     {
974         const uint32_t imgClass = ImageUsageToClass(info.usage);
975         ++m_ImageCreationCount[imgClass];
976     }
977 
978     m_VkImageCreateInfo.PostValue(info);
979 }
980 
RegisterCreateBuffer(const VkBufferCreateInfo & info)981 void Statistics::RegisterCreateBuffer(const VkBufferCreateInfo& info)
982 {
983     const uint32_t bufClass = BufferUsageToClass(info.usage);
984     ++m_BufferCreationCount[bufClass];
985 
986     m_VkBufferCreateInfo.PostValue(info);
987 }
988 
RegisterCreatePool(const VmaPoolCreateInfo & info)989 void Statistics::RegisterCreatePool(const VmaPoolCreateInfo& info)
990 {
991     m_VmaPoolCreateInfo.PostValue(info);
992 }
993 
RegisterCreateAllocation(const VmaAllocationCreateInfo & info,size_t allocCount)994 void Statistics::RegisterCreateAllocation(const VmaAllocationCreateInfo& info, size_t allocCount)
995 {
996     m_VmaAllocationCreateInfo.PostValue(info, allocCount);
997 }
998 
RegisterDefragmentation(const VmaDefragmentationInfo2 & info)999 void Statistics::RegisterDefragmentation(const VmaDefragmentationInfo2& info)
1000 {
1001     m_VmaDefragmentationInfo2.PostValue(info);
1002 }
1003 
UpdateMemStats(const VmaStats & currStats)1004 void Statistics::UpdateMemStats(const VmaStats& currStats)
1005 {
1006     UpdateMemStatInfo(m_PeakMemStats.total, currStats.total);
1007 
1008     for(uint32_t i = 0; i < m_MemHeapCount; ++i)
1009     {
1010         UpdateMemStatInfo(m_PeakMemStats.memoryHeap[i], currStats.memoryHeap[i]);
1011     }
1012 
1013     for(uint32_t i = 0; i < m_MemTypeCount; ++i)
1014     {
1015         UpdateMemStatInfo(m_PeakMemStats.memoryType[i], currStats.memoryType[i]);
1016     }
1017 }
1018 
RegisterDeviceMemoryAllocation(uint32_t memoryType,VkDeviceSize size)1019 void Statistics::RegisterDeviceMemoryAllocation(uint32_t memoryType, VkDeviceSize size)
1020 {
1021     ++m_DeviceMemStats.total.allocationCount;
1022     m_DeviceMemStats.total.allocationTotalSize += size;
1023 
1024     ++m_DeviceMemStats.memoryType[memoryType].allocationCount;
1025     m_DeviceMemStats.memoryType[memoryType].allocationTotalSize += size;
1026 }
1027 
UpdateMemStatInfo(MemStatInfo & inoutPeakInfo,const VmaStatInfo & currInfo)1028 void Statistics::UpdateMemStatInfo(MemStatInfo& inoutPeakInfo, const VmaStatInfo& currInfo)
1029 {
1030 #define SET_PEAK(inoutDst, src) \
1031     if((src) > (inoutDst)) \
1032     { \
1033         (inoutDst) = (src); \
1034     }
1035 
1036     SET_PEAK(inoutPeakInfo.blockCount, currInfo.blockCount);
1037     SET_PEAK(inoutPeakInfo.allocationCount, currInfo.allocationCount);
1038     SET_PEAK(inoutPeakInfo.unusedRangeCount, currInfo.unusedRangeCount);
1039     SET_PEAK(inoutPeakInfo.usedBytes, currInfo.usedBytes);
1040     SET_PEAK(inoutPeakInfo.unusedBytes, currInfo.unusedBytes);
1041     SET_PEAK(inoutPeakInfo.totalBytes, currInfo.usedBytes + currInfo.unusedBytes);
1042 
1043 #undef SET_PEAK
1044 }
1045 
PrintMemStatInfo(const MemStatInfo & info)1046 void Statistics::PrintMemStatInfo(const MemStatInfo& info)
1047 {
1048     printf("        Peak blocks %u, allocations %u, unused ranges %u\n",
1049         info.blockCount,
1050         info.allocationCount,
1051         info.unusedRangeCount);
1052     printf("        Peak total bytes %llu, used bytes %llu, unused bytes %llu\n",
1053         info.totalBytes,
1054         info.usedBytes,
1055         info.unusedBytes);
1056 }
1057 
1058 ////////////////////////////////////////////////////////////////////////////////
1059 // class ConfigurationParser
1060 
1061 class ConfigurationParser
1062 {
1063 public:
1064     ConfigurationParser();
1065 
1066     bool Parse(LineSplit& lineSplit);
1067 
1068     void Compare(
1069         const VkPhysicalDeviceProperties& currDevProps,
1070         const VkPhysicalDeviceMemoryProperties& currMemProps,
1071         uint32_t vulkanApiVersion,
1072         bool currMemoryBudgetEnabled);
1073 
1074 private:
1075     enum class OPTION
1076     {
1077         VulkanApiVersion,
1078         PhysicalDevice_apiVersion,
1079         PhysicalDevice_driverVersion,
1080         PhysicalDevice_vendorID,
1081         PhysicalDevice_deviceID,
1082         PhysicalDevice_deviceType,
1083         PhysicalDevice_deviceName,
1084         PhysicalDeviceLimits_maxMemoryAllocationCount,
1085         PhysicalDeviceLimits_bufferImageGranularity,
1086         PhysicalDeviceLimits_nonCoherentAtomSize,
1087         Extension_VK_KHR_dedicated_allocation,
1088         Extension_VK_KHR_bind_memory2,
1089         Extension_VK_EXT_memory_budget,
1090         Extension_VK_AMD_device_coherent_memory,
1091         Macro_VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,
1092         Macro_VMA_DEBUG_ALIGNMENT,
1093         Macro_VMA_DEBUG_MARGIN,
1094         Macro_VMA_DEBUG_INITIALIZE_ALLOCATIONS,
1095         Macro_VMA_DEBUG_DETECT_CORRUPTION,
1096         Macro_VMA_DEBUG_GLOBAL_MUTEX,
1097         Macro_VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,
1098         Macro_VMA_SMALL_HEAP_MAX_SIZE,
1099         Macro_VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,
1100         Count
1101     };
1102 
1103     std::vector<bool> m_OptionSet;
1104     std::vector<std::string> m_OptionValue;
1105     VkPhysicalDeviceMemoryProperties m_MemProps;
1106 
1107     bool m_WarningHeaderPrinted = false;
1108 
1109     void SetOption(
1110         size_t lineNumber,
1111         OPTION option,
1112         const StrRange& str);
1113     void EnsureWarningHeader();
1114     void CompareOption(VERBOSITY minVerbosity, const char* name,
1115         OPTION option, uint32_t currValue);
1116     void CompareOption(VERBOSITY minVerbosity, const char* name,
1117         OPTION option, uint64_t currValue);
1118     void CompareOption(VERBOSITY minVerbosity, const char* name,
1119         OPTION option, bool currValue);
1120     void CompareOption(VERBOSITY minVerbosity, const char* name,
1121         OPTION option, const char* currValue);
1122     void CompareMemProps(
1123         const VkPhysicalDeviceMemoryProperties& currMemProps);
1124 };
1125 
ConfigurationParser()1126 ConfigurationParser::ConfigurationParser() :
1127     m_OptionSet((size_t)OPTION::Count),
1128     m_OptionValue((size_t)OPTION::Count)
1129 {
1130     ZeroMemory(&m_MemProps, sizeof(m_MemProps));
1131 }
1132 
Parse(LineSplit & lineSplit)1133 bool ConfigurationParser::Parse(LineSplit& lineSplit)
1134 {
1135     for(auto& it : m_OptionSet)
1136     {
1137         it = false;
1138     }
1139     for(auto& it : m_OptionValue)
1140     {
1141         it.clear();
1142     }
1143 
1144     StrRange line;
1145 
1146     if(!lineSplit.GetNextLine(line) && !StrRangeEq(line, "Config,Begin"))
1147     {
1148         return false;
1149     }
1150 
1151     CsvSplit csvSplit;
1152     while(lineSplit.GetNextLine(line))
1153     {
1154         if(StrRangeEq(line, "Config,End"))
1155         {
1156             break;
1157         }
1158 
1159         const size_t currLineNumber = lineSplit.GetNextLineIndex();
1160 
1161         csvSplit.Set(line);
1162         if(csvSplit.GetCount() == 0)
1163         {
1164             return false;
1165         }
1166 
1167         const StrRange optionName = csvSplit.GetRange(0);
1168         if(StrRangeEq(optionName, "VulkanApiVersion"))
1169         {
1170             SetOption(currLineNumber, OPTION::VulkanApiVersion, StrRange{csvSplit.GetRange(1).beg, csvSplit.GetRange(2).end});
1171         }
1172         else if(StrRangeEq(optionName, "PhysicalDevice"))
1173         {
1174             if(csvSplit.GetCount() >= 3)
1175             {
1176                 const StrRange subOptionName = csvSplit.GetRange(1);
1177                 if(StrRangeEq(subOptionName, "apiVersion"))
1178                     SetOption(currLineNumber, OPTION::PhysicalDevice_apiVersion, csvSplit.GetRange(2));
1179                 else if(StrRangeEq(subOptionName, "driverVersion"))
1180                     SetOption(currLineNumber, OPTION::PhysicalDevice_driverVersion, csvSplit.GetRange(2));
1181                 else if(StrRangeEq(subOptionName, "vendorID"))
1182                     SetOption(currLineNumber, OPTION::PhysicalDevice_vendorID, csvSplit.GetRange(2));
1183                 else if(StrRangeEq(subOptionName, "deviceID"))
1184                     SetOption(currLineNumber, OPTION::PhysicalDevice_deviceID, csvSplit.GetRange(2));
1185                 else if(StrRangeEq(subOptionName, "deviceType"))
1186                     SetOption(currLineNumber, OPTION::PhysicalDevice_deviceType, csvSplit.GetRange(2));
1187                 else if(StrRangeEq(subOptionName, "deviceName"))
1188                     SetOption(currLineNumber, OPTION::PhysicalDevice_deviceName, StrRange(csvSplit.GetRange(2).beg, line.end));
1189                 else
1190                     printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1191             }
1192             else
1193                 printf("Line %zu: Too few columns.\n", currLineNumber);
1194         }
1195         else if(StrRangeEq(optionName, "PhysicalDeviceLimits"))
1196         {
1197             if(csvSplit.GetCount() >= 3)
1198             {
1199                 const StrRange subOptionName = csvSplit.GetRange(1);
1200                 if(StrRangeEq(subOptionName, "maxMemoryAllocationCount"))
1201                     SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_maxMemoryAllocationCount, csvSplit.GetRange(2));
1202                 else if(StrRangeEq(subOptionName, "bufferImageGranularity"))
1203                     SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_bufferImageGranularity, csvSplit.GetRange(2));
1204                 else if(StrRangeEq(subOptionName, "nonCoherentAtomSize"))
1205                     SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_nonCoherentAtomSize, csvSplit.GetRange(2));
1206                 else
1207                     printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1208             }
1209             else
1210                 printf("Line %zu: Too few columns.\n", currLineNumber);
1211         }
1212         else if(StrRangeEq(optionName, "Extension"))
1213         {
1214             if(csvSplit.GetCount() >= 3)
1215             {
1216                 const StrRange subOptionName = csvSplit.GetRange(1);
1217                 if(StrRangeEq(subOptionName, "VK_KHR_dedicated_allocation"))
1218                 {
1219                     // Ignore because this extension is promoted to Vulkan 1.1.
1220                 }
1221                 else if(StrRangeEq(subOptionName, "VK_KHR_bind_memory2"))
1222                     SetOption(currLineNumber, OPTION::Extension_VK_KHR_bind_memory2, csvSplit.GetRange(2));
1223                 else if(StrRangeEq(subOptionName, "VK_EXT_memory_budget"))
1224                     SetOption(currLineNumber, OPTION::Extension_VK_EXT_memory_budget, csvSplit.GetRange(2));
1225                 else if(StrRangeEq(subOptionName, "VK_AMD_device_coherent_memory"))
1226                     SetOption(currLineNumber, OPTION::Extension_VK_AMD_device_coherent_memory, csvSplit.GetRange(2));
1227                 else
1228                     printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1229             }
1230             else
1231                 printf("Line %zu: Too few columns.\n", currLineNumber);
1232         }
1233         else if(StrRangeEq(optionName, "Macro"))
1234         {
1235             if(csvSplit.GetCount() >= 3)
1236             {
1237                 const StrRange subOptionName = csvSplit.GetRange(1);
1238                 if(StrRangeEq(subOptionName, "VMA_DEBUG_ALWAYS_DEDICATED_MEMORY"))
1239                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_ALWAYS_DEDICATED_MEMORY, csvSplit.GetRange(2));
1240                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_ALIGNMENT"))
1241                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_ALIGNMENT, csvSplit.GetRange(2));
1242                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_MARGIN"))
1243                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_MARGIN, csvSplit.GetRange(2));
1244                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_INITIALIZE_ALLOCATIONS"))
1245                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_INITIALIZE_ALLOCATIONS, csvSplit.GetRange(2));
1246                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_DETECT_CORRUPTION"))
1247                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_DETECT_CORRUPTION, csvSplit.GetRange(2));
1248                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_GLOBAL_MUTEX"))
1249                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_GLOBAL_MUTEX, csvSplit.GetRange(2));
1250                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY"))
1251                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY, csvSplit.GetRange(2));
1252                 else if(StrRangeEq(subOptionName, "VMA_SMALL_HEAP_MAX_SIZE"))
1253                     SetOption(currLineNumber, OPTION::Macro_VMA_SMALL_HEAP_MAX_SIZE, csvSplit.GetRange(2));
1254                 else if(StrRangeEq(subOptionName, "VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE"))
1255                     SetOption(currLineNumber, OPTION::Macro_VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE, csvSplit.GetRange(2));
1256                 else
1257                     printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1258             }
1259             else
1260                 printf("Line %zu: Too few columns.\n", currLineNumber);
1261         }
1262         else if(StrRangeEq(optionName, "PhysicalDeviceMemory"))
1263         {
1264             uint32_t value = 0;
1265             if(csvSplit.GetCount() == 3 && StrRangeEq(csvSplit.GetRange(1), "HeapCount") &&
1266                 StrRangeToUint(csvSplit.GetRange(2), value))
1267             {
1268                 m_MemProps.memoryHeapCount = value;
1269             }
1270             else if(csvSplit.GetCount() == 3 && StrRangeEq(csvSplit.GetRange(1), "TypeCount") &&
1271                 StrRangeToUint(csvSplit.GetRange(2), value))
1272             {
1273                 m_MemProps.memoryTypeCount = value;
1274             }
1275             else if(csvSplit.GetCount() == 5 && StrRangeEq(csvSplit.GetRange(1), "Heap") &&
1276                 StrRangeToUint(csvSplit.GetRange(2), value) &&
1277                 value < m_MemProps.memoryHeapCount)
1278             {
1279                 if(StrRangeEq(csvSplit.GetRange(3), "size") &&
1280                     StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryHeaps[value].size))
1281                 {
1282                      // Parsed.
1283                 }
1284                 else if(StrRangeEq(csvSplit.GetRange(3), "flags") &&
1285                     StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryHeaps[value].flags))
1286                 {
1287                      // Parsed.
1288                 }
1289                 else
1290                     printf("Line %zu: Invalid configuration option.\n", currLineNumber);
1291             }
1292             else if(csvSplit.GetCount() == 5 && StrRangeEq(csvSplit.GetRange(1), "Type") &&
1293                 StrRangeToUint(csvSplit.GetRange(2), value) &&
1294                 value < m_MemProps.memoryTypeCount)
1295             {
1296                 if(StrRangeEq(csvSplit.GetRange(3), "heapIndex") &&
1297                     StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryTypes[value].heapIndex))
1298                 {
1299                      // Parsed.
1300                 }
1301                 else if(StrRangeEq(csvSplit.GetRange(3), "propertyFlags") &&
1302                     StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryTypes[value].propertyFlags))
1303                 {
1304                      // Parsed.
1305                 }
1306                 else
1307                     printf("Line %zu: Invalid configuration option.\n", currLineNumber);
1308             }
1309             else
1310                 printf("Line %zu: Invalid configuration option.\n", currLineNumber);
1311         }
1312         else
1313             printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1314     }
1315 
1316     return true;
1317 }
1318 
Compare(const VkPhysicalDeviceProperties & currDevProps,const VkPhysicalDeviceMemoryProperties & currMemProps,uint32_t vulkanApiVersion,bool currMemoryBudgetEnabled)1319 void ConfigurationParser::Compare(
1320     const VkPhysicalDeviceProperties& currDevProps,
1321     const VkPhysicalDeviceMemoryProperties& currMemProps,
1322     uint32_t vulkanApiVersion,
1323     bool currMemoryBudgetEnabled)
1324 {
1325     char vulkanApiVersionStr[32];
1326     sprintf_s(vulkanApiVersionStr, "%u,%u", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
1327     CompareOption(VERBOSITY::DEFAULT, "VulkanApiVersion",
1328         OPTION::VulkanApiVersion, vulkanApiVersionStr);
1329 
1330     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice apiVersion",
1331         OPTION::PhysicalDevice_apiVersion, currDevProps.apiVersion);
1332     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice driverVersion",
1333         OPTION::PhysicalDevice_driverVersion, currDevProps.driverVersion);
1334     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice vendorID",
1335         OPTION::PhysicalDevice_vendorID, currDevProps.vendorID);
1336     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceID",
1337         OPTION::PhysicalDevice_deviceID, currDevProps.deviceID);
1338     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceType",
1339         OPTION::PhysicalDevice_deviceType, (uint32_t)currDevProps.deviceType);
1340     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceName",
1341         OPTION::PhysicalDevice_deviceName, currDevProps.deviceName);
1342 
1343     CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits maxMemoryAllocationCount",
1344         OPTION::PhysicalDeviceLimits_maxMemoryAllocationCount, currDevProps.limits.maxMemoryAllocationCount);
1345     CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits bufferImageGranularity",
1346         OPTION::PhysicalDeviceLimits_bufferImageGranularity, currDevProps.limits.bufferImageGranularity);
1347     CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits nonCoherentAtomSize",
1348         OPTION::PhysicalDeviceLimits_nonCoherentAtomSize, currDevProps.limits.nonCoherentAtomSize);
1349 
1350     CompareMemProps(currMemProps);
1351 }
1352 
SetOption(size_t lineNumber,OPTION option,const StrRange & str)1353 void ConfigurationParser::SetOption(
1354     size_t lineNumber,
1355     OPTION option,
1356     const StrRange& str)
1357 {
1358     if(m_OptionSet[(size_t)option])
1359     {
1360         printf("Line %zu: Option already specified.\n" ,lineNumber);
1361     }
1362 
1363     m_OptionSet[(size_t)option] = true;
1364 
1365     std::string val;
1366     str.to_str(val);
1367     m_OptionValue[(size_t)option] = std::move(val);
1368 }
1369 
EnsureWarningHeader()1370 void ConfigurationParser::EnsureWarningHeader()
1371 {
1372     if(!m_WarningHeaderPrinted)
1373     {
1374         printf("WARNING: Following configuration parameters don't match:\n");
1375         m_WarningHeaderPrinted = true;
1376     }
1377 }
1378 
CompareOption(VERBOSITY minVerbosity,const char * name,OPTION option,uint32_t currValue)1379 void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,
1380     OPTION option, uint32_t currValue)
1381 {
1382     if(m_OptionSet[(size_t)option] &&
1383         g_Verbosity >= minVerbosity)
1384     {
1385         uint32_t origValue;
1386         if(StrRangeToUint(StrRange(m_OptionValue[(size_t)option]), origValue))
1387         {
1388             if(origValue != currValue)
1389             {
1390                 EnsureWarningHeader();
1391                 printf("    %s: original %u, current %u\n", name, origValue, currValue);
1392             }
1393         }
1394     }
1395 }
1396 
CompareOption(VERBOSITY minVerbosity,const char * name,OPTION option,uint64_t currValue)1397 void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,
1398     OPTION option, uint64_t currValue)
1399 {
1400     if(m_OptionSet[(size_t)option] &&
1401         g_Verbosity >= minVerbosity)
1402     {
1403         uint64_t origValue;
1404         if(StrRangeToUint(StrRange(m_OptionValue[(size_t)option]), origValue))
1405         {
1406             if(origValue != currValue)
1407             {
1408                 EnsureWarningHeader();
1409                 printf("    %s: original %llu, current %llu\n", name, origValue, currValue);
1410             }
1411         }
1412     }
1413 }
1414 
CompareOption(VERBOSITY minVerbosity,const char * name,OPTION option,bool currValue)1415 void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,
1416     OPTION option, bool currValue)
1417 {
1418     if(m_OptionSet[(size_t)option] &&
1419         g_Verbosity >= minVerbosity)
1420     {
1421         bool origValue;
1422         if(StrRangeToBool(StrRange(m_OptionValue[(size_t)option]), origValue))
1423         {
1424             if(origValue != currValue)
1425             {
1426                 EnsureWarningHeader();
1427                 printf("    %s: original %u, current %u\n", name,
1428                     origValue ? 1 : 0,
1429                     currValue ? 1 : 0);
1430             }
1431         }
1432     }
1433 }
1434 
CompareOption(VERBOSITY minVerbosity,const char * name,OPTION option,const char * currValue)1435 void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,
1436     OPTION option, const char* currValue)
1437 {
1438     if(m_OptionSet[(size_t)option] &&
1439         g_Verbosity >= minVerbosity)
1440     {
1441         const std::string& origValue = m_OptionValue[(size_t)option];
1442         if(origValue != currValue)
1443         {
1444             EnsureWarningHeader();
1445             printf("    %s: original \"%s\", current \"%s\"\n", name, origValue.c_str(), currValue);
1446         }
1447     }
1448 }
1449 
CompareMemProps(const VkPhysicalDeviceMemoryProperties & currMemProps)1450 void ConfigurationParser::CompareMemProps(
1451     const VkPhysicalDeviceMemoryProperties& currMemProps)
1452 {
1453     if(g_Verbosity < VERBOSITY::DEFAULT)
1454     {
1455         return;
1456     }
1457 
1458     bool memoryMatch =
1459         currMemProps.memoryHeapCount == m_MemProps.memoryHeapCount &&
1460         currMemProps.memoryTypeCount == m_MemProps.memoryTypeCount;
1461 
1462     for(uint32_t i = 0; memoryMatch && i < currMemProps.memoryHeapCount; ++i)
1463     {
1464         memoryMatch =
1465             currMemProps.memoryHeaps[i].flags == m_MemProps.memoryHeaps[i].flags;
1466     }
1467     for(uint32_t i = 0; memoryMatch && i < currMemProps.memoryTypeCount; ++i)
1468     {
1469         memoryMatch =
1470             currMemProps.memoryTypes[i].heapIndex == m_MemProps.memoryTypes[i].heapIndex &&
1471             currMemProps.memoryTypes[i].propertyFlags == m_MemProps.memoryTypes[i].propertyFlags;
1472     }
1473 
1474     if(memoryMatch && g_Verbosity == VERBOSITY::MAXIMUM)
1475     {
1476         bool memorySizeMatch = true;
1477         for(uint32_t i = 0; memorySizeMatch && i < currMemProps.memoryHeapCount; ++i)
1478         {
1479             memorySizeMatch =
1480                 currMemProps.memoryHeaps[i].size == m_MemProps.memoryHeaps[i].size;
1481         }
1482 
1483         if(!memorySizeMatch)
1484         {
1485             printf("WARNING: Sizes of original memory heaps are different from current ones.\n");
1486         }
1487     }
1488     else
1489     {
1490         printf("WARNING: Layout of original memory heaps and types is different from current one.\n");
1491     }
1492 }
1493 
1494 ////////////////////////////////////////////////////////////////////////////////
1495 // class Player
1496 
1497 static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_LUNARG_standard_validation";
1498 
1499 static const bool g_MemoryAliasingWarningEnabled = false;
1500 
MyDebugReportCallback(VkDebugReportFlagsEXT flags,VkDebugReportObjectTypeEXT objectType,uint64_t object,size_t location,int32_t messageCode,const char * pLayerPrefix,const char * pMessage,void * pUserData)1501 static VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback(
1502     VkDebugReportFlagsEXT flags,
1503     VkDebugReportObjectTypeEXT objectType,
1504     uint64_t object,
1505     size_t location,
1506     int32_t messageCode,
1507     const char* pLayerPrefix,
1508     const char* pMessage,
1509     void* pUserData)
1510 {
1511     // "Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug."
1512     if(!g_MemoryAliasingWarningEnabled && flags == VK_DEBUG_REPORT_WARNING_BIT_EXT &&
1513         (strstr(pMessage, " is aliased with non-linear ") || strstr(pMessage, " is aliased with linear ")))
1514     {
1515         return VK_FALSE;
1516     }
1517 
1518     // Ignoring because when VK_KHR_dedicated_allocation extension is enabled,
1519     // vkGetBufferMemoryRequirements2KHR function is used instead, while Validation
1520     // Layer seems to be unaware of it.
1521     if (strstr(pMessage, "but vkGetBufferMemoryRequirements() has not been called on that buffer") != nullptr)
1522     {
1523         return VK_FALSE;
1524     }
1525     if (strstr(pMessage, "but vkGetImageMemoryRequirements() has not been called on that image") != nullptr)
1526     {
1527         return VK_FALSE;
1528     }
1529 
1530     /*
1531     "Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used."
1532     Ignoring because we map entire VkDeviceMemory blocks, where different types of
1533     images and buffers may end up together, especially on GPUs with unified memory
1534     like Intel.
1535     */
1536     if(strstr(pMessage, "Mapping an image with layout") != nullptr &&
1537         strstr(pMessage, "can result in undefined behavior if this memory is used by the device") != nullptr)
1538     {
1539         return VK_FALSE;
1540     }
1541 
1542     printf("%s \xBA %s\n", pLayerPrefix, pMessage);
1543 
1544     return VK_FALSE;
1545 }
1546 
IsLayerSupported(const VkLayerProperties * pProps,size_t propCount,const char * pLayerName)1547 static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, const char* pLayerName)
1548 {
1549     const VkLayerProperties* propsEnd = pProps + propCount;
1550     return std::find_if(
1551         pProps,
1552         propsEnd,
1553         [pLayerName](const VkLayerProperties& prop) -> bool {
1554             return strcmp(pLayerName, prop.layerName) == 0;
1555         }) != propsEnd;
1556 }
1557 
1558 static const size_t FIRST_PARAM_INDEX = 4;
1559 
InitVulkanFeatures(VkPhysicalDeviceFeatures & outFeatures,const VkPhysicalDeviceFeatures & supportedFeatures)1560 static void InitVulkanFeatures(
1561     VkPhysicalDeviceFeatures& outFeatures,
1562     const VkPhysicalDeviceFeatures& supportedFeatures)
1563 {
1564     ZeroMemory(&outFeatures, sizeof(outFeatures));
1565 
1566     // Enable something what may interact with memory/buffer/image support.
1567 
1568     outFeatures.fullDrawIndexUint32 = supportedFeatures.fullDrawIndexUint32;
1569     outFeatures.imageCubeArray = supportedFeatures.imageCubeArray;
1570     outFeatures.geometryShader = supportedFeatures.geometryShader;
1571     outFeatures.tessellationShader = supportedFeatures.tessellationShader;
1572     outFeatures.multiDrawIndirect = supportedFeatures.multiDrawIndirect;
1573     outFeatures.textureCompressionETC2 = supportedFeatures.textureCompressionETC2;
1574     outFeatures.textureCompressionASTC_LDR = supportedFeatures.textureCompressionASTC_LDR;
1575     outFeatures.textureCompressionBC = supportedFeatures.textureCompressionBC;
1576 }
1577 
1578 class Player
1579 {
1580 public:
1581     Player();
1582     int Init();
1583     ~Player();
1584 
1585     void ApplyConfig(ConfigurationParser& configParser);
1586     void ExecuteLine(size_t lineNumber, const StrRange& line);
1587     void DumpStats(const char* fileNameFormat, size_t lineNumber, bool detailed);
1588     void Defragment();
1589 
1590     void PrintStats();
1591 
1592 private:
1593     static const size_t MAX_WARNINGS_TO_SHOW = 64;
1594 
1595     size_t m_WarningCount = 0;
1596     bool m_AllocateForBufferImageWarningIssued = false;
1597 
1598     VkInstance m_VulkanInstance = VK_NULL_HANDLE;
1599     VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE;
1600     uint32_t m_GraphicsQueueFamilyIndex = UINT32_MAX;
1601     uint32_t m_TransferQueueFamilyIndex = UINT32_MAX;
1602     VkDevice m_Device = VK_NULL_HANDLE;
1603     VkQueue m_GraphicsQueue = VK_NULL_HANDLE;
1604     VkQueue m_TransferQueue = VK_NULL_HANDLE;
1605     VmaAllocator m_Allocator = VK_NULL_HANDLE;
1606     VkCommandPool m_CommandPool = VK_NULL_HANDLE;
1607     VkCommandBuffer m_CommandBuffer = VK_NULL_HANDLE;
1608     bool m_MemoryBudgetEnabled = false;
1609     const VkPhysicalDeviceProperties* m_DevProps = nullptr;
1610     const VkPhysicalDeviceMemoryProperties* m_MemProps = nullptr;
1611 
1612     PFN_vkCreateDebugReportCallbackEXT m_pvkCreateDebugReportCallbackEXT;
1613     PFN_vkDebugReportMessageEXT m_pvkDebugReportMessageEXT;
1614     PFN_vkDestroyDebugReportCallbackEXT m_pvkDestroyDebugReportCallbackEXT;
1615     VkDebugReportCallbackEXT m_hCallback;
1616 
1617     uint32_t m_VmaFrameIndex = 0;
1618 
1619     // Any of these handles null can mean it was created in original but couldn't be created now.
1620     struct Pool
1621     {
1622         VmaPool pool;
1623     };
1624     struct Allocation
1625     {
1626         uint32_t allocationFlags = 0;
1627         VmaAllocation allocation = VK_NULL_HANDLE;
1628         VkBuffer buffer = VK_NULL_HANDLE;
1629         VkImage image = VK_NULL_HANDLE;
1630     };
1631     std::unordered_map<uint64_t, Pool> m_Pools;
1632     std::unordered_map<uint64_t, Allocation> m_Allocations;
1633     std::unordered_map<uint64_t, VmaDefragmentationContext> m_DefragmentationContexts;
1634 
1635     struct Thread
1636     {
1637         uint32_t callCount;
1638     };
1639     std::unordered_map<uint32_t, Thread> m_Threads;
1640 
1641     // Copy of column [1] from previously parsed line.
1642     std::string m_LastLineTimeStr;
1643     Statistics m_Stats;
1644 
1645     std::vector<char> m_UserDataTmpStr;
1646 
1647     void Destroy(const Allocation& alloc);
1648 
1649     // Finds VmaPool bu original pointer.
1650     // If origPool = null, returns true and outPool = null.
1651     // If failed, prints warning, returns false and outPool = null.
1652     bool FindPool(size_t lineNumber, uint64_t origPool, VmaPool& outPool);
1653     // If allocation with that origPtr already exists, prints warning and replaces it.
1654     void AddAllocation(size_t lineNumber, uint64_t origPtr, VkResult res, const char* functionName, Allocation&& allocDesc);
1655 
1656     // Increments warning counter. Returns true if warning message should be printed.
1657     bool IssueWarning();
1658 
1659     int InitVulkan();
1660     void FinalizeVulkan();
1661     void RegisterDebugCallbacks();
1662 
1663     // If parmeter count doesn't match, issues warning and returns false.
1664     bool ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound);
1665 
1666     // If failed, prints warning, returns false, and sets allocCreateInfo.pUserData to null.
1667     bool PrepareUserData(size_t lineNumber, uint32_t allocCreateFlags, const StrRange& userDataColumn, const StrRange& wholeLine, void*& outUserData);
1668 
1669     void UpdateMemStats();
1670 
1671     void ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit);
1672     void ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit);
1673     void ExecuteSetAllocationUserData(size_t lineNumber, const CsvSplit& csvSplit);
1674     void ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit);
ExecuteDestroyBuffer(size_t lineNumber,const CsvSplit & csvSplit)1675     void ExecuteDestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyBuffer); DestroyAllocation(lineNumber, csvSplit, "vmaDestroyBuffer"); }
1676     void ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit);
ExecuteDestroyImage(size_t lineNumber,const CsvSplit & csvSplit)1677     void ExecuteDestroyImage(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyImage); DestroyAllocation(lineNumber, csvSplit, "vmaDestroyImage"); }
ExecuteFreeMemory(size_t lineNumber,const CsvSplit & csvSplit)1678     void ExecuteFreeMemory(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::FreeMemory); DestroyAllocation(lineNumber, csvSplit, "vmaFreeMemory"); }
1679     void ExecuteFreeMemoryPages(size_t lineNumber, const CsvSplit& csvSplit);
1680     void ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1681     void ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit);
1682     void ExecuteAllocateMemoryPages(size_t lineNumber, const CsvSplit& csvSplit);
1683     void ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType);
1684     void ExecuteMapMemory(size_t lineNumber, const CsvSplit& csvSplit);
1685     void ExecuteUnmapMemory(size_t lineNumber, const CsvSplit& csvSplit);
1686     void ExecuteFlushAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1687     void ExecuteInvalidateAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1688     void ExecuteTouchAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1689     void ExecuteGetAllocationInfo(size_t lineNumber, const CsvSplit& csvSplit);
1690     void ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit);
1691     void ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1692     void ExecuteDefragmentationBegin(size_t lineNumber, const CsvSplit& csvSplit);
1693     void ExecuteDefragmentationEnd(size_t lineNumber, const CsvSplit& csvSplit);
1694     void ExecuteSetPoolName(size_t lineNumber, const CsvSplit& csvSplit);
1695 
1696     void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit, const char* functionName);
1697 
1698     void PrintStats(const VmaStats& stats, const char* suffix);
1699     void PrintStatInfo(const VmaStatInfo& info);
1700 };
1701 
Player()1702 Player::Player()
1703 {
1704 }
1705 
Init()1706 int Player::Init()
1707 {
1708     int result = InitVulkan();
1709 
1710     if(result == 0)
1711     {
1712         m_Stats.Init(m_MemProps->memoryHeapCount, m_MemProps->memoryTypeCount);
1713         UpdateMemStats();
1714     }
1715 
1716     return result;
1717 }
1718 
~Player()1719 Player::~Player()
1720 {
1721     FinalizeVulkan();
1722 
1723     if(g_Verbosity < VERBOSITY::MAXIMUM && m_WarningCount > MAX_WARNINGS_TO_SHOW)
1724         printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW);
1725 }
1726 
ApplyConfig(ConfigurationParser & configParser)1727 void Player::ApplyConfig(ConfigurationParser& configParser)
1728 {
1729     configParser.Compare(*m_DevProps, *m_MemProps,
1730         VULKAN_API_VERSION,
1731         m_MemoryBudgetEnabled);
1732 }
1733 
ExecuteLine(size_t lineNumber,const StrRange & line)1734 void Player::ExecuteLine(size_t lineNumber, const StrRange& line)
1735 {
1736     CsvSplit csvSplit;
1737     csvSplit.Set(line);
1738 
1739     if(csvSplit.GetCount() >= FIRST_PARAM_INDEX)
1740     {
1741         // Check thread ID.
1742         uint32_t threadId;
1743         if(StrRangeToUint(csvSplit.GetRange(0), threadId))
1744         {
1745             const auto it = m_Threads.find(threadId);
1746             if(it != m_Threads.end())
1747             {
1748                 ++it->second.callCount;
1749             }
1750             else
1751             {
1752                 Thread threadInfo{};
1753                 threadInfo.callCount = 1;
1754                 m_Threads[threadId] = threadInfo;
1755             }
1756         }
1757         else
1758         {
1759             if(IssueWarning())
1760             {
1761                 printf("Line %zu: Incorrect thread ID.\n", lineNumber);
1762             }
1763         }
1764 
1765         // Save time.
1766         csvSplit.GetRange(1).to_str(m_LastLineTimeStr);
1767 
1768         // Update VMA current frame index.
1769         StrRange frameIndexStr = csvSplit.GetRange(2);
1770         uint32_t frameIndex;
1771         if(StrRangeToUint(frameIndexStr, frameIndex))
1772         {
1773             if(frameIndex != m_VmaFrameIndex)
1774             {
1775                 vmaSetCurrentFrameIndex(m_Allocator, frameIndex);
1776                 m_VmaFrameIndex = frameIndex;
1777             }
1778         }
1779         else
1780         {
1781             if(IssueWarning())
1782             {
1783                 printf("Line %zu: Incorrect frame index.\n", lineNumber);
1784             }
1785         }
1786 
1787         StrRange functionName = csvSplit.GetRange(3);
1788 
1789         if(StrRangeEq(functionName, "vmaCreateAllocator"))
1790         {
1791             if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false))
1792             {
1793                 // Nothing.
1794             }
1795         }
1796         else if(StrRangeEq(functionName, "vmaDestroyAllocator"))
1797         {
1798             if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false))
1799             {
1800                 // Nothing.
1801             }
1802         }
1803         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreatePool]))
1804             ExecuteCreatePool(lineNumber, csvSplit);
1805         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyPool]))
1806             ExecuteDestroyPool(lineNumber, csvSplit);
1807         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::SetAllocationUserData]))
1808             ExecuteSetAllocationUserData(lineNumber, csvSplit);
1809         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateBuffer]))
1810             ExecuteCreateBuffer(lineNumber, csvSplit);
1811         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyBuffer]))
1812             ExecuteDestroyBuffer(lineNumber, csvSplit);
1813         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateImage]))
1814             ExecuteCreateImage(lineNumber, csvSplit);
1815         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyImage]))
1816             ExecuteDestroyImage(lineNumber, csvSplit);
1817         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FreeMemory]))
1818             ExecuteFreeMemory(lineNumber, csvSplit);
1819         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FreeMemoryPages]))
1820             ExecuteFreeMemoryPages(lineNumber, csvSplit);
1821         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateLostAllocation]))
1822             ExecuteCreateLostAllocation(lineNumber, csvSplit);
1823         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemory]))
1824             ExecuteAllocateMemory(lineNumber, csvSplit);
1825         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryPages]))
1826             ExecuteAllocateMemoryPages(lineNumber, csvSplit);
1827         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryForBuffer]))
1828             ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::BUFFER);
1829         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryForImage]))
1830             ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::IMAGE);
1831         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::MapMemory]))
1832             ExecuteMapMemory(lineNumber, csvSplit);
1833         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::UnmapMemory]))
1834             ExecuteUnmapMemory(lineNumber, csvSplit);
1835         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FlushAllocation]))
1836             ExecuteFlushAllocation(lineNumber, csvSplit);
1837         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::InvalidateAllocation]))
1838             ExecuteInvalidateAllocation(lineNumber, csvSplit);
1839         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::TouchAllocation]))
1840             ExecuteTouchAllocation(lineNumber, csvSplit);
1841         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::GetAllocationInfo]))
1842             ExecuteGetAllocationInfo(lineNumber, csvSplit);
1843         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::MakePoolAllocationsLost]))
1844             ExecuteMakePoolAllocationsLost(lineNumber, csvSplit);
1845         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::ResizeAllocation]))
1846             ExecuteResizeAllocation(lineNumber, csvSplit);
1847         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DefragmentationBegin]))
1848             ExecuteDefragmentationBegin(lineNumber, csvSplit);
1849         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DefragmentationEnd]))
1850             ExecuteDefragmentationEnd(lineNumber, csvSplit);
1851         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::SetPoolName]))
1852             ExecuteSetPoolName(lineNumber, csvSplit);
1853         else
1854         {
1855             if(IssueWarning())
1856             {
1857                 printf("Line %zu: Unknown function.\n", lineNumber);
1858             }
1859         }
1860     }
1861     else
1862     {
1863         if(IssueWarning())
1864         {
1865             printf("Line %zu: Too few columns.\n", lineNumber);
1866         }
1867     }
1868 }
1869 
DumpStats(const char * fileNameFormat,size_t lineNumber,bool detailed)1870 void Player::DumpStats(const char* fileNameFormat, size_t lineNumber, bool detailed)
1871 {
1872     char* pStatsString = nullptr;
1873     vmaBuildStatsString(m_Allocator, &pStatsString, detailed ? VK_TRUE : VK_FALSE);
1874 
1875     char fileName[MAX_PATH];
1876     sprintf_s(fileName, fileNameFormat, lineNumber);
1877 
1878     FILE* file = nullptr;
1879     errno_t err = fopen_s(&file, fileName, "wb");
1880     if(err == 0)
1881     {
1882         fwrite(pStatsString, 1, strlen(pStatsString), file);
1883         fclose(file);
1884     }
1885     else
1886     {
1887         printf("ERROR: Failed to write file: %s\n", fileName);
1888     }
1889 
1890     vmaFreeStatsString(m_Allocator, pStatsString);
1891 }
1892 
Destroy(const Allocation & alloc)1893 void Player::Destroy(const Allocation& alloc)
1894 {
1895     if(alloc.buffer)
1896     {
1897         assert(alloc.image == VK_NULL_HANDLE);
1898         vmaDestroyBuffer(m_Allocator, alloc.buffer, alloc.allocation);
1899     }
1900     else if(alloc.image)
1901     {
1902         vmaDestroyImage(m_Allocator, alloc.image, alloc.allocation);
1903     }
1904     else
1905         vmaFreeMemory(m_Allocator, alloc.allocation);
1906 }
1907 
FindPool(size_t lineNumber,uint64_t origPool,VmaPool & outPool)1908 bool Player::FindPool(size_t lineNumber, uint64_t origPool, VmaPool& outPool)
1909 {
1910     outPool = VK_NULL_HANDLE;
1911 
1912     if(origPool != 0)
1913     {
1914         const auto poolIt = m_Pools.find(origPool);
1915         if(poolIt != m_Pools.end())
1916         {
1917             outPool = poolIt->second.pool;
1918             return true;
1919         }
1920         else
1921         {
1922             if(IssueWarning())
1923             {
1924                 printf("Line %zu: Pool %llX not found.\n", lineNumber, origPool);
1925             }
1926         }
1927     }
1928 
1929     return true;
1930 }
1931 
AddAllocation(size_t lineNumber,uint64_t origPtr,VkResult res,const char * functionName,Allocation && allocDesc)1932 void Player::AddAllocation(size_t lineNumber, uint64_t origPtr, VkResult res, const char* functionName, Allocation&& allocDesc)
1933 {
1934     if(origPtr)
1935     {
1936         if(res == VK_SUCCESS)
1937         {
1938             // Originally succeeded, currently succeeded.
1939             // Just save pointer (done below).
1940         }
1941         else
1942         {
1943             // Originally succeeded, currently failed.
1944             // Print warning. Save null pointer.
1945             if(IssueWarning())
1946             {
1947                 printf("Line %zu: %s failed (%d), while originally succeeded.\n", lineNumber, functionName, res);
1948             }
1949         }
1950 
1951         const auto existingIt = m_Allocations.find(origPtr);
1952         if(existingIt != m_Allocations.end())
1953         {
1954             if(IssueWarning())
1955             {
1956                 printf("Line %zu: Allocation %llX already exists.\n", lineNumber, origPtr);
1957             }
1958         }
1959         m_Allocations[origPtr] = std::move(allocDesc);
1960     }
1961     else
1962     {
1963         if(res == VK_SUCCESS)
1964         {
1965             // Originally failed, currently succeeded.
1966             // Print warning, destroy the object.
1967             if(IssueWarning())
1968             {
1969                 printf("Line %zu: %s succeeded, originally failed.\n", lineNumber, functionName);
1970             }
1971 
1972             Destroy(allocDesc);
1973         }
1974         else
1975         {
1976             // Originally failed, currently failed.
1977             // Print warning.
1978             if(IssueWarning())
1979             {
1980                 printf("Line %zu: %s failed (%d), originally also failed.\n", lineNumber, functionName, res);
1981             }
1982         }
1983     }
1984 }
1985 
IssueWarning()1986 bool Player::IssueWarning()
1987 {
1988     if(g_Verbosity < VERBOSITY::MAXIMUM)
1989     {
1990         return m_WarningCount++ < MAX_WARNINGS_TO_SHOW;
1991     }
1992     else
1993     {
1994         ++m_WarningCount;
1995         return true;
1996     }
1997 }
1998 
InitVulkan()1999 int Player::InitVulkan()
2000 {
2001     if(g_Verbosity == VERBOSITY::MAXIMUM)
2002     {
2003         printf("Initializing Vulkan...\n");
2004     }
2005 
2006     uint32_t instanceLayerPropCount = 0;
2007     VkResult res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr);
2008     assert(res == VK_SUCCESS);
2009 
2010     std::vector<VkLayerProperties> instanceLayerProps(instanceLayerPropCount);
2011     if(instanceLayerPropCount > 0)
2012     {
2013         res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data());
2014         assert(res == VK_SUCCESS);
2015     }
2016 
2017     const bool validationLayersAvailable =
2018         IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME);
2019 
2020     bool validationLayersEnabled = false;
2021     switch(g_VK_LAYER_LUNARG_standard_validation)
2022     {
2023     case VULKAN_EXTENSION_REQUEST::DISABLED:
2024         break;
2025     case VULKAN_EXTENSION_REQUEST::DEFAULT:
2026         validationLayersEnabled = validationLayersAvailable;
2027         break;
2028     case VULKAN_EXTENSION_REQUEST::ENABLED:
2029         validationLayersEnabled = validationLayersAvailable;
2030         if(!validationLayersAvailable)
2031         {
2032             printf("WARNING: %s layer cannot be enabled.\n", VALIDATION_LAYER_NAME);
2033         }
2034         break;
2035     default: assert(0);
2036     }
2037 
2038     uint32_t availableInstanceExtensionCount = 0;
2039     res = vkEnumerateInstanceExtensionProperties(nullptr, &availableInstanceExtensionCount, nullptr);
2040     assert(res == VK_SUCCESS);
2041     std::vector<VkExtensionProperties> availableInstanceExtensions(availableInstanceExtensionCount);
2042     if(availableInstanceExtensionCount > 0)
2043     {
2044         res = vkEnumerateInstanceExtensionProperties(nullptr, &availableInstanceExtensionCount, availableInstanceExtensions.data());
2045         assert(res == VK_SUCCESS);
2046     }
2047 
2048     std::vector<const char*> enabledInstanceExtensions;
2049     //enabledInstanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
2050     //enabledInstanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
2051 
2052     std::vector<const char*> instanceLayers;
2053     if(validationLayersEnabled)
2054     {
2055         instanceLayers.push_back(VALIDATION_LAYER_NAME);
2056         enabledInstanceExtensions.push_back("VK_EXT_debug_report");
2057     }
2058 
2059     bool VK_KHR_get_physical_device_properties2_enabled = false;
2060     for(const auto& extensionProperties : availableInstanceExtensions)
2061     {
2062         if(strcmp(extensionProperties.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0)
2063         {
2064             enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
2065             VK_KHR_get_physical_device_properties2_enabled = true;
2066         }
2067     }
2068 
2069     VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
2070     appInfo.pApplicationName = "VmaReplay";
2071     appInfo.applicationVersion = VK_MAKE_VERSION(2, 3, 0);
2072     appInfo.pEngineName = "Vulkan Memory Allocator";
2073     appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 0);
2074     appInfo.apiVersion = VULKAN_API_VERSION;
2075 
2076     VkInstanceCreateInfo instInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
2077     instInfo.pApplicationInfo = &appInfo;
2078     instInfo.enabledExtensionCount = (uint32_t)enabledInstanceExtensions.size();
2079     instInfo.ppEnabledExtensionNames = enabledInstanceExtensions.data();
2080     instInfo.enabledLayerCount = (uint32_t)instanceLayers.size();
2081     instInfo.ppEnabledLayerNames = instanceLayers.data();
2082 
2083     res = vkCreateInstance(&instInfo, NULL, &m_VulkanInstance);
2084     if(res != VK_SUCCESS)
2085     {
2086         printf("ERROR: vkCreateInstance failed (%d)\n", res);
2087         return RESULT_ERROR_VULKAN;
2088     }
2089 
2090     if(validationLayersEnabled)
2091     {
2092         RegisterDebugCallbacks();
2093     }
2094 
2095     // Find physical device
2096 
2097     uint32_t physicalDeviceCount = 0;
2098     res = vkEnumeratePhysicalDevices(m_VulkanInstance, &physicalDeviceCount, nullptr);
2099     assert(res == VK_SUCCESS);
2100     if(physicalDeviceCount == 0)
2101     {
2102         printf("ERROR: No Vulkan physical devices found.\n");
2103         return RESULT_ERROR_VULKAN;
2104     }
2105 
2106     std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
2107     res = vkEnumeratePhysicalDevices(m_VulkanInstance, &physicalDeviceCount, physicalDevices.data());
2108     assert(res == VK_SUCCESS);
2109 
2110     if(g_PhysicalDeviceIndex >= physicalDeviceCount)
2111     {
2112         printf("ERROR: Incorrect Vulkan physical device index %u. System has %u physical devices.\n",
2113             g_PhysicalDeviceIndex,
2114             physicalDeviceCount);
2115         return RESULT_ERROR_VULKAN;
2116     }
2117 
2118     m_PhysicalDevice = physicalDevices[0];
2119 
2120     // Find queue family index
2121 
2122     uint32_t queueFamilyCount = 0;
2123     vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, nullptr);
2124     if(queueFamilyCount)
2125     {
2126         std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
2127         vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, queueFamilies.data());
2128         for(uint32_t i = 0; i < queueFamilyCount; ++i)
2129         {
2130             if(queueFamilies[i].queueCount > 0)
2131             {
2132                 if(m_GraphicsQueueFamilyIndex == UINT32_MAX &&
2133                     (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
2134                 {
2135                     m_GraphicsQueueFamilyIndex = i;
2136                 }
2137                 if(m_TransferQueueFamilyIndex == UINT32_MAX &&
2138                     (queueFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT) != 0)
2139                 {
2140                     m_TransferQueueFamilyIndex = i;
2141                 }
2142             }
2143         }
2144     }
2145     if(m_GraphicsQueueFamilyIndex == UINT_MAX)
2146     {
2147         printf("ERROR: Couldn't find graphics queue.\n");
2148         return RESULT_ERROR_VULKAN;
2149     }
2150     if(m_TransferQueueFamilyIndex == UINT_MAX)
2151     {
2152         printf("ERROR: Couldn't find transfer queue.\n");
2153         return RESULT_ERROR_VULKAN;
2154     }
2155 
2156     VkPhysicalDeviceFeatures supportedFeatures;
2157     vkGetPhysicalDeviceFeatures(m_PhysicalDevice, &supportedFeatures);
2158 
2159     // Create logical device
2160 
2161     const float queuePriority = 1.f;
2162 
2163     VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
2164     deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
2165     deviceQueueCreateInfo[0].queueFamilyIndex = m_GraphicsQueueFamilyIndex;
2166     deviceQueueCreateInfo[0].queueCount = 1;
2167     deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority;
2168 
2169     if(m_TransferQueueFamilyIndex != m_GraphicsQueueFamilyIndex)
2170     {
2171         deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
2172         deviceQueueCreateInfo[1].queueFamilyIndex = m_TransferQueueFamilyIndex;
2173         deviceQueueCreateInfo[1].queueCount = 1;
2174         deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority;
2175     }
2176 
2177     // Enable something what may interact with memory/buffer/image support.
2178     VkPhysicalDeviceFeatures enabledFeatures;
2179     InitVulkanFeatures(enabledFeatures, supportedFeatures);
2180 
2181     bool VK_KHR_get_memory_requirements2_available = false;
2182 
2183     // Determine list of device extensions to enable.
2184     std::vector<const char*> enabledDeviceExtensions;
2185     //enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
2186     bool memoryBudgetAvailable = false;
2187     {
2188         uint32_t propertyCount = 0;
2189         res = vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, nullptr);
2190         assert(res == VK_SUCCESS);
2191 
2192         if(propertyCount)
2193         {
2194             std::vector<VkExtensionProperties> properties{propertyCount};
2195             res = vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, properties.data());
2196             assert(res == VK_SUCCESS);
2197 
2198             for(uint32_t i = 0; i < propertyCount; ++i)
2199             {
2200                 if(strcmp(properties[i].extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME) == 0)
2201                 {
2202                     VK_KHR_get_memory_requirements2_available = true;
2203                 }
2204                 else if(strcmp(properties[i].extensionName, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) == 0)
2205                 {
2206                     if(VK_KHR_get_physical_device_properties2_enabled)
2207                     {
2208                         memoryBudgetAvailable = true;
2209                     }
2210                 }
2211             }
2212         }
2213     }
2214 
2215     switch(g_VK_EXT_memory_budget_request)
2216     {
2217     case VULKAN_EXTENSION_REQUEST::DISABLED:
2218         break;
2219     case VULKAN_EXTENSION_REQUEST::DEFAULT:
2220         m_MemoryBudgetEnabled = memoryBudgetAvailable;
2221         break;
2222     case VULKAN_EXTENSION_REQUEST::ENABLED:
2223         m_MemoryBudgetEnabled = memoryBudgetAvailable;
2224         if(!memoryBudgetAvailable)
2225         {
2226             printf("WARNING: VK_EXT_memory_budget extension cannot be enabled.\n");
2227         }
2228         break;
2229     default: assert(0);
2230     }
2231 
2232     if(g_VK_AMD_device_coherent_memory_request == VULKAN_EXTENSION_REQUEST::ENABLED)
2233     {
2234         printf("WARNING: AMD_device_coherent_memory requested but not currently supported by the player.\n");
2235     }
2236 
2237     if(m_MemoryBudgetEnabled)
2238     {
2239         enabledDeviceExtensions.push_back(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
2240     }
2241 
2242     VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
2243     deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size();
2244     deviceCreateInfo.ppEnabledExtensionNames = !enabledDeviceExtensions.empty() ? enabledDeviceExtensions.data() : nullptr;
2245     deviceCreateInfo.queueCreateInfoCount = m_TransferQueueFamilyIndex != m_GraphicsQueueFamilyIndex ? 2 : 1;
2246     deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
2247     deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
2248 
2249     res = vkCreateDevice(m_PhysicalDevice, &deviceCreateInfo, nullptr, &m_Device);
2250     if(res != VK_SUCCESS)
2251     {
2252         printf("ERROR: vkCreateDevice failed (%d)\n", res);
2253         return RESULT_ERROR_VULKAN;
2254     }
2255 
2256     // Fetch queues
2257     vkGetDeviceQueue(m_Device, m_GraphicsQueueFamilyIndex, 0, &m_GraphicsQueue);
2258     vkGetDeviceQueue(m_Device, m_TransferQueueFamilyIndex, 0, &m_TransferQueue);
2259 
2260     // Create memory allocator
2261 
2262     VmaDeviceMemoryCallbacks deviceMemoryCallbacks = {};
2263     deviceMemoryCallbacks.pfnAllocate = AllocateDeviceMemoryCallback;
2264     deviceMemoryCallbacks.pfnFree = FreeDeviceMemoryCallback;
2265 
2266     VmaAllocatorCreateInfo allocatorInfo = {};
2267     allocatorInfo.instance = m_VulkanInstance;
2268     allocatorInfo.physicalDevice = m_PhysicalDevice;
2269     allocatorInfo.device = m_Device;
2270     allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
2271     allocatorInfo.pDeviceMemoryCallbacks = &deviceMemoryCallbacks;
2272     allocatorInfo.vulkanApiVersion = VULKAN_API_VERSION;
2273 
2274     if(m_MemoryBudgetEnabled)
2275     {
2276         allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
2277     }
2278 
2279     res = vmaCreateAllocator(&allocatorInfo, &m_Allocator);
2280     if(res != VK_SUCCESS)
2281     {
2282         printf("ERROR: vmaCreateAllocator failed (%d)\n", res);
2283         return RESULT_ERROR_VULKAN;
2284     }
2285 
2286     vmaGetPhysicalDeviceProperties(m_Allocator, &m_DevProps);
2287     vmaGetMemoryProperties(m_Allocator, &m_MemProps);
2288 
2289     // Create command pool
2290 
2291     VkCommandPoolCreateInfo cmdPoolCreateInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
2292     cmdPoolCreateInfo.queueFamilyIndex = m_TransferQueueFamilyIndex;
2293     cmdPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
2294 
2295     res = vkCreateCommandPool(m_Device, &cmdPoolCreateInfo, nullptr, &m_CommandPool);
2296     if(res != VK_SUCCESS)
2297     {
2298         printf("ERROR: vkCreateCommandPool failed (%d)\n", res);
2299         return RESULT_ERROR_VULKAN;
2300     }
2301 
2302     // Create command buffer
2303 
2304     VkCommandBufferAllocateInfo cmdBufAllocInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
2305     cmdBufAllocInfo.commandBufferCount = 1;
2306     cmdBufAllocInfo.commandPool = m_CommandPool;
2307     cmdBufAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
2308     res = vkAllocateCommandBuffers(m_Device, &cmdBufAllocInfo, &m_CommandBuffer);
2309     if(res != VK_SUCCESS)
2310     {
2311         printf("ERROR: vkAllocateCommandBuffers failed (%d)\n", res);
2312         return RESULT_ERROR_VULKAN;
2313     }
2314 
2315     return 0;
2316 }
2317 
FinalizeVulkan()2318 void Player::FinalizeVulkan()
2319 {
2320     if(!m_DefragmentationContexts.empty())
2321     {
2322         printf("WARNING: Defragmentation contexts not destroyed: %zu.\n", m_DefragmentationContexts.size());
2323 
2324         if(CLEANUP_LEAKED_OBJECTS)
2325         {
2326             for(const auto& it : m_DefragmentationContexts)
2327             {
2328                 vmaDefragmentationEnd(m_Allocator, it.second);
2329             }
2330         }
2331 
2332         m_DefragmentationContexts.clear();
2333     }
2334 
2335     if(!m_Allocations.empty())
2336     {
2337         printf("WARNING: Allocations not destroyed: %zu.\n", m_Allocations.size());
2338 
2339         if(CLEANUP_LEAKED_OBJECTS)
2340         {
2341             for(const auto it : m_Allocations)
2342             {
2343                 Destroy(it.second);
2344             }
2345         }
2346 
2347         m_Allocations.clear();
2348     }
2349 
2350     if(!m_Pools.empty())
2351     {
2352         printf("WARNING: Custom pools not destroyed: %zu.\n", m_Pools.size());
2353 
2354         if(CLEANUP_LEAKED_OBJECTS)
2355         {
2356             for(const auto it : m_Pools)
2357             {
2358                 vmaDestroyPool(m_Allocator, it.second.pool);
2359             }
2360         }
2361 
2362         m_Pools.clear();
2363     }
2364 
2365     vkDeviceWaitIdle(m_Device);
2366 
2367     if(m_CommandBuffer != VK_NULL_HANDLE)
2368     {
2369         vkFreeCommandBuffers(m_Device, m_CommandPool, 1, &m_CommandBuffer);
2370         m_CommandBuffer = VK_NULL_HANDLE;
2371     }
2372 
2373     if(m_CommandPool != VK_NULL_HANDLE)
2374     {
2375         vkDestroyCommandPool(m_Device, m_CommandPool, nullptr);
2376         m_CommandPool = VK_NULL_HANDLE;
2377     }
2378 
2379     if(m_Allocator != VK_NULL_HANDLE)
2380     {
2381         vmaDestroyAllocator(m_Allocator);
2382         m_Allocator = nullptr;
2383     }
2384 
2385     if(m_Device != VK_NULL_HANDLE)
2386     {
2387         vkDestroyDevice(m_Device, nullptr);
2388         m_Device = nullptr;
2389     }
2390 
2391     if(m_pvkDestroyDebugReportCallbackEXT && m_hCallback != VK_NULL_HANDLE)
2392     {
2393         m_pvkDestroyDebugReportCallbackEXT(m_VulkanInstance, m_hCallback, nullptr);
2394         m_hCallback = VK_NULL_HANDLE;
2395     }
2396 
2397     if(m_VulkanInstance != VK_NULL_HANDLE)
2398     {
2399         vkDestroyInstance(m_VulkanInstance, NULL);
2400         m_VulkanInstance = VK_NULL_HANDLE;
2401     }
2402 }
2403 
RegisterDebugCallbacks()2404 void Player::RegisterDebugCallbacks()
2405 {
2406     m_pvkCreateDebugReportCallbackEXT =
2407         reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>
2408             (vkGetInstanceProcAddr(m_VulkanInstance, "vkCreateDebugReportCallbackEXT"));
2409     m_pvkDebugReportMessageEXT =
2410         reinterpret_cast<PFN_vkDebugReportMessageEXT>
2411             (vkGetInstanceProcAddr(m_VulkanInstance, "vkDebugReportMessageEXT"));
2412     m_pvkDestroyDebugReportCallbackEXT =
2413         reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>
2414             (vkGetInstanceProcAddr(m_VulkanInstance, "vkDestroyDebugReportCallbackEXT"));
2415     assert(m_pvkCreateDebugReportCallbackEXT);
2416     assert(m_pvkDebugReportMessageEXT);
2417     assert(m_pvkDestroyDebugReportCallbackEXT);
2418 
2419     VkDebugReportCallbackCreateInfoEXT callbackCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT };
2420     callbackCreateInfo.flags = //VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
2421         VK_DEBUG_REPORT_ERROR_BIT_EXT |
2422         VK_DEBUG_REPORT_WARNING_BIT_EXT |
2423         VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT /*|
2424         VK_DEBUG_REPORT_DEBUG_BIT_EXT*/;
2425     callbackCreateInfo.pfnCallback = &MyDebugReportCallback;
2426 
2427     VkResult res = m_pvkCreateDebugReportCallbackEXT(m_VulkanInstance, &callbackCreateInfo, nullptr, &m_hCallback);
2428     assert(res == VK_SUCCESS);
2429 }
2430 
Defragment()2431 void Player::Defragment()
2432 {
2433     VmaStats stats;
2434     vmaCalculateStats(m_Allocator, &stats);
2435     PrintStats(stats, "before defragmentation");
2436 
2437     const size_t allocCount = m_Allocations.size();
2438     std::vector<VmaAllocation> allocations(allocCount);
2439     size_t notNullAllocCount = 0;
2440     for(const auto& it : m_Allocations)
2441     {
2442         if(it.second.allocation != VK_NULL_HANDLE)
2443         {
2444             allocations[notNullAllocCount] = it.second.allocation;
2445             ++notNullAllocCount;
2446         }
2447     }
2448     if(notNullAllocCount == 0)
2449     {
2450         printf("    Nothing to defragment.\n");
2451         return;
2452     }
2453 
2454     allocations.resize(notNullAllocCount);
2455     std::vector<VkBool32> allocationsChanged(notNullAllocCount);
2456 
2457     VmaDefragmentationStats defragStats = {};
2458 
2459     VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
2460     cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
2461     VkResult res = vkBeginCommandBuffer(m_CommandBuffer, &cmdBufBeginInfo);
2462     if(res != VK_SUCCESS)
2463     {
2464         printf("ERROR: vkBeginCommandBuffer failed (%d)\n", res);
2465         return;
2466     }
2467 
2468     const time_point timeBeg = std::chrono::high_resolution_clock::now();
2469 
2470     VmaDefragmentationInfo2 defragInfo = {};
2471     defragInfo.allocationCount = (uint32_t)notNullAllocCount;
2472     defragInfo.pAllocations = allocations.data();
2473     defragInfo.pAllocationsChanged = allocationsChanged.data();
2474     defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
2475     defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
2476     defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2477     defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2478     defragInfo.flags = g_DefragmentationFlags;
2479     defragInfo.commandBuffer = m_CommandBuffer;
2480 
2481     VmaDefragmentationContext defragCtx = VK_NULL_HANDLE;
2482     res = vmaDefragmentationBegin(m_Allocator, &defragInfo, &defragStats, &defragCtx);
2483 
2484     const time_point timeAfterDefragBegin = std::chrono::high_resolution_clock::now();
2485 
2486     vkEndCommandBuffer(m_CommandBuffer);
2487 
2488     if(res >= VK_SUCCESS)
2489     {
2490         VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
2491         submitInfo.commandBufferCount = 1;
2492         submitInfo.pCommandBuffers = &m_CommandBuffer;
2493         vkQueueSubmit(m_TransferQueue, 1, &submitInfo, VK_NULL_HANDLE);
2494         vkQueueWaitIdle(m_TransferQueue);
2495 
2496         const time_point timeAfterGpu = std::chrono::high_resolution_clock::now();
2497 
2498         vmaDefragmentationEnd(m_Allocator, defragCtx);
2499 
2500         const time_point timeAfterDefragEnd = std::chrono::high_resolution_clock::now();
2501 
2502         const duration defragDurationBegin = timeAfterDefragBegin - timeBeg;
2503         const duration defragDurationGpu   = timeAfterGpu - timeAfterDefragBegin;
2504         const duration defragDurationEnd   = timeAfterDefragEnd - timeAfterGpu;
2505 
2506         // If anything changed.
2507         if(defragStats.allocationsMoved > 0)
2508         {
2509             // Go over allocation that changed and destroy their buffers and images.
2510             size_t i = 0;
2511             for(auto& it : m_Allocations)
2512             {
2513                 if(allocationsChanged[i] != VK_FALSE)
2514                 {
2515                     if(it.second.buffer != VK_NULL_HANDLE)
2516                     {
2517                         vkDestroyBuffer(m_Device, it.second.buffer, nullptr);
2518                         it.second.buffer = VK_NULL_HANDLE;
2519                     }
2520                     if(it.second.image != VK_NULL_HANDLE)
2521                     {
2522                         vkDestroyImage(m_Device, it.second.image, nullptr);
2523                         it.second.image = VK_NULL_HANDLE;
2524                     }
2525                 }
2526                 ++i;
2527             }
2528         }
2529 
2530         // Print statistics
2531         std::string defragDurationBeginStr;
2532         std::string defragDurationGpuStr;
2533         std::string defragDurationEndStr;
2534         SecondsToFriendlyStr(ToFloatSeconds(defragDurationBegin), defragDurationBeginStr);
2535         SecondsToFriendlyStr(ToFloatSeconds(defragDurationGpu), defragDurationGpuStr);
2536         SecondsToFriendlyStr(ToFloatSeconds(defragDurationEnd), defragDurationEndStr);
2537 
2538         printf("    Defragmentation took:\n");
2539         printf("        vmaDefragmentationBegin: %s\n", defragDurationBeginStr.c_str());
2540         printf("        GPU: %s\n", defragDurationGpuStr.c_str());
2541         printf("        vmaDefragmentationEnd: %s\n", defragDurationEndStr.c_str());
2542         printf("    VmaDefragmentationStats:\n");
2543         printf("        bytesMoved: %llu\n", defragStats.bytesMoved);
2544         printf("        bytesFreed: %llu\n", defragStats.bytesFreed);
2545         printf("        allocationsMoved: %u\n", defragStats.allocationsMoved);
2546         printf("        deviceMemoryBlocksFreed: %u\n", defragStats.deviceMemoryBlocksFreed);
2547 
2548         vmaCalculateStats(m_Allocator, &stats);
2549         PrintStats(stats, "after defragmentation");
2550     }
2551     else
2552     {
2553         printf("vmaDefragmentationBegin failed (%d).\n", res);
2554     }
2555 
2556     vkResetCommandPool(m_Device, m_CommandPool, 0);
2557 }
2558 
PrintStats()2559 void Player::PrintStats()
2560 {
2561     if(g_Verbosity == VERBOSITY::MINIMUM)
2562     {
2563         return;
2564     }
2565 
2566     m_Stats.PrintDeviceMemStats();
2567 
2568     printf("Statistics:\n");
2569     if(m_Stats.GetAllocationCreationCount() > 0)
2570     {
2571         printf("    Total allocations created: %zu\n", m_Stats.GetAllocationCreationCount());
2572     }
2573 
2574     // Buffers
2575     if(m_Stats.GetBufferCreationCount())
2576     {
2577         printf("    Total buffers created: %zu\n", m_Stats.GetBufferCreationCount());
2578         if(g_Verbosity == VERBOSITY::MAXIMUM)
2579         {
2580             printf("        Class 0 (indirect/vertex/index): %zu\n", m_Stats.GetBufferCreationCount(0));
2581             printf("        Class 1 (storage): %zu\n", m_Stats.GetBufferCreationCount(1));
2582             printf("        Class 2 (uniform): %zu\n", m_Stats.GetBufferCreationCount(2));
2583             printf("        Class 3 (other): %zu\n", m_Stats.GetBufferCreationCount(3));
2584         }
2585     }
2586 
2587     // Images
2588     const size_t imageCreationCount =
2589         m_Stats.GetImageCreationCount(0) +
2590         m_Stats.GetImageCreationCount(1) +
2591         m_Stats.GetImageCreationCount(2) +
2592         m_Stats.GetImageCreationCount(3) +
2593         m_Stats.GetLinearImageCreationCount();
2594     if(imageCreationCount > 0)
2595     {
2596         printf("    Total images created: %zu\n", imageCreationCount);
2597         if(g_Verbosity == VERBOSITY::MAXIMUM)
2598         {
2599             printf("        Class 0 (depth/stencil): %zu\n", m_Stats.GetImageCreationCount(0));
2600             printf("        Class 1 (attachment): %zu\n", m_Stats.GetImageCreationCount(1));
2601             printf("        Class 2 (sampled): %zu\n", m_Stats.GetImageCreationCount(2));
2602             printf("        Class 3 (other): %zu\n", m_Stats.GetImageCreationCount(3));
2603             if(m_Stats.GetLinearImageCreationCount() > 0)
2604             {
2605                 printf("        LINEAR tiling: %zu\n", m_Stats.GetLinearImageCreationCount());
2606             }
2607         }
2608     }
2609 
2610     if(m_Stats.GetPoolCreationCount() > 0)
2611     {
2612         printf("    Total custom pools created: %zu\n", m_Stats.GetPoolCreationCount());
2613     }
2614 
2615     float lastTime;
2616     if(!m_LastLineTimeStr.empty() && StrRangeToFloat(StrRange(m_LastLineTimeStr), lastTime))
2617     {
2618         std::string origTimeStr;
2619         SecondsToFriendlyStr(lastTime, origTimeStr);
2620         printf("    Original recording time: %s\n", origTimeStr.c_str());
2621     }
2622 
2623     // Thread statistics.
2624     const size_t threadCount = m_Threads.size();
2625     if(threadCount > 1)
2626     {
2627         uint32_t threadCallCountMax = 0;
2628         uint32_t threadCallCountSum = 0;
2629         for(const auto& it : m_Threads)
2630         {
2631             threadCallCountMax = std::max(threadCallCountMax, it.second.callCount);
2632             threadCallCountSum += it.second.callCount;
2633         }
2634         printf("    Threads making calls to VMA: %zu\n", threadCount);
2635         printf("        %.2f%% calls from most active thread.\n",
2636             (float)threadCallCountMax * 100.f / (float)threadCallCountSum);
2637     }
2638     else
2639     {
2640         printf("    VMA used from only one thread.\n");
2641     }
2642 
2643     // Function call count
2644     if(g_Verbosity == VERBOSITY::MAXIMUM)
2645     {
2646         printf("    Function call count:\n");
2647         const size_t* const functionCallCount = m_Stats.GetFunctionCallCount();
2648         for(size_t i = 0; i < (size_t)VMA_FUNCTION::Count; ++i)
2649         {
2650             if(functionCallCount[i] > 0)
2651             {
2652                 printf("        %s %zu\n", VMA_FUNCTION_NAMES[i], functionCallCount[i]);
2653             }
2654         }
2655     }
2656 
2657     // Detailed stats
2658     if(g_Verbosity == VERBOSITY::MAXIMUM)
2659     {
2660         m_Stats.PrintDetailedStats();
2661     }
2662 
2663     if(g_MemStatsEnabled)
2664     {
2665         m_Stats.PrintMemStats();
2666     }
2667 }
2668 
ValidateFunctionParameterCount(size_t lineNumber,const CsvSplit & csvSplit,size_t expectedParamCount,bool lastUnbound)2669 bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound)
2670 {
2671     bool ok;
2672     if(lastUnbound)
2673         ok = csvSplit.GetCount() >= FIRST_PARAM_INDEX + expectedParamCount - 1;
2674     else
2675         ok = csvSplit.GetCount() == FIRST_PARAM_INDEX + expectedParamCount;
2676 
2677     if(!ok)
2678     {
2679         if(IssueWarning())
2680         {
2681             printf("Line %zu: Incorrect number of function parameters.\n", lineNumber);
2682         }
2683     }
2684 
2685     return ok;
2686 }
2687 
PrepareUserData(size_t lineNumber,uint32_t allocCreateFlags,const StrRange & userDataColumn,const StrRange & wholeLine,void * & outUserData)2688 bool Player::PrepareUserData(size_t lineNumber, uint32_t allocCreateFlags, const StrRange& userDataColumn, const StrRange& wholeLine, void*& outUserData)
2689 {
2690     if(!g_UserDataEnabled)
2691     {
2692         outUserData = nullptr;
2693         return true;
2694     }
2695 
2696     // String
2697     if((allocCreateFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
2698     {
2699         const size_t len = wholeLine.end - userDataColumn.beg;
2700         m_UserDataTmpStr.resize(len + 1);
2701         memcpy(m_UserDataTmpStr.data(), userDataColumn.beg, len);
2702         m_UserDataTmpStr[len] = '\0';
2703         outUserData = m_UserDataTmpStr.data();
2704         return true;
2705     }
2706     // Pointer
2707     else
2708     {
2709         uint64_t pUserData = 0;
2710         if(StrRangeToPtr(userDataColumn, pUserData))
2711         {
2712             outUserData = (void*)(uintptr_t)pUserData;
2713             return true;
2714         }
2715     }
2716 
2717     if(IssueWarning())
2718     {
2719         printf("Line %zu: Invalid pUserData.\n", lineNumber);
2720     }
2721     outUserData = 0;
2722     return false;
2723 }
2724 
UpdateMemStats()2725 void Player::UpdateMemStats()
2726 {
2727     if(!g_MemStatsEnabled)
2728     {
2729         return;
2730     }
2731 
2732     VmaStats stats;
2733     vmaCalculateStats(m_Allocator, &stats);
2734     m_Stats.UpdateMemStats(stats);
2735 }
2736 
ExecuteCreatePool(size_t lineNumber,const CsvSplit & csvSplit)2737 void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit)
2738 {
2739     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreatePool);
2740 
2741     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 7, false))
2742     {
2743         VmaPoolCreateInfo poolCreateInfo = {};
2744         uint64_t origPtr = 0;
2745 
2746         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), poolCreateInfo.memoryTypeIndex) &&
2747             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), poolCreateInfo.flags) &&
2748             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), poolCreateInfo.blockSize) &&
2749             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), poolCreateInfo.minBlockCount) &&
2750             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), poolCreateInfo.maxBlockCount) &&
2751             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), poolCreateInfo.frameInUseCount) &&
2752             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), origPtr))
2753         {
2754             m_Stats.RegisterCreatePool(poolCreateInfo);
2755 
2756             Pool poolDesc = {};
2757             VkResult res = vmaCreatePool(m_Allocator, &poolCreateInfo, &poolDesc.pool);
2758 
2759             if(origPtr)
2760             {
2761                 if(res == VK_SUCCESS)
2762                 {
2763                     // Originally succeeded, currently succeeded.
2764                     // Just save pointer (done below).
2765                 }
2766                 else
2767                 {
2768                     // Originally succeeded, currently failed.
2769                     // Print warning. Save null pointer.
2770                     if(IssueWarning())
2771                     {
2772                         printf("Line %zu: vmaCreatePool failed (%d), while originally succeeded.\n", lineNumber, res);
2773                     }
2774                }
2775 
2776                 const auto existingIt = m_Pools.find(origPtr);
2777                 if(existingIt != m_Pools.end())
2778                 {
2779                     if(IssueWarning())
2780                     {
2781                         printf("Line %zu: Pool %llX already exists.\n", lineNumber, origPtr);
2782                     }
2783                 }
2784                 m_Pools[origPtr] = poolDesc;
2785             }
2786             else
2787             {
2788                 if(res == VK_SUCCESS)
2789                 {
2790                     // Originally failed, currently succeeded.
2791                     // Print warning, destroy the pool.
2792                     if(IssueWarning())
2793                     {
2794                         printf("Line %zu: vmaCreatePool succeeded, originally failed.\n", lineNumber);
2795                     }
2796 
2797                     vmaDestroyPool(m_Allocator, poolDesc.pool);
2798                 }
2799                 else
2800                 {
2801                     // Originally failed, currently failed.
2802                     // Print warning.
2803                     if(IssueWarning())
2804                     {
2805                         printf("Line %zu: vmaCreatePool failed (%d), originally also failed.\n", lineNumber, res);
2806                     }
2807                 }
2808             }
2809 
2810             UpdateMemStats();
2811         }
2812         else
2813         {
2814             if(IssueWarning())
2815             {
2816                 printf("Line %zu: Invalid parameters for vmaCreatePool.\n", lineNumber);
2817             }
2818         }
2819     }
2820 }
2821 
ExecuteDestroyPool(size_t lineNumber,const CsvSplit & csvSplit)2822 void Player::ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit)
2823 {
2824     m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyPool);
2825 
2826     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
2827     {
2828         uint64_t origPtr = 0;
2829 
2830         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
2831         {
2832             if(origPtr != 0)
2833             {
2834                 const auto it = m_Pools.find(origPtr);
2835                 if(it != m_Pools.end())
2836                 {
2837                     vmaDestroyPool(m_Allocator, it->second.pool);
2838                     UpdateMemStats();
2839                     m_Pools.erase(it);
2840                 }
2841                 else
2842                 {
2843                     if(IssueWarning())
2844                     {
2845                         printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr);
2846                     }
2847                 }
2848             }
2849         }
2850         else
2851         {
2852             if(IssueWarning())
2853             {
2854                 printf("Line %zu: Invalid parameters for vmaDestroyPool.\n", lineNumber);
2855             }
2856         }
2857     }
2858 }
2859 
ExecuteSetAllocationUserData(size_t lineNumber,const CsvSplit & csvSplit)2860 void Player::ExecuteSetAllocationUserData(size_t lineNumber, const CsvSplit& csvSplit)
2861 {
2862     m_Stats.RegisterFunctionCall(VMA_FUNCTION::SetAllocationUserData);
2863 
2864     if(!g_UserDataEnabled)
2865     {
2866         return;
2867     }
2868 
2869     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true))
2870     {
2871         uint64_t origPtr = 0;
2872         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
2873         {
2874             const auto it = m_Allocations.find(origPtr);
2875             if(it != m_Allocations.end())
2876             {
2877                 void* pUserData = nullptr;
2878                 if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 1)
2879                 {
2880                     PrepareUserData(
2881                         lineNumber,
2882                         it->second.allocationFlags,
2883                         csvSplit.GetRange(FIRST_PARAM_INDEX + 1),
2884                         csvSplit.GetLine(),
2885                         pUserData);
2886                 }
2887 
2888                 vmaSetAllocationUserData(m_Allocator, it->second.allocation, pUserData);
2889             }
2890             else
2891             {
2892                 if(IssueWarning())
2893                 {
2894                     printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
2895                 }
2896             }
2897         }
2898         else
2899         {
2900             if(IssueWarning())
2901             {
2902                 printf("Line %zu: Invalid parameters for vmaSetAllocationUserData.\n", lineNumber);
2903             }
2904         }
2905     }
2906 }
2907 
ExecuteCreateBuffer(size_t lineNumber,const CsvSplit & csvSplit)2908 void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit)
2909 {
2910     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreateBuffer);
2911 
2912     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 12, true))
2913     {
2914         VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2915         VmaAllocationCreateInfo allocCreateInfo = {};
2916         uint64_t origPool = 0;
2917         uint64_t origPtr = 0;
2918 
2919         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), bufCreateInfo.flags) &&
2920             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), bufCreateInfo.size) &&
2921             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), bufCreateInfo.usage) &&
2922             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), (uint32_t&)bufCreateInfo.sharingMode) &&
2923             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), allocCreateInfo.flags) &&
2924             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), (uint32_t&)allocCreateInfo.usage) &&
2925             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.requiredFlags) &&
2926             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.preferredFlags) &&
2927             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), allocCreateInfo.memoryTypeBits) &&
2928             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPool) &&
2929             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), origPtr))
2930         {
2931             FindPool(lineNumber, origPool, allocCreateInfo.pool);
2932 
2933             if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 11)
2934             {
2935                 PrepareUserData(
2936                     lineNumber,
2937                     allocCreateInfo.flags,
2938                     csvSplit.GetRange(FIRST_PARAM_INDEX + 11),
2939                     csvSplit.GetLine(),
2940                     allocCreateInfo.pUserData);
2941             }
2942 
2943             m_Stats.RegisterCreateBuffer(bufCreateInfo);
2944             m_Stats.RegisterCreateAllocation(allocCreateInfo);
2945 
2946             // Forcing VK_SHARING_MODE_EXCLUSIVE because we use only one queue anyway.
2947             bufCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
2948 
2949             Allocation allocDesc = { };
2950             allocDesc.allocationFlags = allocCreateInfo.flags;
2951             VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr);
2952             UpdateMemStats();
2953             AddAllocation(lineNumber, origPtr, res, "vmaCreateBuffer", std::move(allocDesc));
2954         }
2955         else
2956         {
2957             if(IssueWarning())
2958             {
2959                 printf("Line %zu: Invalid parameters for vmaCreateBuffer.\n", lineNumber);
2960             }
2961         }
2962     }
2963 }
2964 
DestroyAllocation(size_t lineNumber,const CsvSplit & csvSplit,const char * functionName)2965 void Player::DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit, const char* functionName)
2966 {
2967     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
2968     {
2969         uint64_t origAllocPtr = 0;
2970 
2971         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtr))
2972         {
2973             if(origAllocPtr != 0)
2974             {
2975                 const auto it = m_Allocations.find(origAllocPtr);
2976                 if(it != m_Allocations.end())
2977                 {
2978                     Destroy(it->second);
2979                     UpdateMemStats();
2980                     m_Allocations.erase(it);
2981                 }
2982                 else
2983                 {
2984                     if(IssueWarning())
2985                     {
2986                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr);
2987                     }
2988                 }
2989             }
2990         }
2991         else
2992         {
2993             if(IssueWarning())
2994             {
2995                 printf("Line %zu: Invalid parameters for %s.\n", lineNumber, functionName);
2996             }
2997         }
2998     }
2999 }
3000 
PrintStats(const VmaStats & stats,const char * suffix)3001 void Player::PrintStats(const VmaStats& stats, const char* suffix)
3002 {
3003     printf("    VmaStats %s:\n", suffix);
3004     printf("        total:\n");
3005     PrintStatInfo(stats.total);
3006 
3007     if(g_Verbosity == VERBOSITY::MAXIMUM)
3008     {
3009         for(uint32_t i = 0; i < m_MemProps->memoryHeapCount; ++i)
3010         {
3011             printf("        memoryHeap[%u]:\n", i);
3012             PrintStatInfo(stats.memoryHeap[i]);
3013         }
3014         for(uint32_t i = 0; i < m_MemProps->memoryTypeCount; ++i)
3015         {
3016             printf("        memoryType[%u]:\n", i);
3017             PrintStatInfo(stats.memoryType[i]);
3018         }
3019     }
3020 }
3021 
PrintStatInfo(const VmaStatInfo & info)3022 void Player::PrintStatInfo(const VmaStatInfo& info)
3023 {
3024     printf("            blockCount: %u\n", info.blockCount);
3025     printf("            allocationCount: %u\n", info.allocationCount);
3026     printf("            unusedRangeCount: %u\n", info.unusedRangeCount);
3027     printf("            usedBytes: %llu\n", info.usedBytes);
3028     printf("            unusedBytes: %llu\n", info.unusedBytes);
3029     printf("            allocationSizeMin: %llu\n", info.allocationSizeMin);
3030     printf("            allocationSizeAvg: %llu\n", info.allocationSizeAvg);
3031     printf("            allocationSizeMax: %llu\n", info.allocationSizeMax);
3032     printf("            unusedRangeSizeMin: %llu\n", info.unusedRangeSizeMin);
3033     printf("            unusedRangeSizeAvg: %llu\n", info.unusedRangeSizeAvg);
3034     printf("            unusedRangeSizeMax: %llu\n", info.unusedRangeSizeMax);
3035 }
3036 
ExecuteCreateImage(size_t lineNumber,const CsvSplit & csvSplit)3037 void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit)
3038 {
3039     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreateImage);
3040 
3041     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 21, true))
3042     {
3043         VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
3044         VmaAllocationCreateInfo allocCreateInfo = {};
3045         uint64_t origPool = 0;
3046         uint64_t origPtr = 0;
3047 
3048         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), imageCreateInfo.flags) &&
3049             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), (uint32_t&)imageCreateInfo.imageType) &&
3050             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), (uint32_t&)imageCreateInfo.format) &&
3051             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), imageCreateInfo.extent.width) &&
3052             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), imageCreateInfo.extent.height) &&
3053             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), imageCreateInfo.extent.depth) &&
3054             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), imageCreateInfo.mipLevels) &&
3055             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), imageCreateInfo.arrayLayers) &&
3056             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), (uint32_t&)imageCreateInfo.samples) &&
3057             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), (uint32_t&)imageCreateInfo.tiling) &&
3058             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), imageCreateInfo.usage) &&
3059             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 11), (uint32_t&)imageCreateInfo.sharingMode) &&
3060             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 12), (uint32_t&)imageCreateInfo.initialLayout) &&
3061             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 13), allocCreateInfo.flags) &&
3062             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 14), (uint32_t&)allocCreateInfo.usage) &&
3063             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 15), allocCreateInfo.requiredFlags) &&
3064             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 16), allocCreateInfo.preferredFlags) &&
3065             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 17), allocCreateInfo.memoryTypeBits) &&
3066             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 18), origPool) &&
3067             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 19), origPtr))
3068         {
3069             FindPool(lineNumber, origPool, allocCreateInfo.pool);
3070 
3071             if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 20)
3072             {
3073                 PrepareUserData(
3074                     lineNumber,
3075                     allocCreateInfo.flags,
3076                     csvSplit.GetRange(FIRST_PARAM_INDEX + 20),
3077                     csvSplit.GetLine(),
3078                     allocCreateInfo.pUserData);
3079             }
3080 
3081             m_Stats.RegisterCreateImage(imageCreateInfo);
3082             m_Stats.RegisterCreateAllocation(allocCreateInfo);
3083 
3084             // Forcing VK_SHARING_MODE_EXCLUSIVE because we use only one queue anyway.
3085             imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
3086 
3087             Allocation allocDesc = {};
3088             allocDesc.allocationFlags = allocCreateInfo.flags;
3089             VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr);
3090             UpdateMemStats();
3091             AddAllocation(lineNumber, origPtr, res, "vmaCreateImage", std::move(allocDesc));
3092         }
3093         else
3094         {
3095             if(IssueWarning())
3096             {
3097                 printf("Line %zu: Invalid parameters for vmaCreateImage.\n", lineNumber);
3098             }
3099         }
3100     }
3101 }
3102 
ExecuteFreeMemoryPages(size_t lineNumber,const CsvSplit & csvSplit)3103 void Player::ExecuteFreeMemoryPages(size_t lineNumber, const CsvSplit& csvSplit)
3104 {
3105     m_Stats.RegisterFunctionCall(VMA_FUNCTION::FreeMemoryPages);
3106 
3107     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3108     {
3109         std::vector<uint64_t> origAllocPtrs;
3110         if(StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtrs))
3111         {
3112             const size_t allocCount = origAllocPtrs.size();
3113             size_t notNullCount = 0;
3114             for(size_t i = 0; i < allocCount; ++i)
3115             {
3116                 const uint64_t origAllocPtr = origAllocPtrs[i];
3117                 if(origAllocPtr != 0)
3118                 {
3119                     const auto it = m_Allocations.find(origAllocPtr);
3120                     if(it != m_Allocations.end())
3121                     {
3122                         Destroy(it->second);
3123                         m_Allocations.erase(it);
3124                         ++notNullCount;
3125                     }
3126                     else
3127                     {
3128                         if(IssueWarning())
3129                         {
3130                             printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr);
3131                         }
3132                     }
3133                 }
3134             }
3135             if(notNullCount)
3136             {
3137                 UpdateMemStats();
3138             }
3139         }
3140         else
3141         {
3142             if(IssueWarning())
3143             {
3144                 printf("Line %zu: Invalid parameters for vmaFreeMemoryPages.\n", lineNumber);
3145             }
3146         }
3147     }
3148 }
3149 
ExecuteCreateLostAllocation(size_t lineNumber,const CsvSplit & csvSplit)3150 void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3151 {
3152     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreateLostAllocation);
3153 
3154     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3155     {
3156         uint64_t origPtr = 0;
3157 
3158         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3159         {
3160             Allocation allocDesc = {};
3161             vmaCreateLostAllocation(m_Allocator, &allocDesc.allocation);
3162             UpdateMemStats();
3163             m_Stats.RegisterCreateLostAllocation();
3164 
3165             AddAllocation(lineNumber, origPtr, VK_SUCCESS, "vmaCreateLostAllocation", std::move(allocDesc));
3166         }
3167         else
3168         {
3169             if(IssueWarning())
3170             {
3171                 printf("Line %zu: Invalid parameters for vmaCreateLostAllocation.\n", lineNumber);
3172             }
3173         }
3174     }
3175 }
3176 
ExecuteAllocateMemory(size_t lineNumber,const CsvSplit & csvSplit)3177 void Player::ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit)
3178 {
3179     m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemory);
3180 
3181     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 11, true))
3182     {
3183         VkMemoryRequirements memReq = {};
3184         VmaAllocationCreateInfo allocCreateInfo = {};
3185         uint64_t origPool = 0;
3186         uint64_t origPtr = 0;
3187 
3188         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) &&
3189             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) &&
3190             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) &&
3191             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) &&
3192             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), (uint32_t&)allocCreateInfo.usage) &&
3193             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), allocCreateInfo.requiredFlags) &&
3194             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.preferredFlags) &&
3195             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.memoryTypeBits) &&
3196             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), origPool) &&
3197             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPtr))
3198         {
3199             FindPool(lineNumber, origPool, allocCreateInfo.pool);
3200 
3201             if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 10)
3202             {
3203                 PrepareUserData(
3204                     lineNumber,
3205                     allocCreateInfo.flags,
3206                     csvSplit.GetRange(FIRST_PARAM_INDEX + 10),
3207                     csvSplit.GetLine(),
3208                     allocCreateInfo.pUserData);
3209             }
3210 
3211             UpdateMemStats();
3212             m_Stats.RegisterCreateAllocation(allocCreateInfo);
3213 
3214             Allocation allocDesc = {};
3215             allocDesc.allocationFlags = allocCreateInfo.flags;
3216             VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr);
3217             AddAllocation(lineNumber, origPtr, res, "vmaAllocateMemory", std::move(allocDesc));
3218         }
3219         else
3220         {
3221             if(IssueWarning())
3222             {
3223                 printf("Line %zu: Invalid parameters for vmaAllocateMemory.\n", lineNumber);
3224             }
3225         }
3226     }
3227 }
3228 
ExecuteAllocateMemoryPages(size_t lineNumber,const CsvSplit & csvSplit)3229 void Player::ExecuteAllocateMemoryPages(size_t lineNumber, const CsvSplit& csvSplit)
3230 {
3231     m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemoryPages);
3232 
3233     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 11, true))
3234     {
3235         VkMemoryRequirements memReq = {};
3236         VmaAllocationCreateInfo allocCreateInfo = {};
3237         uint64_t origPool = 0;
3238         std::vector<uint64_t> origPtrs;
3239 
3240         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) &&
3241             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) &&
3242             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) &&
3243             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) &&
3244             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), (uint32_t&)allocCreateInfo.usage) &&
3245             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), allocCreateInfo.requiredFlags) &&
3246             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.preferredFlags) &&
3247             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.memoryTypeBits) &&
3248             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), origPool) &&
3249             StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPtrs))
3250         {
3251             const size_t allocCount = origPtrs.size();
3252             if(allocCount > 0)
3253             {
3254                 FindPool(lineNumber, origPool, allocCreateInfo.pool);
3255 
3256                 if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 10)
3257                 {
3258                     PrepareUserData(
3259                         lineNumber,
3260                         allocCreateInfo.flags,
3261                         csvSplit.GetRange(FIRST_PARAM_INDEX + 10),
3262                         csvSplit.GetLine(),
3263                         allocCreateInfo.pUserData);
3264                 }
3265 
3266                 UpdateMemStats();
3267                 m_Stats.RegisterCreateAllocation(allocCreateInfo, allocCount);
3268                 m_Stats.RegisterAllocateMemoryPages(allocCount);
3269 
3270                 std::vector<VmaAllocation> allocations(allocCount);
3271 
3272                 VkResult res = vmaAllocateMemoryPages(m_Allocator, &memReq, &allocCreateInfo, allocCount, allocations.data(), nullptr);
3273                 for(size_t i = 0; i < allocCount; ++i)
3274                 {
3275                     Allocation allocDesc = {};
3276                     allocDesc.allocationFlags = allocCreateInfo.flags;
3277                     allocDesc.allocation = allocations[i];
3278                     AddAllocation(lineNumber, origPtrs[i], res, "vmaAllocateMemoryPages", std::move(allocDesc));
3279                 }
3280             }
3281         }
3282         else
3283         {
3284             if(IssueWarning())
3285             {
3286                 printf("Line %zu: Invalid parameters for vmaAllocateMemoryPages.\n", lineNumber);
3287             }
3288         }
3289     }
3290 }
3291 
ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber,const CsvSplit & csvSplit,OBJECT_TYPE objType)3292 void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType)
3293 {
3294     switch(objType)
3295     {
3296     case OBJECT_TYPE::BUFFER:
3297         m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemoryForBuffer);
3298         break;
3299     case OBJECT_TYPE::IMAGE:
3300         m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemoryForImage);
3301         break;
3302     default: assert(0);
3303     }
3304 
3305     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 13, true))
3306     {
3307         VkMemoryRequirements memReq = {};
3308         VmaAllocationCreateInfo allocCreateInfo = {};
3309         bool requiresDedicatedAllocation = false;
3310         bool prefersDedicatedAllocation = false;
3311         uint64_t origPool = 0;
3312         uint64_t origPtr = 0;
3313 
3314         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) &&
3315             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) &&
3316             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) &&
3317             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) &&
3318             StrRangeToBool(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), requiresDedicatedAllocation) &&
3319             StrRangeToBool(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), prefersDedicatedAllocation) &&
3320             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), (uint32_t&)allocCreateInfo.usage) &&
3321             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.requiredFlags) &&
3322             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), allocCreateInfo.preferredFlags) &&
3323             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), allocCreateInfo.memoryTypeBits) &&
3324             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), origPool) &&
3325             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 11), origPtr))
3326         {
3327             FindPool(lineNumber, origPool, allocCreateInfo.pool);
3328 
3329             if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 12)
3330             {
3331                 PrepareUserData(
3332                     lineNumber,
3333                     allocCreateInfo.flags,
3334                     csvSplit.GetRange(FIRST_PARAM_INDEX + 12),
3335                     csvSplit.GetLine(),
3336                     allocCreateInfo.pUserData);
3337             }
3338 
3339             UpdateMemStats();
3340             m_Stats.RegisterCreateAllocation(allocCreateInfo);
3341 
3342             if(requiresDedicatedAllocation || prefersDedicatedAllocation)
3343             {
3344                 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3345             }
3346 
3347             if(!m_AllocateForBufferImageWarningIssued)
3348             {
3349                 if(IssueWarning())
3350                 {
3351                     printf("Line %zu: vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage cannot be replayed accurately. Using vmaCreateAllocation instead.\n", lineNumber);
3352                 }
3353                 m_AllocateForBufferImageWarningIssued = true;
3354             }
3355 
3356             Allocation allocDesc = {};
3357             allocDesc.allocationFlags = allocCreateInfo.flags;
3358             VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr);
3359             AddAllocation(lineNumber, origPtr, res, "vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage)", std::move(allocDesc));
3360         }
3361         else
3362         {
3363             if(IssueWarning())
3364             {
3365                 printf("Line %zu: Invalid parameters for vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage.\n", lineNumber);
3366             }
3367         }
3368     }
3369 }
3370 
ExecuteMapMemory(size_t lineNumber,const CsvSplit & csvSplit)3371 void Player::ExecuteMapMemory(size_t lineNumber, const CsvSplit& csvSplit)
3372 {
3373     m_Stats.RegisterFunctionCall(VMA_FUNCTION::MapMemory);
3374 
3375     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3376     {
3377         uint64_t origPtr = 0;
3378 
3379         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3380         {
3381             if(origPtr != 0)
3382             {
3383                 const auto it = m_Allocations.find(origPtr);
3384                 if(it != m_Allocations.end())
3385                 {
3386                     if(it->second.allocation)
3387                     {
3388                         void* pData;
3389                         VkResult res = vmaMapMemory(m_Allocator, it->second.allocation, &pData);
3390                         if(res != VK_SUCCESS)
3391                         {
3392                             printf("Line %zu: vmaMapMemory failed (%d)\n", lineNumber, res);
3393                         }
3394                     }
3395                     else
3396                     {
3397                         if(IssueWarning())
3398                         {
3399                             printf("Line %zu: Cannot call vmaMapMemory - allocation is null.\n", lineNumber);
3400                         }
3401                     }
3402                 }
3403                 else
3404                 {
3405                     if(IssueWarning())
3406                     {
3407                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3408                     }
3409                 }
3410             }
3411         }
3412         else
3413         {
3414             if(IssueWarning())
3415             {
3416                 printf("Line %zu: Invalid parameters for vmaMapMemory.\n", lineNumber);
3417             }
3418         }
3419     }
3420 }
3421 
ExecuteUnmapMemory(size_t lineNumber,const CsvSplit & csvSplit)3422 void Player::ExecuteUnmapMemory(size_t lineNumber, const CsvSplit& csvSplit)
3423 {
3424     m_Stats.RegisterFunctionCall(VMA_FUNCTION::UnmapMemory);
3425 
3426     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3427     {
3428         uint64_t origPtr = 0;
3429 
3430         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3431         {
3432             if(origPtr != 0)
3433             {
3434                 const auto it = m_Allocations.find(origPtr);
3435                 if(it != m_Allocations.end())
3436                 {
3437                     if(it->second.allocation)
3438                     {
3439                         vmaUnmapMemory(m_Allocator, it->second.allocation);
3440                     }
3441                     else
3442                     {
3443                         if(IssueWarning())
3444                         {
3445                             printf("Line %zu: Cannot call vmaUnmapMemory - allocation is null.\n", lineNumber);
3446                         }
3447                     }
3448                 }
3449                 else
3450                 {
3451                     if(IssueWarning())
3452                     {
3453                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3454                     }
3455                 }
3456             }
3457         }
3458         else
3459         {
3460             if(IssueWarning())
3461             {
3462                 printf("Line %zu: Invalid parameters for vmaMapMemory.\n", lineNumber);
3463             }
3464         }
3465     }
3466 }
3467 
ExecuteFlushAllocation(size_t lineNumber,const CsvSplit & csvSplit)3468 void Player::ExecuteFlushAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3469 {
3470     m_Stats.RegisterFunctionCall(VMA_FUNCTION::FlushAllocation);
3471 
3472     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 3, false))
3473     {
3474         uint64_t origPtr = 0;
3475         uint64_t offset = 0;
3476         uint64_t size = 0;
3477 
3478         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) &&
3479             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), offset) &&
3480             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), size))
3481         {
3482             if(origPtr != 0)
3483             {
3484                 const auto it = m_Allocations.find(origPtr);
3485                 if(it != m_Allocations.end())
3486                 {
3487                     if(it->second.allocation)
3488                     {
3489                         vmaFlushAllocation(m_Allocator, it->second.allocation, offset, size);
3490                     }
3491                     else
3492                     {
3493                         if(IssueWarning())
3494                         {
3495                             printf("Line %zu: Cannot call vmaFlushAllocation - allocation is null.\n", lineNumber);
3496                         }
3497                     }
3498                 }
3499                 else
3500                 {
3501                     if(IssueWarning())
3502                     {
3503                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3504                     }
3505                 }
3506             }
3507         }
3508         else
3509         {
3510             if(IssueWarning())
3511             {
3512                 printf("Line %zu: Invalid parameters for vmaFlushAllocation.\n", lineNumber);
3513             }
3514         }
3515     }
3516 }
3517 
ExecuteInvalidateAllocation(size_t lineNumber,const CsvSplit & csvSplit)3518 void Player::ExecuteInvalidateAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3519 {
3520     m_Stats.RegisterFunctionCall(VMA_FUNCTION::InvalidateAllocation);
3521 
3522     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 3, false))
3523     {
3524         uint64_t origPtr = 0;
3525         uint64_t offset = 0;
3526         uint64_t size = 0;
3527 
3528         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) &&
3529             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), offset) &&
3530             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), size))
3531         {
3532             if(origPtr != 0)
3533             {
3534                 const auto it = m_Allocations.find(origPtr);
3535                 if(it != m_Allocations.end())
3536                 {
3537                     if(it->second.allocation)
3538                     {
3539                         vmaInvalidateAllocation(m_Allocator, it->second.allocation, offset, size);
3540                     }
3541                     else
3542                     {
3543                         if(IssueWarning())
3544                         {
3545                             printf("Line %zu: Cannot call vmaInvalidateAllocation - allocation is null.\n", lineNumber);
3546                         }
3547                     }
3548                 }
3549                 else
3550                 {
3551                     if(IssueWarning())
3552                     {
3553                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3554                     }
3555                 }
3556             }
3557         }
3558         else
3559         {
3560             if(IssueWarning())
3561             {
3562                 printf("Line %zu: Invalid parameters for vmaInvalidateAllocation.\n", lineNumber);
3563             }
3564         }
3565     }
3566 }
3567 
ExecuteTouchAllocation(size_t lineNumber,const CsvSplit & csvSplit)3568 void Player::ExecuteTouchAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3569 {
3570     m_Stats.RegisterFunctionCall(VMA_FUNCTION::TouchAllocation);
3571 
3572     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3573     {
3574         uint64_t origPtr = 0;
3575         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3576         {
3577             const auto it = m_Allocations.find(origPtr);
3578             if(it != m_Allocations.end())
3579             {
3580                 if(it->second.allocation)
3581                 {
3582                     vmaTouchAllocation(m_Allocator, it->second.allocation);
3583                 }
3584                 else
3585                 {
3586                     if(IssueWarning())
3587                     {
3588                         printf("Line %zu: Cannot call vmaTouchAllocation - allocation is null.\n", lineNumber);
3589                     }
3590                 }
3591             }
3592             else
3593             {
3594                 if(IssueWarning())
3595                 {
3596                     printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3597                 }
3598             }
3599         }
3600         else
3601         {
3602             if(IssueWarning())
3603             {
3604                 printf("Line %zu: Invalid parameters for vmaTouchAllocation.\n", lineNumber);
3605             }
3606         }
3607     }
3608 }
3609 
ExecuteGetAllocationInfo(size_t lineNumber,const CsvSplit & csvSplit)3610 void Player::ExecuteGetAllocationInfo(size_t lineNumber, const CsvSplit& csvSplit)
3611 {
3612     m_Stats.RegisterFunctionCall(VMA_FUNCTION::GetAllocationInfo);
3613 
3614     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3615     {
3616         uint64_t origPtr = 0;
3617         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3618         {
3619             const auto it = m_Allocations.find(origPtr);
3620             if(it != m_Allocations.end())
3621             {
3622                 if(it->second.allocation)
3623                 {
3624                     VmaAllocationInfo allocInfo;
3625                     vmaGetAllocationInfo(m_Allocator, it->second.allocation, &allocInfo);
3626                 }
3627                 else
3628                 {
3629                     if(IssueWarning())
3630                     {
3631                         printf("Line %zu: Cannot call vmaGetAllocationInfo - allocation is null.\n", lineNumber);
3632                     }
3633                 }
3634             }
3635             else
3636             {
3637                 if(IssueWarning())
3638                 {
3639                     printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3640                 }
3641             }
3642         }
3643         else
3644         {
3645             if(IssueWarning())
3646             {
3647                 printf("Line %zu: Invalid parameters for vmaGetAllocationInfo.\n", lineNumber);
3648             }
3649         }
3650     }
3651 }
3652 
ExecuteMakePoolAllocationsLost(size_t lineNumber,const CsvSplit & csvSplit)3653 void Player::ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit)
3654 {
3655     m_Stats.RegisterFunctionCall(VMA_FUNCTION::MakePoolAllocationsLost);
3656 
3657     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3658     {
3659         uint64_t origPtr = 0;
3660 
3661         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3662         {
3663             if(origPtr != 0)
3664             {
3665                 const auto it = m_Pools.find(origPtr);
3666                 if(it != m_Pools.end())
3667                 {
3668                     vmaMakePoolAllocationsLost(m_Allocator, it->second.pool, nullptr);
3669                     UpdateMemStats();
3670                 }
3671                 else
3672                 {
3673                     if(IssueWarning())
3674                     {
3675                         printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr);
3676                     }
3677                 }
3678             }
3679         }
3680         else
3681         {
3682             if(IssueWarning())
3683             {
3684                 printf("Line %zu: Invalid parameters for vmaMakePoolAllocationsLost.\n", lineNumber);
3685             }
3686         }
3687     }
3688 }
3689 
ExecuteResizeAllocation(size_t lineNumber,const CsvSplit & csvSplit)3690 void Player::ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3691 {
3692     m_Stats.RegisterFunctionCall(VMA_FUNCTION::ResizeAllocation);
3693 
3694     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, false))
3695     {
3696         uint64_t origPtr = 0;
3697         uint64_t newSize = 0;
3698 
3699         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) &&
3700             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), newSize))
3701         {
3702             if(origPtr != 0)
3703             {
3704                 const auto it = m_Allocations.find(origPtr);
3705                 if(it != m_Allocations.end())
3706                 {
3707                     vmaResizeAllocation(m_Allocator, it->second.allocation, newSize);
3708                     UpdateMemStats();
3709                 }
3710                 else
3711                 {
3712                     if(IssueWarning())
3713                     {
3714                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3715                     }
3716                 }
3717             }
3718         }
3719         else
3720         {
3721             if(IssueWarning())
3722             {
3723                 printf("Line %zu: Invalid parameters for vmaResizeAllocation.\n", lineNumber);
3724             }
3725         }
3726     }
3727 }
3728 
ExecuteDefragmentationBegin(size_t lineNumber,const CsvSplit & csvSplit)3729 void Player::ExecuteDefragmentationBegin(size_t lineNumber, const CsvSplit& csvSplit)
3730 {
3731     m_Stats.RegisterFunctionCall(VMA_FUNCTION::DefragmentationBegin);
3732 
3733     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 9, false))
3734     {
3735         VmaDefragmentationInfo2 defragInfo = {};
3736         std::vector<uint64_t> allocationOrigPtrs;
3737         std::vector<uint64_t> poolOrigPtrs;
3738         uint64_t cmdBufOrigPtr = 0;
3739         uint64_t defragCtxOrigPtr = 0;
3740 
3741         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), defragInfo.flags) &&
3742             StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), allocationOrigPtrs) &&
3743             StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), poolOrigPtrs) &&
3744             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), defragInfo.maxCpuBytesToMove) &&
3745             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), defragInfo.maxCpuAllocationsToMove) &&
3746             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), defragInfo.maxGpuBytesToMove) &&
3747             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), defragInfo.maxGpuAllocationsToMove) &&
3748             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), cmdBufOrigPtr) &&
3749             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), defragCtxOrigPtr))
3750         {
3751             const size_t allocationOrigPtrCount = allocationOrigPtrs.size();
3752             std::vector<VmaAllocation> allocations;
3753             allocations.reserve(allocationOrigPtrCount);
3754             for(size_t i = 0; i < allocationOrigPtrCount; ++i)
3755             {
3756                 const auto it = m_Allocations.find(allocationOrigPtrs[i]);
3757                 if(it != m_Allocations.end() && it->second.allocation)
3758                 {
3759                     allocations.push_back(it->second.allocation);
3760                 }
3761             }
3762             if(!allocations.empty())
3763             {
3764                 defragInfo.allocationCount = (uint32_t)allocations.size();
3765                 defragInfo.pAllocations = allocations.data();
3766             }
3767 
3768             const size_t poolOrigPtrCount = poolOrigPtrs.size();
3769             std::vector<VmaPool> pools;
3770             pools.reserve(poolOrigPtrCount);
3771             for(size_t i = 0; i < poolOrigPtrCount; ++i)
3772             {
3773                 const auto it = m_Pools.find(poolOrigPtrs[i]);
3774                 if(it != m_Pools.end() && it->second.pool)
3775                 {
3776                     pools.push_back(it->second.pool);
3777                 }
3778             }
3779             if(!pools.empty())
3780             {
3781                 defragInfo.poolCount = (uint32_t)pools.size();
3782                 defragInfo.pPools = pools.data();
3783             }
3784 
3785             if(allocations.size() != allocationOrigPtrCount ||
3786                 pools.size() != poolOrigPtrCount)
3787             {
3788                 if(IssueWarning())
3789                 {
3790                     printf("Line %zu: Passing %zu allocations and %zu pools to vmaDefragmentationBegin, while originally %zu allocations and %zu pools were passed.\n",
3791                         lineNumber,
3792                         allocations.size(), pools.size(),
3793                         allocationOrigPtrCount, poolOrigPtrCount);
3794                 }
3795             }
3796 
3797             if(cmdBufOrigPtr)
3798             {
3799                 VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
3800                 cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
3801                 VkResult res = vkBeginCommandBuffer(m_CommandBuffer, &cmdBufBeginInfo);
3802                 if(res == VK_SUCCESS)
3803                 {
3804                     defragInfo.commandBuffer = m_CommandBuffer;
3805                 }
3806                 else
3807                 {
3808                     printf("Line %zu: vkBeginCommandBuffer failed (%d)\n", lineNumber, res);
3809                 }
3810             }
3811 
3812             m_Stats.RegisterDefragmentation(defragInfo);
3813 
3814             VmaDefragmentationContext defragCtx = nullptr;
3815             VkResult res = vmaDefragmentationBegin(m_Allocator, &defragInfo, nullptr, &defragCtx);
3816 
3817             if(defragInfo.commandBuffer)
3818             {
3819                 vkEndCommandBuffer(m_CommandBuffer);
3820 
3821                 VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
3822                 submitInfo.commandBufferCount = 1;
3823                 submitInfo.pCommandBuffers = &m_CommandBuffer;
3824                 vkQueueSubmit(m_TransferQueue, 1, &submitInfo, VK_NULL_HANDLE);
3825                 vkQueueWaitIdle(m_TransferQueue);
3826             }
3827 
3828             if(res >= VK_SUCCESS)
3829             {
3830                 if(defragCtx)
3831                 {
3832                     if(defragCtxOrigPtr)
3833                     {
3834                         // We have defragmentation context, originally had defragmentation context: Store it.
3835                         m_DefragmentationContexts[defragCtxOrigPtr] = defragCtx;
3836                     }
3837                     else
3838                     {
3839                         // We have defragmentation context, originally it was null: End immediately.
3840                         vmaDefragmentationEnd(m_Allocator, defragCtx);
3841                     }
3842                 }
3843                 else
3844                 {
3845                     if(defragCtxOrigPtr)
3846                     {
3847                         // We have no defragmentation context, originally there was one: Store null.
3848                         m_DefragmentationContexts[defragCtxOrigPtr] = nullptr;
3849                     }
3850                     else
3851                     {
3852                         // We have no defragmentation context, originally there wasn't as well - nothing to do.
3853                     }
3854                 }
3855             }
3856             else
3857             {
3858                 if(defragCtxOrigPtr)
3859                 {
3860                     // Currently failed, originally succeeded.
3861                     if(IssueWarning())
3862                     {
3863                         printf("Line %zu: vmaDefragmentationBegin failed (%d), while originally succeeded.\n", lineNumber, res);
3864                     }
3865                 }
3866                 else
3867                 {
3868                     // Currently failed, originally don't know.
3869                     if(IssueWarning())
3870                     {
3871                         printf("Line %zu: vmaDefragmentationBegin failed (%d).\n", lineNumber, res);
3872                     }
3873                 }
3874             }
3875         }
3876         else
3877         {
3878             if(IssueWarning())
3879             {
3880                 printf("Line %zu: Invalid parameters for vmaDefragmentationBegin.\n", lineNumber);
3881             }
3882         }
3883     }
3884 }
3885 
ExecuteDefragmentationEnd(size_t lineNumber,const CsvSplit & csvSplit)3886 void Player::ExecuteDefragmentationEnd(size_t lineNumber, const CsvSplit& csvSplit)
3887 {
3888     m_Stats.RegisterFunctionCall(VMA_FUNCTION::DefragmentationEnd);
3889 
3890     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3891     {
3892         uint64_t origPtr = 0;
3893 
3894         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3895         {
3896             if(origPtr != 0)
3897             {
3898                 const auto it = m_DefragmentationContexts.find(origPtr);
3899                 if(it != m_DefragmentationContexts.end())
3900                 {
3901                     vmaDefragmentationEnd(m_Allocator, it->second);
3902                     m_DefragmentationContexts.erase(it);
3903                 }
3904                 else
3905                 {
3906                     if(IssueWarning())
3907                     {
3908                         printf("Line %zu: Defragmentation context %llX not found.\n", lineNumber, origPtr);
3909                     }
3910                 }
3911             }
3912         }
3913         else
3914         {
3915             if(IssueWarning())
3916             {
3917                 printf("Line %zu: Invalid parameters for vmaDefragmentationEnd.\n", lineNumber);
3918             }
3919         }
3920     }
3921 }
3922 
ExecuteSetPoolName(size_t lineNumber,const CsvSplit & csvSplit)3923 void Player::ExecuteSetPoolName(size_t lineNumber, const CsvSplit& csvSplit)
3924 {
3925     m_Stats.RegisterFunctionCall(VMA_FUNCTION::SetPoolName);
3926 
3927     if(!g_UserDataEnabled)
3928     {
3929         return;
3930     }
3931 
3932     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true))
3933     {
3934         uint64_t origPtr = 0;
3935         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3936         {
3937             if(origPtr != 0)
3938             {
3939                 const auto it = m_Pools.find(origPtr);
3940                 if(it != m_Pools.end())
3941                 {
3942                     std::string poolName;
3943                     csvSplit.GetRange(FIRST_PARAM_INDEX + 1).to_str(poolName);
3944                     vmaSetPoolName(m_Allocator, it->second.pool, !poolName.empty() ? poolName.c_str() : nullptr);
3945                 }
3946                 else
3947                 {
3948                     if(IssueWarning())
3949                     {
3950                         printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr);
3951                     }
3952                 }
3953             }
3954         }
3955         else
3956         {
3957             if(IssueWarning())
3958             {
3959                 printf("Line %zu: Invalid parameters for vmaSetPoolName.\n", lineNumber);
3960             }
3961         }
3962     }
3963 }
3964 
3965 ////////////////////////////////////////////////////////////////////////////////
3966 // Main functions
3967 
PrintCommandLineSyntax()3968 static void PrintCommandLineSyntax()
3969 {
3970     printf(
3971         "Command line syntax:\n"
3972         "    VmaReplay [Options] <SrcFile.csv>\n"
3973         "Available options:\n"
3974         "    -v <Number> - Verbosity level:\n"
3975         "        0 - Minimum verbosity. Prints only warnings and errors.\n"
3976         "        1 - Default verbosity. Prints important messages and statistics.\n"
3977         "        2 - Maximum verbosity. Prints a lot of information.\n"
3978         "    -i <Number> - Repeat playback given number of times (iterations)\n"
3979         "        Default is 1. Vulkan is reinitialized with every iteration.\n"
3980         "    --MemStats <Value> - 0 to disable or 1 to enable memory statistics.\n"
3981         "        Default is 0. Enabling it may negatively impact playback performance.\n"
3982         "    --DumpStatsAfterLine <Line> - Dump VMA statistics to JSON file after specified source file line finishes execution.\n"
3983         "        File is written to current directory with name: VmaReplay_Line####.json.\n"
3984         "        This parameter can be repeated.\n"
3985         "    --DumpDetailedStatsAfterLine <Line> - Like command above, but includes detailed map.\n"
3986         "    --DefragmentAfterLine <Line> - Defragment memory after specified source file line and print statistics.\n"
3987         "        It also prints detailed statistics to files VmaReplay_Line####_Defragment*.json\n"
3988         "    --DefragmentationFlags <Flags> - Flags to be applied when using DefragmentAfterLine.\n"
3989         "    --Lines <Ranges> - Replay only limited set of lines from file\n"
3990         "        Ranges is comma-separated list of ranges, e.g. \"-10,15,18-25,31-\".\n"
3991         "    --PhysicalDevice <Index> - Choice of Vulkan physical device. Default: 0.\n"
3992         "    --UserData <Value> - 0 to disable or 1 to enable setting pUserData during playback.\n"
3993         "        Default is 1. Affects both creation of buffers and images, as well as calls to vmaSetAllocationUserData.\n"
3994         "    --VK_LAYER_LUNARG_standard_validation <Value> - 0 to disable or 1 to enable validation layers.\n"
3995         "        By default the layers are silently enabled if available.\n"
3996         "    --VK_EXT_memory_budget <Value> - 0 to disable or 1 to enable this extension.\n"
3997         "        By default the extension is silently enabled if available.\n"
3998     );
3999 }
4000 
ProcessFile(size_t iterationIndex,const char * data,size_t numBytes,duration & outDuration)4001 static int ProcessFile(size_t iterationIndex, const char* data, size_t numBytes, duration& outDuration)
4002 {
4003     outDuration = duration::max();
4004 
4005     const bool useLineRanges = !g_LineRanges.IsEmpty();
4006     const bool useDumpStatsAfterLine = !g_DumpStatsAfterLine.empty();
4007     const bool useDefragmentAfterLine = !g_DefragmentAfterLine.empty();
4008 
4009     LineSplit lineSplit(data, numBytes);
4010     StrRange line;
4011 
4012     if(!lineSplit.GetNextLine(line) ||
4013         !StrRangeEq(line, "Vulkan Memory Allocator,Calls recording"))
4014     {
4015         printf("ERROR: Incorrect file format.\n");
4016         return RESULT_ERROR_FORMAT;
4017     }
4018 
4019     if(!lineSplit.GetNextLine(line) || !ParseFileVersion(line) || !ValidateFileVersion())
4020     {
4021         printf("ERROR: Incorrect file format version.\n");
4022         return RESULT_ERROR_FORMAT;
4023     }
4024 
4025     if(g_Verbosity == VERBOSITY::MAXIMUM)
4026     {
4027         printf("Format version: %u,%u\n",
4028             GetVersionMajor(g_FileVersion),
4029             GetVersionMinor(g_FileVersion));
4030     }
4031 
4032     // Parse configuration
4033     const bool configEnabled = g_FileVersion >= MakeVersion(1, 3);
4034     ConfigurationParser configParser;
4035     if(configEnabled)
4036     {
4037         if(!configParser.Parse(lineSplit))
4038         {
4039             return RESULT_ERROR_FORMAT;
4040         }
4041     }
4042 
4043     Player player;
4044     int result = player.Init();
4045 
4046     if(configEnabled)
4047     {
4048         player.ApplyConfig(configParser);
4049     }
4050 
4051     size_t executedLineCount = 0;
4052     if(result == 0)
4053     {
4054         if(g_Verbosity > VERBOSITY::MINIMUM)
4055         {
4056             if(useLineRanges)
4057             {
4058                 printf("Playing #%zu (limited range of lines)...\n", iterationIndex + 1);
4059             }
4060             else
4061             {
4062                 printf("Playing #%zu...\n", iterationIndex + 1);
4063             }
4064         }
4065 
4066         const time_point timeBeg = std::chrono::high_resolution_clock::now();
4067 
4068         while(lineSplit.GetNextLine(line))
4069         {
4070             const size_t currLineNumber = lineSplit.GetNextLineIndex();
4071 
4072             bool execute = true;
4073             if(useLineRanges)
4074             {
4075                 execute = g_LineRanges.Includes(currLineNumber);
4076             }
4077 
4078             if(execute)
4079             {
4080                 player.ExecuteLine(currLineNumber, line);
4081                 ++executedLineCount;
4082             }
4083 
4084             while(useDumpStatsAfterLine &&
4085                 g_DumpStatsAfterLineNextIndex < g_DumpStatsAfterLine.size() &&
4086                 currLineNumber >= g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].line)
4087             {
4088                 const size_t requestedLine = g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].line;
4089                 const bool detailed = g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].detailed;
4090 
4091                 if(g_Verbosity == VERBOSITY::MAXIMUM)
4092                 {
4093                     printf("Dumping %sstats after line %zu actual line %zu...\n",
4094                         detailed ? "detailed " : "",
4095                         requestedLine,
4096                         currLineNumber);
4097                 }
4098 
4099                 player.DumpStats("VmaReplay_Line%04zu.json", requestedLine, detailed);
4100 
4101                 ++g_DumpStatsAfterLineNextIndex;
4102             }
4103 
4104             while(useDefragmentAfterLine &&
4105                 g_DefragmentAfterLineNextIndex < g_DefragmentAfterLine.size() &&
4106                 currLineNumber >= g_DefragmentAfterLine[g_DefragmentAfterLineNextIndex])
4107             {
4108                 const size_t requestedLine = g_DefragmentAfterLine[g_DefragmentAfterLineNextIndex];
4109                 if(g_Verbosity >= VERBOSITY::DEFAULT)
4110                 {
4111                     printf("Defragmenting after line %zu actual line %zu...\n",
4112                         requestedLine,
4113                         currLineNumber);
4114                 }
4115 
4116                 player.DumpStats("VmaReplay_Line%04zu_Defragment_1Before.json", requestedLine, true);
4117                 player.Defragment();
4118                 player.DumpStats("VmaReplay_Line%04zu_Defragment_2After.json", requestedLine, true);
4119 
4120                 ++g_DefragmentAfterLineNextIndex;
4121             }
4122         }
4123 
4124         const duration playDuration = std::chrono::high_resolution_clock::now() - timeBeg;
4125         outDuration = playDuration;
4126 
4127         // End stats.
4128         if(g_Verbosity > VERBOSITY::MINIMUM)
4129         {
4130             std::string playDurationStr;
4131             SecondsToFriendlyStr(ToFloatSeconds(playDuration), playDurationStr);
4132 
4133             printf("Done.\n");
4134             printf("Playback took: %s\n", playDurationStr.c_str());
4135         }
4136         if(g_Verbosity == VERBOSITY::MAXIMUM)
4137         {
4138             printf("File lines: %zu\n", lineSplit.GetNextLineIndex());
4139             printf("Executed %zu file lines\n", executedLineCount);
4140         }
4141 
4142         player.PrintStats();
4143     }
4144 
4145     return result;
4146 }
4147 
ProcessFile()4148 static int ProcessFile()
4149 {
4150     if(g_Verbosity > VERBOSITY::MINIMUM)
4151     {
4152         printf("Loading file \"%s\"...\n", g_FilePath.c_str());
4153     }
4154     int result = 0;
4155 
4156     FILE* file = nullptr;
4157     const errno_t err = fopen_s(&file, g_FilePath.c_str(), "rb");
4158     if(err == 0)
4159     {
4160         _fseeki64(file, 0, SEEK_END);
4161         const size_t fileSize = (size_t)_ftelli64(file);
4162         _fseeki64(file, 0, SEEK_SET);
4163 
4164         if(fileSize > 0)
4165         {
4166             std::vector<char> fileContents(fileSize);
4167             fread(fileContents.data(), 1, fileSize, file);
4168 
4169             // Begin stats.
4170             if(g_Verbosity == VERBOSITY::MAXIMUM)
4171             {
4172                 printf("File size: %zu B\n", fileSize);
4173             }
4174 
4175             duration durationSum = duration::zero();
4176             for(size_t i = 0; i < g_IterationCount; ++i)
4177             {
4178                 duration currDuration;
4179                 ProcessFile(i, fileContents.data(), fileContents.size(), currDuration);
4180                 durationSum += currDuration;
4181             }
4182 
4183             if(g_IterationCount > 1)
4184             {
4185                 std::string playDurationStr;
4186                 SecondsToFriendlyStr(ToFloatSeconds(durationSum / g_IterationCount), playDurationStr);
4187                 printf("Average playback time from %zu iterations: %s\n", g_IterationCount, playDurationStr.c_str());
4188             }
4189         }
4190         else
4191         {
4192             printf("ERROR: Source file is empty.\n");
4193             result = RESULT_ERROR_SOURCE_FILE;
4194         }
4195 
4196         fclose(file);
4197     }
4198     else
4199     {
4200         printf("ERROR: Couldn't open file (%i).\n", err);
4201         result = RESULT_ERROR_SOURCE_FILE;
4202     }
4203 
4204     return result;
4205 }
4206 
main2(int argc,char ** argv)4207 static int main2(int argc, char** argv)
4208 {
4209     CmdLineParser cmdLineParser(argc, argv);
4210 
4211     cmdLineParser.RegisterOpt(CMD_LINE_OPT_VERBOSITY, 'v', true);
4212     cmdLineParser.RegisterOpt(CMD_LINE_OPT_ITERATIONS, 'i', true);
4213     cmdLineParser.RegisterOpt(CMD_LINE_OPT_LINES, "Lines", true);
4214     cmdLineParser.RegisterOpt(CMD_LINE_OPT_PHYSICAL_DEVICE, "PhysicalDevice", true);
4215     cmdLineParser.RegisterOpt(CMD_LINE_OPT_USER_DATA, "UserData", true);
4216     cmdLineParser.RegisterOpt(CMD_LINE_OPT_VK_EXT_MEMORY_BUDGET, "VK_EXT_memory_budget", true);
4217     cmdLineParser.RegisterOpt(CMD_LINE_OPT_VK_LAYER_LUNARG_STANDARD_VALIDATION, VALIDATION_LAYER_NAME, true);
4218     cmdLineParser.RegisterOpt(CMD_LINE_OPT_MEM_STATS, "MemStats", true);
4219     cmdLineParser.RegisterOpt(CMD_LINE_OPT_DUMP_STATS_AFTER_LINE, "DumpStatsAfterLine", true);
4220     cmdLineParser.RegisterOpt(CMD_LINE_OPT_DEFRAGMENT_AFTER_LINE, "DefragmentAfterLine", true);
4221     cmdLineParser.RegisterOpt(CMD_LINE_OPT_DEFRAGMENTATION_FLAGS, "DefragmentationFlags", true);
4222     cmdLineParser.RegisterOpt(CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE, "DumpDetailedStatsAfterLine", true);
4223 
4224     CmdLineParser::RESULT res;
4225     while((res = cmdLineParser.ReadNext()) != CmdLineParser::RESULT_END)
4226     {
4227         switch(res)
4228         {
4229         case CmdLineParser::RESULT_OPT:
4230             switch(cmdLineParser.GetOptId())
4231             {
4232             case CMD_LINE_OPT_VERBOSITY:
4233                 {
4234                     uint32_t verbosityVal = UINT32_MAX;
4235                     if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), verbosityVal) &&
4236                         verbosityVal < (uint32_t)VERBOSITY::COUNT)
4237                     {
4238                         g_Verbosity = (VERBOSITY)verbosityVal;
4239                     }
4240                     else
4241                     {
4242                         PrintCommandLineSyntax();
4243                         return RESULT_ERROR_COMMAND_LINE;
4244                     }
4245                 }
4246                 break;
4247             case CMD_LINE_OPT_ITERATIONS:
4248                 if(!StrRangeToUint(StrRange(cmdLineParser.GetParameter()), g_IterationCount))
4249                 {
4250                     PrintCommandLineSyntax();
4251                     return RESULT_ERROR_COMMAND_LINE;
4252                 }
4253                 break;
4254             case CMD_LINE_OPT_LINES:
4255                 if(!g_LineRanges.Parse(StrRange(cmdLineParser.GetParameter())))
4256                 {
4257                     PrintCommandLineSyntax();
4258                     return RESULT_ERROR_COMMAND_LINE;
4259                 }
4260                 break;
4261             case CMD_LINE_OPT_PHYSICAL_DEVICE:
4262                 if(!StrRangeToUint(StrRange(cmdLineParser.GetParameter()), g_PhysicalDeviceIndex))
4263                 {
4264                     PrintCommandLineSyntax();
4265                     return RESULT_ERROR_COMMAND_LINE;
4266                 }
4267                 break;
4268             case CMD_LINE_OPT_USER_DATA:
4269                 if(!StrRangeToBool(StrRange(cmdLineParser.GetParameter()), g_UserDataEnabled))
4270                 {
4271                     PrintCommandLineSyntax();
4272                     return RESULT_ERROR_COMMAND_LINE;
4273                 }
4274                 break;
4275             case CMD_LINE_OPT_VK_EXT_MEMORY_BUDGET:
4276                 {
4277                     bool newValue;
4278                     if(StrRangeToBool(StrRange(cmdLineParser.GetParameter()), newValue))
4279                     {
4280                         g_VK_EXT_memory_budget_request = newValue ?
4281                             VULKAN_EXTENSION_REQUEST::ENABLED :
4282                             VULKAN_EXTENSION_REQUEST::DISABLED;
4283                     }
4284                     else
4285                     {
4286                         PrintCommandLineSyntax();
4287                         return RESULT_ERROR_COMMAND_LINE;
4288                     }
4289                 }
4290                 break;
4291             case CMD_LINE_OPT_VK_LAYER_LUNARG_STANDARD_VALIDATION:
4292                 {
4293                     bool newValue;
4294                     if(StrRangeToBool(StrRange(cmdLineParser.GetParameter()), newValue))
4295                     {
4296                         g_VK_LAYER_LUNARG_standard_validation = newValue ?
4297                             VULKAN_EXTENSION_REQUEST::ENABLED :
4298                             VULKAN_EXTENSION_REQUEST::DISABLED;
4299                     }
4300                     else
4301                     {
4302                         PrintCommandLineSyntax();
4303                         return RESULT_ERROR_COMMAND_LINE;
4304                     }
4305                 }
4306                 break;
4307             case CMD_LINE_OPT_MEM_STATS:
4308                 if(!StrRangeToBool(StrRange(cmdLineParser.GetParameter()), g_MemStatsEnabled))
4309                 {
4310                     PrintCommandLineSyntax();
4311                     return RESULT_ERROR_COMMAND_LINE;
4312                 }
4313                 break;
4314             case CMD_LINE_OPT_DUMP_STATS_AFTER_LINE:
4315             case CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE:
4316                 {
4317                     size_t line;
4318                     if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), line))
4319                     {
4320                         const bool detailed =
4321                             cmdLineParser.GetOptId() == CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE;
4322                         g_DumpStatsAfterLine.push_back({line, detailed});
4323                     }
4324                     else
4325                     {
4326                         PrintCommandLineSyntax();
4327                         return RESULT_ERROR_COMMAND_LINE;
4328                     }
4329                 }
4330                 break;
4331             case CMD_LINE_OPT_DEFRAGMENT_AFTER_LINE:
4332                 {
4333                     size_t line;
4334                     if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), line))
4335                     {
4336                         g_DefragmentAfterLine.push_back(line);
4337                     }
4338                     else
4339                     {
4340                         PrintCommandLineSyntax();
4341                         return RESULT_ERROR_COMMAND_LINE;
4342                     }
4343                 }
4344                 break;
4345             case CMD_LINE_OPT_DEFRAGMENTATION_FLAGS:
4346                 {
4347                     if(!StrRangeToUint(StrRange(cmdLineParser.GetParameter()), g_DefragmentationFlags))
4348                     {
4349                         PrintCommandLineSyntax();
4350                         return RESULT_ERROR_COMMAND_LINE;
4351                     }
4352                 }
4353                 break;
4354             default:
4355                 assert(0);
4356             }
4357             break;
4358         case CmdLineParser::RESULT_PARAMETER:
4359             if(g_FilePath.empty())
4360             {
4361                 g_FilePath = cmdLineParser.GetParameter();
4362             }
4363             else
4364             {
4365                 PrintCommandLineSyntax();
4366                 return RESULT_ERROR_COMMAND_LINE;
4367             }
4368             break;
4369         case CmdLineParser::RESULT_ERROR:
4370             PrintCommandLineSyntax();
4371             return RESULT_ERROR_COMMAND_LINE;
4372             break;
4373         default:
4374             assert(0);
4375         }
4376     }
4377 
4378     // Postprocess command line parameters.
4379 
4380     if(g_FilePath.empty())
4381     {
4382         PrintCommandLineSyntax();
4383         return RESULT_ERROR_COMMAND_LINE;
4384     }
4385 
4386     // Sort g_DumpStatsAfterLine and make unique.
4387     std::sort(g_DumpStatsAfterLine.begin(), g_DumpStatsAfterLine.end());
4388     g_DumpStatsAfterLine.erase(
4389         std::unique(g_DumpStatsAfterLine.begin(), g_DumpStatsAfterLine.end()),
4390         g_DumpStatsAfterLine.end());
4391 
4392     // Sort g_DefragmentAfterLine and make unique.
4393     std::sort(g_DefragmentAfterLine.begin(), g_DefragmentAfterLine.end());
4394     g_DefragmentAfterLine.erase(
4395         std::unique(g_DefragmentAfterLine.begin(), g_DefragmentAfterLine.end()),
4396         g_DefragmentAfterLine.end());
4397 
4398     return ProcessFile();
4399 }
4400 
main(int argc,char ** argv)4401 int main(int argc, char** argv)
4402 {
4403     try
4404     {
4405         return main2(argc, argv);
4406     }
4407     catch(const std::exception& e)
4408     {
4409         printf("ERROR: %s\n", e.what());
4410         return RESULT_EXCEPTION;
4411     }
4412     catch(...)
4413     {
4414         printf("UNKNOWN ERROR\n");
4415         return RESULT_EXCEPTION;
4416     }
4417 }
4418