Updated file format to 1,3. Added configuration section.
diff --git a/docs/Recording file format.md b/docs/Recording file format.md
index d6c2878..b01eaf4 100644
--- a/docs/Recording file format.md
+++ b/docs/Recording file format.md
@@ -23,7 +23,49 @@
 VmaReplay application supports all older versions.

 Current version is:

 

-    1,2

+    1,3

+

+# Configuration

+

+Header is followed by mandatory configuration section (min format version 1.3). It starts with line:

+

+    Config,Begin

+

+And ends with line:

+

+    Config,End

+

+Between them there can be zero or more lines with configuration options. They store values of various variables from the current environment from the time of recording, like properties and limits of Vulkan physical device, available memory heaps and types, enabled Vulkan extensions, as well macros that configure VMA internals. Supported configuration options are:

+

+    PhysicalDevice,apiVersion,<uint32>

+    PhysicalDevice,driverVersion,<uint32>

+    PhysicalDevice,vendorID,<uint32>

+    PhysicalDevice,deviceID,<uint32>

+    PhysicalDevice,deviceType,<uint32>

+    PhysicalDevice,deviceName,<string>

+    

+    PhysicalDeviceLimits,maxMemoryAllocationCount,<uint32>

+    PhysicalDeviceLimits,bufferImageGranularity,<uint64>

+    PhysicalDeviceLimits,nonCoherentAtomSize,<uint64>

+    

+    PhysicalDeviceMemory,HeapCount,<uint32>

+    PhysicalDeviceMemory,Heap,<index:uint32>,size,<uint64>

+    PhysicalDeviceMemory,Heap,<index:uint32>,flags,<uint32>

+    PhysicalDeviceMemory,TypeCount,<uint32>

+    PhysicalDeviceMemory,Type,<index:uint32>,heapIndex,<uint32>

+    PhysicalDeviceMemory,Type,<index:uint32>,propertyFlags,<uint32>

+    

+    Extension,VK_KHR_dedicated_allocation,<bool>

+    

+    Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,<bool>

+    Macro,VMA_DEBUG_ALIGNMENT,<uint64>

+    Macro,VMA_DEBUG_MARGIN,<uint64>

+    Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,<bool>

+    Macro,VMA_DEBUG_DETECT_CORRUPTION,<bool>

+    Macro,VMA_DEBUG_GLOBAL_MUTEX,<bool>

+    Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,<uint64>

+    Macro,VMA_SMALL_HEAP_MAX_SIZE,<uint64>

+    Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,<uint64>

 

 # Function calls

 

@@ -162,7 +204,7 @@
 

 - pool : pointer

 

-## Data types

+# Data types

 

 **bool**

 

@@ -186,7 +228,44 @@
 # Example file

 

     Vulkan Memory Allocator,Calls recording

-    1,2

+    1,3

+    Config,Begin

+    PhysicalDevice,apiVersion,4198477

+    PhysicalDevice,driverVersion,8388653

+    PhysicalDevice,vendorID,4098

+    PhysicalDevice,deviceID,26751

+    PhysicalDevice,deviceType,2

+    PhysicalDevice,deviceName,Radeon RX Vega

+    PhysicalDeviceLimits,maxMemoryAllocationCount,4096

+    PhysicalDeviceLimits,bufferImageGranularity,1

+    PhysicalDeviceLimits,nonCoherentAtomSize,128

+    PhysicalDeviceMemory,HeapCount,3

+    PhysicalDeviceMemory,Heap,0,size,8304721920

+    PhysicalDeviceMemory,Heap,0,flags,3

+    PhysicalDeviceMemory,Heap,1,size,8286175232

+    PhysicalDeviceMemory,Heap,1,flags,0

+    PhysicalDeviceMemory,Heap,2,size,268435456

+    PhysicalDeviceMemory,Heap,2,flags,3

+    PhysicalDeviceMemory,TypeCount,4

+    PhysicalDeviceMemory,Type,0,heapIndex,0

