diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 09e38f7..6fc13b0 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -22,6 +22,7 @@ #include "VmaUsage.h" #include "Common.h" +#include static const int RESULT_EXCEPTION = -1000; static const int RESULT_ERROR_COMMAND_LINE = -1; @@ -36,6 +37,35 @@ struct StrRange size_t length() const { return end - beg; } }; +static inline bool StrRangeEq(const StrRange& lhs, const char* rhsSz) +{ + const size_t rhsLen = strlen(rhsSz); + return rhsLen == lhs.length() && + memcmp(lhs.beg, rhsSz, rhsLen) == 0; +} + +static inline bool StrRangeToUint(const StrRange& s, uint32_t& out) +{ + // TODO handle failure. + char* end = (char*)s.end; + out = (uint32_t)strtoul(s.beg, &end, 10); + return true; +} +static inline bool StrRangeToUint(const StrRange& s, uint64_t& out) +{ + // TODO handle failure. + char* end = (char*)s.end; + out = (uint64_t)strtoull(s.beg, &end, 10); + return true; +} +static inline bool StrRangeToPtr(const StrRange& s, uint64_t& out) +{ + // TODO handle failure. + char* end = (char*)s.end; + out = (uint64_t)strtoull(s.beg, &end, 16); + return true; +} + //////////////////////////////////////////////////////////////////////////////// // LineSplit class @@ -89,10 +119,11 @@ public: void Set(const StrRange& line, size_t maxCount = RANGE_COUNT_MAX); size_t GetCount() const { return m_Count; } - void GetRange(size_t index, const char*& outBeg, const char*& outEnd) const + StrRange GetRange(size_t index) const { - outBeg = m_Str + m_Ranges[index * 2]; - outEnd = m_Str + m_Ranges[index * 2 + 1]; + return StrRange { + m_Str + m_Ranges[index * 2], + m_Str + m_Ranges[index * 2 + 1] }; } private: @@ -175,6 +206,8 @@ static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, }) != propsEnd; } +static const size_t FIRST_PARAM_INDEX = 4; + class Player { public: @@ -185,10 +218,7 @@ public: void ExecuteLine(size_t lineNumber, const StrRange& line); private: - static const size_t MAX_WARNINGS_TO_SHOW = 16; - - // Increments warning counter. Returns true if warning message should be printed. - bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; } + static const size_t MAX_WARNINGS_TO_SHOW = 64; size_t m_WarningCount = 0; @@ -203,9 +233,44 @@ private: PFN_vkDestroyDebugReportCallbackEXT m_pvkDestroyDebugReportCallbackEXT; VkDebugReportCallbackEXT m_hCallback; + uint32_t m_VmaFrameIndex = 0; + + // Any of these handles null means it was created in original but couldn't be created now. + struct Pool + { + VmaPool pool; + }; + struct Buffer + { + VkBuffer buffer; + VmaAllocation allocation; + }; + struct Image + { + VkImage image; + VmaAllocation allocation; + }; + + std::unordered_map m_Pools; + std::unordered_map m_Buffers; + std::unordered_map m_Images; + + // Increments warning counter. Returns true if warning message should be printed. + bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; } + void InitVulkan(); void FinalizeVulkan(); void RegisterDebugCallbacks(); + + // If parmeter count doesn't match, issues warning and returns false. + bool ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound); + + void CreatePool(size_t lineNumber, const CsvSplit& csvSplit); + void DestroyPool(size_t lineNumber, const CsvSplit& csvSplit); + void CreateBuffer(size_t lineNumber, const CsvSplit& csvSplit); + void DestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit); + void CreateImage(size_t lineNumber, const CsvSplit& csvSplit); + void DestroyImage(size_t lineNumber, const CsvSplit& csvSplit); }; Player::Player() @@ -221,7 +286,7 @@ Player::~Player() { FinalizeVulkan(); - if(m_WarningCount > 0) + if(m_WarningCount > MAX_WARNINGS_TO_SHOW) printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW); } @@ -230,9 +295,58 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) CsvSplit csvSplit; csvSplit.Set(line); - if(csvSplit.GetCount() >= 4) + if(csvSplit.GetCount() >= FIRST_PARAM_INDEX) { - // TODO + // Update VMA current frame index. + StrRange frameIndexStr = csvSplit.GetRange(2); + uint32_t frameIndex; + if(StrRangeToUint(frameIndexStr, frameIndex)) + { + if(frameIndex != m_VmaFrameIndex) + { + vmaSetCurrentFrameIndex(m_Allocator, frameIndex); + m_VmaFrameIndex = frameIndex; + } + } + else + { + if(IssueWarning()) + printf("Line %zu: Incorrect frame index.\n", lineNumber); + } + + StrRange functionName = csvSplit.GetRange(3); + + if(StrRangeEq(functionName, "vmaCreateAllocator")) + { + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false)) + { + // Nothing to do. + } + } + else if(StrRangeEq(functionName, "vmaCreatePool")) + CreatePool(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaDestroyPool")) + DestroyPool(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaSetAllocationUserData")) + { + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true)) + { + // Ignore for now. + } + } + else if(StrRangeEq(functionName, "vmaCreateBuffer")) + CreateBuffer(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaDestroyBuffer")) + DestroyBuffer(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaCreateImage")) + CreateImage(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaDestroyImage")) + DestroyImage(lineNumber, csvSplit); + else + { + if(IssueWarning()) + printf("Line %zu: Unknown function.\n", lineNumber); + } } else { @@ -289,6 +403,8 @@ void Player::InitVulkan() ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, NULL, &m_VulkanInstance) ); + RegisterDebugCallbacks(); + // Find physical device uint32_t deviceCount = 0; @@ -340,7 +456,7 @@ void Player::InitVulkan() // Determine list of device extensions to enable. std::vector enabledDeviceExtensions; - enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + //enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); { uint32_t propertyCount = 0; ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, nullptr) ); @@ -391,6 +507,36 @@ void Player::InitVulkan() void Player::FinalizeVulkan() { + if(!m_Images.empty()) + { + printf("WARNING: Images not destroyed: %zu.\n", m_Images.size()); + + for(const auto it : m_Images) + { + vmaDestroyImage(m_Allocator, it.second.image, it.second.allocation); + } + } + + if(!m_Buffers.empty()) + { + printf("WARNING: Buffers not destroyed: %zu.\n", m_Buffers.size()); + + for(const auto it : m_Buffers) + { + vmaDestroyBuffer(m_Allocator, it.second.buffer, it.second.allocation); + } + } + + if(!m_Pools.empty()) + { + printf("WARNING: Pools not destroyed: %zu.\n", m_Pools.size()); + + for(const auto it : m_Pools) + { + vmaDestroyPool(m_Allocator, it.second.pool); + } + } + vkDeviceWaitIdle(m_Device); if(m_Allocator != VK_NULL_HANDLE) @@ -433,20 +579,287 @@ void Player::RegisterDebugCallbacks() assert(m_pvkDebugReportMessageEXT); assert(m_pvkDestroyDebugReportCallbackEXT); - VkDebugReportCallbackCreateInfoEXT callbackCreateInfo; - callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - callbackCreateInfo.pNext = nullptr; + VkDebugReportCallbackCreateInfoEXT callbackCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; callbackCreateInfo.flags = //VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT /*| VK_DEBUG_REPORT_DEBUG_BIT_EXT*/; callbackCreateInfo.pfnCallback = &MyDebugReportCallback; - callbackCreateInfo.pUserData = nullptr; ERR_GUARD_VULKAN( m_pvkCreateDebugReportCallbackEXT(m_VulkanInstance, &callbackCreateInfo, nullptr, &m_hCallback) ); } +bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound) +{ + bool ok; + if(lastUnbound) + ok = csvSplit.GetCount() >= FIRST_PARAM_INDEX + expectedParamCount - 1; + else + ok = csvSplit.GetCount() == FIRST_PARAM_INDEX + expectedParamCount; + + if(!ok) + { + if(IssueWarning()) + printf("Line %zu: Incorrect number of function parameters.\n", lineNumber); + } + + return ok; +} + +void Player::CreatePool(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 7, false)) + { + VmaPoolCreateInfo poolCreateInfo = {}; + uint64_t origPtr = 0; + + if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), poolCreateInfo.memoryTypeIndex) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), poolCreateInfo.flags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), poolCreateInfo.blockSize) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), poolCreateInfo.minBlockCount) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), poolCreateInfo.maxBlockCount) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), poolCreateInfo.frameInUseCount) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), origPtr)) + { + Pool poolDesc = {}; + VkResult res = vmaCreatePool(m_Allocator, &poolCreateInfo, &poolDesc.pool); + if(res == VK_SUCCESS) + { + const auto existingIt = m_Pools.find(origPtr); + if(existingIt != m_Pools.end()) + { + if(IssueWarning()) + printf("Line %zu: Pool %llX already exists.\n", lineNumber, origPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: vmaCreatePool failed (%u).\n", lineNumber, res); + } + m_Pools[origPtr] = poolDesc; + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaCreatePool.\n", lineNumber); + } + } +} + +void Player::DestroyPool(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) + { + uint64_t origPtr = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr)) + { + const auto it = m_Pools.find(origPtr); + if(it != m_Pools.end()) + { + vmaDestroyPool(m_Allocator, it->second.pool); + m_Pools.erase(it); + } + else + { + if(IssueWarning()) + printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaDestroyPool.\n", lineNumber); + } + } +} + +void Player::CreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 12, true)) + { + VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + VmaAllocationCreateInfo allocCreateInfo = {}; + uint64_t origPool = 0; + uint64_t origPtr = 0; + + if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), bufCreateInfo.flags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), bufCreateInfo.size) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), bufCreateInfo.usage) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), (uint32_t&)bufCreateInfo.sharingMode) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), allocCreateInfo.flags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), (uint32_t&)allocCreateInfo.usage) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.requiredFlags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.preferredFlags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), allocCreateInfo.memoryTypeBits) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPool) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), origPtr)) + { + if(origPool != 0) + { + const auto poolIt = m_Pools.find(origPool); + if(poolIt != m_Pools.end()) + allocCreateInfo.pool = poolIt->second.pool; + else + { + if(IssueWarning()) + printf("Line %zu: Pool %llX not found.\n", lineNumber, origPool); + } + } + + Buffer bufDesc = {}; + VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &bufDesc.buffer, &bufDesc.allocation, nullptr); + if(res == VK_SUCCESS) + { + const auto existingIt = m_Buffers.find(origPtr); + if(existingIt != m_Buffers.end()) + { + if(IssueWarning()) + printf("Line %zu: Allocation %llX for buffer already exists.\n", lineNumber, origPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: vmaCreateBuffer failed (%u).\n", lineNumber, res); + } + m_Buffers[origPtr] = bufDesc; + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaCreateBuffer.\n", lineNumber); + } + } +} + +void Player::DestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) + { + uint64_t origAllocPtr = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtr)) + { + const auto it = m_Buffers.find(origAllocPtr); + if(it != m_Buffers.end()) + { + vmaDestroyBuffer(m_Allocator, it->second.buffer, it->second.allocation); + m_Buffers.erase(it); + } + else + { + if(IssueWarning()) + printf("Line %zu: Allocation %llX for buffer not found.\n", lineNumber, origAllocPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaDestroyBuffer.\n", lineNumber); + } + } +} + +void Player::CreateImage(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 21, true)) + { + VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + VmaAllocationCreateInfo allocCreateInfo = {}; + uint64_t origPool = 0; + uint64_t origPtr = 0; + + if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), imageCreateInfo.flags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), (uint32_t&)imageCreateInfo.imageType) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), (uint32_t&)imageCreateInfo.format) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), imageCreateInfo.extent.width) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), imageCreateInfo.extent.height) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), imageCreateInfo.extent.depth) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), imageCreateInfo.mipLevels) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), imageCreateInfo.arrayLayers) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), (uint32_t&)imageCreateInfo.samples) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), (uint32_t&)imageCreateInfo.tiling) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), imageCreateInfo.usage) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 11), (uint32_t&)imageCreateInfo.sharingMode) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 12), (uint32_t&)imageCreateInfo.initialLayout) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 13), allocCreateInfo.flags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 14), (uint32_t&)allocCreateInfo.usage) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 15), allocCreateInfo.requiredFlags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 16), allocCreateInfo.preferredFlags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 17), allocCreateInfo.memoryTypeBits) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 18), origPool) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 19), origPtr)) + { + if(origPool != 0) + { + const auto poolIt = m_Pools.find(origPool); + if(poolIt != m_Pools.end()) + allocCreateInfo.pool = poolIt->second.pool; + else + { + if(IssueWarning()) + printf("Line %zu: Pool %llX not found.\n", lineNumber, origPool); + } + } + + Image imageDesc = {}; + VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &imageDesc.image, &imageDesc.allocation, nullptr); + if(res == VK_SUCCESS) + { + const auto existingIt = m_Images.find(origPtr); + if(existingIt != m_Images.end()) + { + if(IssueWarning()) + printf("Line %zu: Allocation %llX for image already exists.\n", lineNumber, origPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: vmaCreateImage failed (%u).\n", lineNumber, res); + } + m_Images[origPtr] = imageDesc; + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaCreateImage.\n", lineNumber); + } + } +} + +void Player::DestroyImage(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) + { + uint64_t origAllocPtr = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtr)) + { + const auto it = m_Images.find(origAllocPtr); + if(it != m_Images.end()) + { + vmaDestroyImage(m_Allocator, it->second.image, it->second.allocation); + m_Images.erase(it); + } + else + { + if(IssueWarning()) + printf("Line %zu: Allocation %llX for image not found.\n", lineNumber, origAllocPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaDestroyBuffer.\n", lineNumber); + } + } +} + + //////////////////////////////////////////////////////////////////////////////// // Main functions @@ -456,13 +869,6 @@ static void PrintCommandLineSyntax() " VmaReplay \n"); } -static inline bool StrRangeEq(const StrRange& lhs, const char* rhsSz) -{ - const size_t rhsLen = strlen(rhsSz); - return rhsLen == lhs.length() && - memcmp(lhs.beg, rhsSz, rhsLen) == 0; -} - static int ProcessFile(const char* data, size_t numBytes) { // Begin stats. @@ -488,12 +894,14 @@ static int ProcessFile(const char* data, size_t numBytes) Player player; player.Init(); + printf("Playing...\n"); while(lineSplit.GetNextLine(line)) { player.ExecuteLine(lineSplit.GetNextLineIndex(), line); } // End stats. + printf("Done.\n"); printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); return 0; @@ -532,8 +940,6 @@ static int ProcessFile(const char* filePath) result = RESULT_ERROR_SOURCE_FILE; } - printf("Done.\n"); - return result; }