Added parameters: --DumpStatsAfterLine, --DumpDetailedStatsAfterLine, to dump VMA stats to JSON file.

Set --MemStats default to 0.
diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp
index c714d6f..a71e5a6 100644
--- a/src/VmaReplay/VmaReplay.cpp
+++ b/src/VmaReplay/VmaReplay.cpp
@@ -40,6 +40,8 @@
     CMD_LINE_OPT_VK_KHR_DEDICATED_ALLOCATION,

     CMD_LINE_OPT_VK_LAYER_LUNARG_STANDARD_VALIDATION,

     CMD_LINE_OPT_MEM_STATS,

+    CMD_LINE_OPT_DUMP_STATS_AFTER_LINE,

+    CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE,

 };

 

 static enum class VERBOSITY

@@ -119,10 +121,21 @@
 static uint32_t g_PhysicalDeviceIndex = 0;

 static RangeSequence<size_t> g_LineRanges;

 static bool g_UserDataEnabled = true;

-static bool g_MemStatsEnabled = true;

+static bool g_MemStatsEnabled = false;

 VULKAN_EXTENSION_REQUEST g_VK_KHR_dedicated_allocation_request = VULKAN_EXTENSION_REQUEST::DEFAULT;

 VULKAN_EXTENSION_REQUEST g_VK_LAYER_LUNARG_standard_validation = VULKAN_EXTENSION_REQUEST::DEFAULT;

 

+struct StatsAfterLineEntry

+{

+    size_t line;

+    bool detailed;

+

+    bool operator<(const StatsAfterLineEntry& rhs) const { return line < rhs.line; }

+    bool operator==(const StatsAfterLineEntry& rhs) const { return line == rhs.line; }

+};

+static std::vector<StatsAfterLineEntry> g_DumpStatsAfterLine;

+static size_t g_DumpStatsAfterLineNextIndex = 0;

+

 static bool ValidateFileVersion()

 {

     const uint32_t major = g_FileVersion >> 16;

@@ -483,6 +496,8 @@
     ~Player();

 

     void ExecuteLine(size_t lineNumber, const StrRange& line);

+    void DumpStats(const char* fileNameFormat, size_t lineNumber, bool detailed);

+

     void PrintStats();

 

 private:

@@ -727,6 +742,29 @@
     }

 }

 

+void Player::DumpStats(const char* fileNameFormat, size_t lineNumber, bool detailed)

+{

+    char* pStatsString = nullptr;

+    vmaBuildStatsString(m_Allocator, &pStatsString, detailed ? VK_TRUE : VK_FALSE);

+

+    char fileName[MAX_PATH];

+    sprintf_s(fileName, fileNameFormat, lineNumber);

+

+    FILE* file = nullptr;

+    errno_t err = fopen_s(&file, fileName, "wb");

+    if(err == 0)

+    {

+        fwrite(pStatsString, 1, strlen(pStatsString), file);

+        fclose(file);

+    }

+    else

+    {

+        printf("ERROR: Failed to write file: %s\n", fileName);

+    }

+

+    vmaFreeStatsString(m_Allocator, pStatsString);

+}

+

 void Player::Destroy(const Allocation& alloc)

 {

     if(alloc.buffer)

@@ -2145,7 +2183,11 @@
         "    -i <Number> - Repeat playback given number of times (iterations)\n"

         "        Default is 1. Vulkan is reinitialized with every iteration.\n"

         "    --MemStats <Value> - 0 to disable or 1 to enable memory statistics.\n"

-        "        Default is 1. Enabling it may negatively impact playback performance.\n"

+        "        Default is 0. Enabling it may negatively impact playback performance.\n"

+        "    --DumpStatsAfterLine <Line> - Dump VMA statistics to JSON file after specified source file line finishes execution.\n"

+        "        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"

         "    --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"

@@ -2163,6 +2205,7 @@
     outDuration = duration::max();

 

     const bool useLineRanges = !g_LineRanges.IsEmpty();

+    const bool useDumpStatsAfterLine = !g_DumpStatsAfterLine.empty();

 

     LineSplit lineSplit(data, numBytes);

     StrRange line;

@@ -2206,17 +2249,39 @@
 

         while(lineSplit.GetNextLine(line))

         {

+            const size_t currLineNumber = lineSplit.GetNextLineIndex();

+

             bool execute = true;

             if(useLineRanges)

             {

-                execute = g_LineRanges.Includes(lineSplit.GetNextLineIndex());

+                execute = g_LineRanges.Includes(currLineNumber);

             }

 

             if(execute)

             {

-                player.ExecuteLine(lineSplit.GetNextLineIndex(), line);

+                player.ExecuteLine(currLineNumber, line);

                 ++executedLineCount;

             }

+

+            while(useDumpStatsAfterLine &&

+                g_DumpStatsAfterLineNextIndex < g_DumpStatsAfterLine.size() &&

+                currLineNumber >= g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].line)

+            {

+                const size_t requestedLine = g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].line;

+                const bool detailed = g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].detailed;

+                

+                if(g_Verbosity == VERBOSITY::MAXIMUM)

+                {

+                    printf("Dumping %sstats after line %zu actual line %zu...\n",

+                        detailed ? "detailed " : "",

+                        requestedLine,

+                        currLineNumber);

+                }

+

+                player.DumpStats("VmaReplay_Line%04zu.json", requestedLine, detailed);

+                

+                ++g_DumpStatsAfterLineNextIndex;

+            }

         }

 

         const duration playDuration = std::chrono::high_resolution_clock::now() - timeBeg;

@@ -2314,6 +2379,8 @@
     cmdLineParser.RegisterOpt(CMD_LINE_OPT_VK_KHR_DEDICATED_ALLOCATION, "VK_KHR_dedicated_allocation", true);

     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_DUMP_DETAILED_STATS_AFTER_LINE, "DumpDetailedStatsAfterLine", true);

 

     CmdLineParser::RESULT res;

     while((res = cmdLineParser.ReadNext()) != CmdLineParser::RESULT_END)

@@ -2405,6 +2472,23 @@
                     return RESULT_ERROR_COMMAND_LINE;

                 }

                 break;

+            case CMD_LINE_OPT_DUMP_STATS_AFTER_LINE:

+            case CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE:

+                {

+                    size_t line;

+                    if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), line))

+                    {

+                        const bool detailed =

+                            cmdLineParser.GetOptId() == CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE;

+                        g_DumpStatsAfterLine.push_back({line, detailed});

+                    }

+                    else

+                    {

+                        PrintCommandLineSyntax();

+                        return RESULT_ERROR_COMMAND_LINE;

+                    }

+                }

+                break;

             default:

                 assert(0);

             }

@@ -2429,12 +2513,20 @@
         }

     }

 

+    // Postprocess command line parameters.

+

     if(g_FilePath.empty())

     {

         PrintCommandLineSyntax();

         return RESULT_ERROR_COMMAND_LINE;

     }

 

+    // Sort g_DumpStatsAfterLine and make unique.

+    std::sort(g_DumpStatsAfterLine.begin(), g_DumpStatsAfterLine.end());

+    g_DumpStatsAfterLine.erase(

+        std::unique(g_DumpStatsAfterLine.begin(), g_DumpStatsAfterLine.end()),

+        g_DumpStatsAfterLine.end());

+

     return ProcessFile();

 }