+    PhysicalDeviceMemory,Type,0,propertyFlags,1

+    PhysicalDeviceMemory,Type,1,heapIndex,1

+    PhysicalDeviceMemory,Type,1,propertyFlags,6

+    PhysicalDeviceMemory,Type,2,heapIndex,2

+    PhysicalDeviceMemory,Type,2,propertyFlags,7

+    PhysicalDeviceMemory,Type,3,heapIndex,1

+    PhysicalDeviceMemory,Type,3,propertyFlags,14

+    Extension,VK_KHR_dedicated_allocation,1

+    Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,0

+    Macro,VMA_DEBUG_ALIGNMENT,1

+    Macro,VMA_DEBUG_MARGIN,0

+    Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,0

+    Macro,VMA_DEBUG_DETECT_CORRUPTION,0

+    Macro,VMA_DEBUG_GLOBAL_MUTEX,0

+    Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,1

+    Macro,VMA_SMALL_HEAP_MAX_SIZE,1073741824

+    Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,268435456

+    Config,End 

     12552,0.000,0,vmaCreateAllocator

     12552,0.010,0,vmaCreateImage,0,1,37,128,128,1,1,1,1,1,1,0,8,36,2,0,0,0,0000000000000000,000001D85B8B1A80,

     12552,0.010,0,vmaSetAllocationUserData,000001D85B8B1A80,Ala ma kota

diff --git a/src/VmaReplay/Common.h b/src/VmaReplay/Common.h
index e4b6251..28b5bbc 100644
--- a/src/VmaReplay/Common.h
+++ b/src/VmaReplay/Common.h
@@ -53,7 +53,7 @@
     explicit StrRange(const std::string& s) : beg(s.data()), end(s.data() + s.length()) { }

 

     size_t length() const { return end - beg; }

-    void to_str(std::string& out) { out.assign(beg, end); }

+    void to_str(std::string& out) const { out.assign(beg, end); }

 };

 

 inline bool StrRangeEq(const StrRange& lhs, const char* rhsSz)

diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp
index a71e5a6..feb5005 100644
--- a/src/VmaReplay/VmaReplay.cpp
+++ b/src/VmaReplay/VmaReplay.cpp
@@ -117,6 +117,10 @@
 // Most significant 16 bits are major version, least significant 16 bits are minor version.

 static uint32_t g_FileVersion;

 

+inline uint32_t MakeVersion(uint32_t major, uint32_t minor) { return (major << 16) | minor; }

+inline uint32_t GetVersionMajor(uint32_t version) { return version >> 16; }

+inline uint32_t GetVersionMinor(uint32_t version) { return version & 0xFFFF; }

+

 static size_t g_IterationCount = 1;

 static uint32_t g_PhysicalDeviceIndex = 0;

 static RangeSequence<size_t> g_LineRanges;

@@ -138,9 +142,13 @@
 

 static bool ValidateFileVersion()

 {

-    const uint32_t major = g_FileVersion >> 16;

-    const uint32_t minor = g_FileVersion & 0xFFFF;

-    return major == 1 && minor <= 2;

+    if(GetVersionMajor(g_FileVersion) == 1 &&

+        GetVersionMinor(g_FileVersion) <= 3)

+    {

+        return true;

+    }

+

+    return false;

 }

 

 static bool ParseFileVersion(const StrRange& s)

@@ -405,6 +413,373 @@
 }

 

 ////////////////////////////////////////////////////////////////////////////////

+// class ConfigurationParser

+

+class ConfigurationParser

