VmaReplay: Added parameter --DefragmentAfterLine.
diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp
index 9635b03..88c8642 100644
--- a/src/VmaReplay/VmaReplay.cpp
+++ b/src/VmaReplay/VmaReplay.cpp
@@ -41,6 +41,7 @@
CMD_LINE_OPT_VK_LAYER_LUNARG_STANDARD_VALIDATION,
CMD_LINE_OPT_MEM_STATS,
CMD_LINE_OPT_DUMP_STATS_AFTER_LINE,
+ CMD_LINE_OPT_DEFRAGMENT_AFTER_LINE,
CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE,
};
@@ -138,7 +139,9 @@
bool operator==(const StatsAfterLineEntry& rhs) const { return line == rhs.line; }
};
static std::vector<StatsAfterLineEntry> g_DumpStatsAfterLine;
+static std::vector<size_t> g_DefragmentAfterLine;
static size_t g_DumpStatsAfterLineNextIndex = 0;
+static size_t g_DefragmentAfterLineNextIndex = 0;
static bool ValidateFileVersion()
{
@@ -921,6 +924,7 @@
void ApplyConfig(ConfigurationParser& configParser);
void ExecuteLine(size_t lineNumber, const StrRange& line);
void DumpStats(const char* fileNameFormat, size_t lineNumber, bool detailed);
+ void Defragment();
void PrintStats();
@@ -1017,6 +1021,9 @@
void ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit);
void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit);
+
+ void PrintStats(const VmaStats& stats, const char* suffix);
+ void PrintStatInfo(const VmaStatInfo& info);
};
Player::Player()
@@ -1625,6 +1632,92 @@
assert(res == VK_SUCCESS);
}
+void Player::Defragment()
+{
+ VmaStats stats;
+ vmaCalculateStats(m_Allocator, &stats);
+ PrintStats(stats, "before defragmentation");
+
+ const size_t allocCount = m_Allocations.size();
+ std::vector<VmaAllocation> allocations(allocCount);
+ size_t notNullAllocCount = 0;
+ for(const auto& it : m_Allocations)
+ {
+ if(it.second.allocation != VK_NULL_HANDLE)
+ {
+ allocations[notNullAllocCount] = it.second.allocation;
+ ++notNullAllocCount;
+ }
+ }
+ if(notNullAllocCount == 0)
+ {
+ printf(" Nothing to defragment.\n");
+ return;
+ }
+
+ allocations.resize(notNullAllocCount);
+ std::vector<VkBool32> allocationsChanged(notNullAllocCount);
+
+ VmaDefragmentationInfo2 defragInfo = {};
+ defragInfo.allocationCount = (uint32_t)notNullAllocCount;
+ defragInfo.pAllocations = allocations.data();
+ defragInfo.pAllocationsChanged = allocationsChanged.data();
+ defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
+ defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
+ defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
+ defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
+ defragInfo.flags = VMA_DEFRAGMENTATION_CAN_MAKE_LOST_BIT;
+
+ VmaDefragmentationStats defragStats = {};
+
+ VmaDefragmentationContext defragCtx = VK_NULL_HANDLE;
+ VkResult res = vmaDefragmentationBegin(m_Allocator, &defragInfo, &defragStats, &defragCtx);
+ if(res >= VK_SUCCESS)
+ {
+ // TODO use commandBuffer for GPU defrag.
+ vmaDefragmentationEnd(m_Allocator, defragCtx);
+
+ // If anything changed.
+ if(defragStats.allocationsLost > 0 || defragStats.allocationsMoved > 0)
+ {
+ // Go over allocation that changed and destroy their buffers and images.
+ size_t i = 0;
+ for(auto& it : m_Allocations)
+ {
+ if(allocationsChanged[i] != VK_FALSE)
+ {
+ if(it.second.buffer != VK_NULL_HANDLE)
+ {
+ vkDestroyBuffer(m_Device, it.second.buffer, nullptr);
+ it.second.buffer = VK_NULL_HANDLE;
+ }
+ if(it.second.image != VK_NULL_HANDLE)
+ {
+ vkDestroyImage(m_Device, it.second.image, nullptr);
+ it.second.image = VK_NULL_HANDLE;
+ }
+ }
+ ++i;
+ }
+ }
+
+ // Print statistics
+ printf(" VmaDefragmentationStats:\n");
+ printf(" bytesMoved: %llu\n", defragStats.bytesMoved);
+ printf(" bytesFreed: %llu\n", defragStats.bytesFreed);
+ printf(" allocationsMoved: %u\n", defragStats.allocationsMoved);
+ printf(" deviceMemoryBlocksMoved: %u\n", defragStats.deviceMemoryBlocksFreed);
+ printf(" allocationsLost: %u\n", defragStats.allocationsLost);
+
+ vmaCalculateStats(m_Allocator, &stats);
+ PrintStats(stats, "after defragmentation");
+ }
+ else
+ {
+ printf("vmaDefragmentationBegin failed (%d).\n", res);
+ }
+}
+
void Player::PrintStats()
{
if(g_Verbosity == VERBOSITY::MINIMUM)
@@ -1996,6 +2089,9 @@
{
FindPool(lineNumber, origPool, allocCreateInfo.pool);
+ // Forcing VK_SHARING_MODE_EXCLUSIVE because we use only one queue anyway.
+ bufCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 11)
{
PrepareUserData(
@@ -2060,6 +2156,42 @@
}
}
+void Player::PrintStats(const VmaStats& stats, const char* suffix)
+{
+ printf(" VmaStats %s:\n", suffix);
+ printf(" total:\n");
+ PrintStatInfo(stats.total);
+
+ if(g_Verbosity == VERBOSITY::MAXIMUM)
+ {
+ for(uint32_t i = 0; i < m_MemProps->memoryHeapCount; ++i)
+ {
+ printf(" memoryHeap[%u]:\n", i);
+ PrintStatInfo(stats.memoryHeap[i]);
+ }
+ for(uint32_t i = 0; i < m_MemProps->memoryTypeCount; ++i)
+ {
+ printf(" memoryType[%u]:\n", i);
+ PrintStatInfo(stats.memoryType[i]);
+ }
+ }
+}
+
+void Player::PrintStatInfo(const VmaStatInfo& info)
+{
+ printf(" blockCount: %u\n", info.blockCount);
+ printf(" allocationCount: %u\n", info.allocationCount);
+ printf(" unusedRangeCount: %u\n", info.unusedRangeCount);
+ printf(" usedBytes: %llu\n", info.usedBytes);
+ printf(" unusedBytes: %llu\n", info.unusedBytes);
+ printf(" allocationSizeMin: %llu\n", info.allocationSizeMin);
+ printf(" allocationSizeAvg: %llu\n", info.allocationSizeAvg);
+ printf(" allocationSizeMax: %llu\n", info.allocationSizeMax);
+ printf(" unusedRangeSizeMin: %llu\n", info.unusedRangeSizeMin);
+ printf(" unusedRangeSizeAvg: %llu\n", info.unusedRangeSizeAvg);
+ printf(" unusedRangeSizeMax: %llu\n", info.unusedRangeSizeMax);
+}
+
void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit)
{
m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreateImage);
@@ -2094,6 +2226,9 @@
{
FindPool(lineNumber, origPool, allocCreateInfo.pool);
+ // Forcing VK_SHARING_MODE_EXCLUSIVE because we use only one queue anyway.
+ imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 20)
{
PrepareUserData(
@@ -2620,6 +2755,8 @@
" File is written to current directory with name: VmaReplay_Line####.json.\n"
" This parameter can be repeated.\n"
" --DumpDetailedStatsAfterLine <Line> - Like command above, but includes detailed map.\n"
+ " --DefragmentAfterLine <Line> - Defragment memory after specified source file line and print statistics.\n"
+ " It also prints detailed statistics to files VmaReplay_Line####_Defragment*.json\n"
" --Lines <Ranges> - Replay only limited set of lines from file\n"
" Ranges is comma-separated list of ranges, e.g. \"-10,15,18-25,31-\".\n"
" --PhysicalDevice <Index> - Choice of Vulkan physical device. Default: 0.\n"
@@ -2638,6 +2775,7 @@
const bool useLineRanges = !g_LineRanges.IsEmpty();
const bool useDumpStatsAfterLine = !g_DumpStatsAfterLine.empty();
+ const bool useDefragmentAfterLine = !g_DefragmentAfterLine.empty();
LineSplit lineSplit(data, numBytes);
StrRange line;
@@ -2733,6 +2871,25 @@
++g_DumpStatsAfterLineNextIndex;
}
+
+ while(useDefragmentAfterLine &&
+ g_DefragmentAfterLineNextIndex < g_DefragmentAfterLine.size() &&
+ currLineNumber >= g_DefragmentAfterLine[g_DefragmentAfterLineNextIndex])
+ {
+ const size_t requestedLine = g_DefragmentAfterLine[g_DefragmentAfterLineNextIndex];
+ if(g_Verbosity >= VERBOSITY::DEFAULT)
+ {
+ printf("Defragmenting after line %zu actual line %zu...\n",
+ requestedLine,
+ currLineNumber);
+ }
+
+ player.DumpStats("VmaReplay_Line%04zu_Defragment_1Before.json", requestedLine, true);
+ player.Defragment();
+ player.DumpStats("VmaReplay_Line%04zu_Defragment_2After.json", requestedLine, true);
+
+ ++g_DefragmentAfterLineNextIndex;
+ }
}
const duration playDuration = std::chrono::high_resolution_clock::now() - timeBeg;
@@ -2831,6 +2988,7 @@
cmdLineParser.RegisterOpt(CMD_LINE_OPT_VK_LAYER_LUNARG_STANDARD_VALIDATION, VALIDATION_LAYER_NAME, true);
cmdLineParser.RegisterOpt(CMD_LINE_OPT_MEM_STATS, "MemStats", true);
cmdLineParser.RegisterOpt(CMD_LINE_OPT_DUMP_STATS_AFTER_LINE, "DumpStatsAfterLine", true);
+ cmdLineParser.RegisterOpt(CMD_LINE_OPT_DEFRAGMENT_AFTER_LINE, "DefragmentAfterLine", true);
cmdLineParser.RegisterOpt(CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE, "DumpDetailedStatsAfterLine", true);
CmdLineParser::RESULT res;
@@ -2940,6 +3098,20 @@
}
}
break;
+ case CMD_LINE_OPT_DEFRAGMENT_AFTER_LINE:
+ {
+ size_t line;
+ if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), line))
+ {
+ g_DefragmentAfterLine.push_back(line);
+ }
+ else
+ {
+ PrintCommandLineSyntax();
+ return RESULT_ERROR_COMMAND_LINE;
+ }
+ }
+ break;
default:
assert(0);
}
@@ -2978,6 +3150,12 @@
std::unique(g_DumpStatsAfterLine.begin(), g_DumpStatsAfterLine.end()),
g_DumpStatsAfterLine.end());
+ // Sort g_DefragmentAfterLine and make unique.
+ std::sort(g_DefragmentAfterLine.begin(), g_DefragmentAfterLine.end());
+ g_DefragmentAfterLine.erase(
+ std::unique(g_DefragmentAfterLine.begin(), g_DefragmentAfterLine.end()),
+ g_DefragmentAfterLine.end());
+
return ProcessFile();
}