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