+{

+public:

+    ConfigurationParser();

+    

+    bool Parse(LineSplit& lineSplit);

+

+    void Compare(

+        const VkPhysicalDeviceProperties& devProps,

+        const VkPhysicalDeviceMemoryProperties& memProps,

+        bool dedicatedAllocationExtensionEnabled);

+

+private:

+    enum class OPTION

+    {

+        PhysicalDevice_apiVersion,

+        PhysicalDevice_driverVersion,

+        PhysicalDevice_vendorID,

+        PhysicalDevice_deviceID,

+        PhysicalDevice_deviceType,

+        PhysicalDevice_deviceName,

+        PhysicalDeviceLimits_maxMemoryAllocationCount,

+        PhysicalDeviceLimits_bufferImageGranularity,

+        PhysicalDeviceLimits_nonCoherentAtomSize,

+        Extension_VK_KHR_dedicated_allocation,

+        Macro_VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,

+        Macro_VMA_DEBUG_ALIGNMENT,

+        Macro_VMA_DEBUG_MARGIN,

+        Macro_VMA_DEBUG_INITIALIZE_ALLOCATIONS,

+        Macro_VMA_DEBUG_DETECT_CORRUPTION,

+        Macro_VMA_DEBUG_GLOBAL_MUTEX,

+        Macro_VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,

+        Macro_VMA_SMALL_HEAP_MAX_SIZE,

+        Macro_VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,

+        Count

+    };

+

+    std::vector<bool> m_OptionSet;

+    std::vector<std::string> m_OptionValue;

+    VkPhysicalDeviceMemoryProperties m_MemProps;

+

+    bool m_WarningHeaderPrinted = false;

+

+    void SetOption(

+        size_t lineNumber,

+        OPTION option,

+        const StrRange& str);

+    void EnsureWarningHeader();

+    void CompareOption(VERBOSITY minVerbosity, const char* name,

+        OPTION option, uint32_t currValue);

+    void CompareOption(VERBOSITY minVerbosity, const char* name,

+        OPTION option, uint64_t currValue);

+    void CompareOption(VERBOSITY minVerbosity, const char* name,

+        OPTION option, bool currValue);

+    void CompareOption(VERBOSITY minVerbosity, const char* name,

+        OPTION option, const char* currValue);

+};

+

+ConfigurationParser::ConfigurationParser() :

+    m_OptionSet((size_t)OPTION::Count),

+    m_OptionValue((size_t)OPTION::Count)

+{

+    ZeroMemory(&m_MemProps, sizeof(m_MemProps));

+}

+

+bool ConfigurationParser::Parse(LineSplit& lineSplit)

+{

+    for(auto& it : m_OptionSet)

+    {

+        it = false;

+    }

+    for(auto& it : m_OptionValue)

+    {

+        it.clear();

+    }

+

+    StrRange line;

+

+    if(!lineSplit.GetNextLine(line) && !StrRangeEq(line, "Config,Begin"))

+    {

+        return false;

+    }

+

+    CsvSplit csvSplit;

+    while(lineSplit.GetNextLine(line))

+    {

+        if(StrRangeEq(line, "Config,End"))

+        {

+            break;

+        }

+

+        const size_t currLineNumber = lineSplit.GetNextLineIndex();

+

+        csvSplit.Set(line);

+        if(csvSplit.GetCount() == 0)

+        {

+            return false;

+        }

+

+        const StrRange optionName = csvSplit.GetRange(0);

+        if(StrRangeEq(optionName, "PhysicalDevice"))

+        {

+            if(csvSplit.GetCount() >= 3)

+            {

+                const StrRange subOptionName = csvSplit.GetRange(1);

+                if(StrRangeEq(subOptionName, "apiVersion"))

+                    SetOption(currLineNumber, OPTION::PhysicalDevice_apiVersion, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "driverVersion"))

+                    SetOption(currLineNumber, OPTION::PhysicalDevice_driverVersion, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "vendorID"))

+                    SetOption(currLineNumber, OPTION::PhysicalDevice_vendorID, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "deviceID"))

+                    SetOption(currLineNumber, OPTION::PhysicalDevice_deviceID, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "deviceType"))

+                    SetOption(currLineNumber, OPTION::PhysicalDevice_deviceType, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "deviceName"))

+                    SetOption(currLineNumber, OPTION::PhysicalDevice_deviceName, StrRange(csvSplit.GetRange(2).beg, line.end));

+                else

+                    printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);

+            }

+            else

+                printf("Line %zu: Too few columns.\n", currLineNumber);

+        }

+        else if(StrRangeEq(optionName, "PhysicalDeviceLimits"))

+        {

+            if(csvSplit.GetCount() >= 3)

+            {

+                const StrRange subOptionName = csvSplit.GetRange(1);

+                if(StrRangeEq(subOptionName, "maxMemoryAllocationCount"))

+                    SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_maxMemoryAllocationCount, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "bufferImageGranularity"))

+                    SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_bufferImageGranularity, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "nonCoherentAtomSize"))

+                    SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_nonCoherentAtomSize, csvSplit.GetRange(2));

+                else

+                    printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);

+            }

+            else

+                printf("Line %zu: Too few columns.\n", currLineNumber);

+        }

+        else if(StrRangeEq(optionName, "Extension"))

+        {

+            if(csvSplit.GetCount() >= 3)

+            {

+                const StrRange subOptionName = csvSplit.GetRange(1);

+                if(StrRangeEq(subOptionName, "VK_KHR_dedicated_allocation"))

+                    SetOption(currLineNumber, OPTION::Extension_VK_KHR_dedicated_allocation, csvSplit.GetRange(2));

+                else

+                    printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);

+            }

+            else

+                printf("Line %zu: Too few columns.\n", currLineNumber);

+        }

+        else if(StrRangeEq(optionName, "Macro"))

+        {

+            if(csvSplit.GetCount() >= 3)

+            {

+                const StrRange subOptionName = csvSplit.GetRange(1);

+                if(StrRangeEq(subOptionName, "VMA_DEBUG_ALWAYS_DEDICATED_MEMORY"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_ALWAYS_DEDICATED_MEMORY, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "VMA_DEBUG_ALIGNMENT"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_ALIGNMENT, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "VMA_DEBUG_MARGIN"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_MARGIN, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "VMA_DEBUG_INITIALIZE_ALLOCATIONS"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_INITIALIZE_ALLOCATIONS, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "VMA_DEBUG_DETECT_CORRUPTION"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_DETECT_CORRUPTION, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "VMA_DEBUG_GLOBAL_MUTEX"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_GLOBAL_MUTEX, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "VMA_SMALL_HEAP_MAX_SIZE"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_SMALL_HEAP_MAX_SIZE, csvSplit.GetRange(2));

+                else if(StrRangeEq(subOptionName, "VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE"))

+                    SetOption(currLineNumber, OPTION::Macro_VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE, csvSplit.GetRange(2));

+                else

+                    printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);

+            }

+            else

+                printf("Line %zu: Too few columns.\n", currLineNumber);

+        }

+        else if(StrRangeEq(optionName, "PhysicalDeviceMemory"))

+        {

+            uint32_t value = 0;

+            if(csvSplit.GetCount() == 3 && StrRangeEq(csvSplit.GetRange(1), "HeapCount") &&

+                StrRangeToUint(csvSplit.GetRange(2), value))

+            {

+                m_MemProps.memoryHeapCount = value;

+            }

+            else if(csvSplit.GetCount() == 3 && StrRangeEq(csvSplit.GetRange(1), "TypeCount") &&

+                StrRangeToUint(csvSplit.GetRange(2), value))

+            {

+                m_MemProps.memoryTypeCount = value;

+            }

+            else if(csvSplit.GetCount() == 5 && StrRangeEq(csvSplit.GetRange(1), "Heap") &&

+                StrRangeToUint(csvSplit.GetRange(2), value) &&

+                value < m_MemProps.memoryHeapCount)

+            {

+                if(StrRangeEq(csvSplit.GetRange(3), "size") &&

+                    StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryHeaps[value].size))

+                {

+                     // Parsed.

+                }

+                else if(StrRangeEq(csvSplit.GetRange(3), "flags") &&

+                    StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryHeaps[value].flags))

+                {

+                     // Parsed.

+                }

+                else

+                    printf("Line %zu: Invalid configuration option.\n", currLineNumber);

+            }

+            else if(csvSplit.GetCount() == 5 && StrRangeEq(csvSplit.GetRange(1), "Type") &&

+                StrRangeToUint(csvSplit.GetRange(2), value) &&

+                value < m_MemProps.memoryTypeCount)

+            {

+                if(StrRangeEq(csvSplit.GetRange(3), "heapIndex") &&

+                    StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryTypes[value].heapIndex))

+                {

+                     // Parsed.

+                }

+                else if(StrRangeEq(csvSplit.GetRange(3), "propertyFlags") &&

+                    StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryTypes[value].propertyFlags))

+                {

+                     // Parsed.

+                }

+                else

+                    printf("Line %zu: Invalid configuration option.\n", currLineNumber);

+            }

+            else

+                printf("Line %zu: Invalid configuration option.\n", currLineNumber);

+        }

+        else

+            printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);

+    }

+

+    return true;

+}

+

+void ConfigurationParser::Compare(

+    const VkPhysicalDeviceProperties& devProps,

+    const VkPhysicalDeviceMemoryProperties& memProps,

+    bool dedicatedAllocationExtensionEnabled)

+{

+    CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice apiVersion",

+        OPTION::PhysicalDevice_apiVersion, devProps.apiVersion);

+    CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice driverVersion",

+        OPTION::PhysicalDevice_driverVersion, devProps.driverVersion);

+    CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice vendorID",

+        OPTION::PhysicalDevice_vendorID, devProps.vendorID);

+    CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceID",

+        OPTION::PhysicalDevice_deviceID, devProps.deviceID);

+    CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceType",

+        OPTION::PhysicalDevice_deviceType, (uint32_t)devProps.deviceType);

+    CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceName",

+        OPTION::PhysicalDevice_deviceName, devProps.deviceName);

+

+    CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits maxMemoryAllocationCount",

+        OPTION::PhysicalDeviceLimits_maxMemoryAllocationCount, devProps.limits.maxMemoryAllocationCount);

+    CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits bufferImageGranularity",

+        OPTION::PhysicalDeviceLimits_bufferImageGranularity, devProps.limits.bufferImageGranularity);

+    CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits nonCoherentAtomSize",

+        OPTION::PhysicalDeviceLimits_nonCoherentAtomSize, devProps.limits.nonCoherentAtomSize);

+    CompareOption(VERBOSITY::DEFAULT, "Extension VK_KHR_dedicated_allocation",

+        OPTION::Extension_VK_KHR_dedicated_allocation, dedicatedAllocationExtensionEnabled);

+}

+

+void ConfigurationParser::SetOption(

+    size_t lineNumber,

+    OPTION option,

+    const StrRange& str)

+{

+    if(m_OptionSet[(size_t)option])

+    {

+        printf("Line %zu: Option already specified.\n" ,lineNumber);

+    }

+

+    m_OptionSet[(size_t)option] = true;

+

+    std::string val;

+    str.to_str(val);

+    m_OptionValue[(size_t)option] = std::move(val);

+}

+

+void ConfigurationParser::EnsureWarningHeader()

+{

+    if(!m_WarningHeaderPrinted)

+    {

+        printf("WARNING: Following configuration parameters don't match:\n");

+        m_WarningHeaderPrinted = true;

+    }

+}

+

+void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,

+    OPTION option, uint32_t currValue)

+{

+    if(m_OptionSet[(size_t)option] &&

+        g_Verbosity >= minVerbosity)

+    {

+        uint32_t origValue;

+        if(StrRangeToUint(StrRange(m_OptionValue[(size_t)option]), origValue))

+        {

+            if(origValue != currValue)

+            {

+                EnsureWarningHeader();

+                printf("    %s: original %u, current %u\n", name, origValue, currValue);

+            }

+        }

+    }

+}

+

+void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,

+    OPTION option, uint64_t currValue)

+{

+    if(m_OptionSet[(size_t)option] &&

+        g_Verbosity >= minVerbosity)

+    {

+        uint64_t origValue;

+        if(StrRangeToUint(StrRange(m_OptionValue[(size_t)option]), origValue))

+        {

+            if(origValue != currValue)

+            {

+                EnsureWarningHeader();

+                printf("    %s: original %llu, current %llu\n", name, origValue, currValue);

+            }

+        }

+    }

+}

+

+void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,

+    OPTION option, bool currValue)

+{

+    if(m_OptionSet[(size_t)option] &&

+        g_Verbosity >= minVerbosity)

+    {

+        bool origValue;

+        if(StrRangeToBool(StrRange(m_OptionValue[(size_t)option]), origValue))

+        {

+            if(origValue != currValue)

+            {

+                EnsureWarningHeader();

+                printf("    %s: original %u, current %u\n", name,

+                    origValue ? 1 : 0,

+                    currValue ? 1 : 0);

+            }

+        }

+    }

+}

+

+void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,

+    OPTION option, const char* currValue)

+{

+    if(m_OptionSet[(size_t)option] &&

+        g_Verbosity >= minVerbosity)

+    {

+        const std::string& origValue = m_OptionValue[(size_t)option];

+        if(origValue != currValue)

+        {

+            EnsureWarningHeader();

+            printf("    %s: original \"%s\", current \"%s\"\n", name, origValue.c_str(), currValue);

+        }

+    }

+}

+

+////////////////////////////////////////////////////////////////////////////////

 // class Player

 

 static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_LUNARG_standard_validation";

@@ -495,6 +870,7 @@
     int Init();

     ~Player();

 

+    void ApplyConfig(ConfigurationParser& configParser);

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

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

 

@@ -511,6 +887,8 @@
     uint32_t m_GraphicsQueueFamilyIndex = UINT_MAX;

     VkDevice m_Device = VK_NULL_HANDLE;

     VmaAllocator m_Allocator = VK_NULL_HANDLE;

+    bool m_DedicatedAllocationEnabled = false;

+    const VkPhysicalDeviceProperties* m_DevProps = nullptr;

     const VkPhysicalDeviceMemoryProperties* m_MemProps = nullptr;

 

     PFN_vkCreateDebugReportCallbackEXT m_pvkCreateDebugReportCallbackEXT;

@@ -618,6 +996,11 @@
         printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW);

 }

 

+void Player::ApplyConfig(ConfigurationParser& configParser)

+{

+    configParser.Compare(*m_DevProps, *m_MemProps, m_DedicatedAllocationEnabled);

+}

+

 void Player::ExecuteLine(size_t lineNumber, const StrRange& line)

 {

     CsvSplit csvSplit;

@@ -1046,16 +1429,15 @@
     const bool dedicatedAllocationAvailable =

         VK_KHR_get_memory_requirements2_available && VK_KHR_dedicated_allocation_available;

 

-    bool dedicatedAllocationEnabled = false;

     switch(g_VK_KHR_dedicated_allocation_request)

     {

     case VULKAN_EXTENSION_REQUEST::DISABLED:

         break;

     case VULKAN_EXTENSION_REQUEST::DEFAULT:

-        dedicatedAllocationEnabled = dedicatedAllocationAvailable;

+        m_DedicatedAllocationEnabled = dedicatedAllocationAvailable;

         break;

     case VULKAN_EXTENSION_REQUEST::ENABLED:

-        dedicatedAllocationEnabled = dedicatedAllocationAvailable;

+        m_DedicatedAllocationEnabled = dedicatedAllocationAvailable;

         if(!dedicatedAllocationAvailable)

         {

             printf("WARNING: VK_KHR_dedicated_allocation extension cannot be enabled.\n");

@@ -1064,7 +1446,7 @@
     default: assert(0);

     }

 

-    if(dedicatedAllocationEnabled)

+    if(m_DedicatedAllocationEnabled)

     {

         enabledDeviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);

         enabledDeviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);

@@ -1090,7 +1472,7 @@
     allocatorInfo.physicalDevice = m_PhysicalDevice;

     allocatorInfo.device = m_Device;

 

-    if(dedicatedAllocationEnabled)

+    if(m_DedicatedAllocationEnabled)

     {

         allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;

     }

@@ -1102,6 +1484,7 @@
         return RESULT_ERROR_VULKAN;

     }

 

+    vmaGetPhysicalDeviceProperties(m_Allocator, &m_DevProps);

     vmaGetMemoryProperties(m_Allocator, &m_MemProps);

 

     return 0;

@@ -2225,11 +2608,30 @@
 

     if(g_Verbosity == VERBOSITY::MAXIMUM)

     {

-        printf("Format version: %u,%u\n", g_FileVersion >> 16, g_FileVersion & 0xFFFF);

+        printf("Format version: %u,%u\n",

+            GetVersionMajor(g_FileVersion),

+            GetVersionMinor(g_FileVersion));

+    }

+

+    // Parse configuration

+    const bool configEnabled = g_FileVersion >= MakeVersion(1, 3);

+    ConfigurationParser configParser;

+    if(configEnabled)

+    {

+        if(!configParser.Parse(lineSplit))

+        {

+            return RESULT_ERROR_FORMAT;

+        }

     }

 

     Player player;

     int result = player.Init();

+

+    if(configEnabled)

+    {

+        player.ApplyConfig(configParser);

+    }

+

     size_t executedLineCount = 0;

     if(result == 0)

     {

diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h
index 479b4b4..a908c90 100644
--- a/src/vk_mem_alloc.h
+++ b/src/vk_mem_alloc.h
@@ -4799,6 +4799,10 @@
 public:

     VmaRecorder();

     VkResult Init(const VmaRecordSettings& settings, bool useMutex);

+    void WriteConfiguration(

+        const VkPhysicalDeviceProperties& devProps,

+        const VkPhysicalDeviceMemoryProperties& memProps,

+        bool dedicatedAllocationExtensionEnabled);

     ~VmaRecorder();

 

     void RecordCreateAllocator(uint32_t frameIndex);

@@ -8163,7 +8167,7 @@
 

     // Write header.

     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");

-    fprintf(m_File, "%s\n", "1,2");

+    fprintf(m_File, "%s\n", "1,3");

 

     return VK_SUCCESS;

 }

@@ -8541,6 +8545,52 @@
     }

 }

 

+void VmaRecorder::WriteConfiguration(

+    const VkPhysicalDeviceProperties& devProps,

+    const VkPhysicalDeviceMemoryProperties& memProps,

+    bool dedicatedAllocationExtensionEnabled)

+{

+    fprintf(m_File, "Config,Begin\n");

+

+    fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);

+    fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);

+    fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);

+    fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);

+    fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);

+    fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);

+

+    fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);

+    fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);

+    fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);

+

+    fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);

+    for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)

+    {

+        fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);

+        fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);

+    }

+    fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);

+    for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)

+    {

+        fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);

+        fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);

+    }

+

+    fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);

+

+    fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);

+    fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);

+    fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);

+    fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);

+    fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);

+    fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);

+    fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);

+    fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);

+    fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);

+

+    fprintf(m_File, "Config,End\n");

+}

+

 void VmaRecorder::GetBasicParams(CallParams& outParams)

 {

     outParams.threadId = GetCurrentThreadId();

@@ -8670,6 +8720,10 @@
         {

             return res;

         }

+        m_pRecorder->WriteConfiguration(

+            m_PhysicalDeviceProperties,

+            m_MemProps,

+            m_UseKhrDedicatedAllocation);

         m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());

 #else

         VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");