diff --git a/docs/Recording file format.md b/docs/Recording file format.md index 6ee8b37..8fb17ea 100644 --- a/docs/Recording file format.md +++ b/docs/Recording file format.md @@ -209,6 +209,22 @@ No parameters. - allocation : pointer - newSize : uint64 +**vmaDefragmentationBegin** (min format version: 1.5) + +- defragmentationInfo2.flags : uint32 +- defragmentationInfo2.pAllocations : list of pointers +- defragmentationInfo2.pPools : list of pointers +- defragmentationInfo2.maxCpuBytesToMove : uint64 +- defragmentationInfo2.maxCpuAllocationsToMove : uint32 +- defragmentationInfo2.maxGpuBytesToMove : uint64 +- defragmentationInfo2.maxGpuAllocationsToMove : uint32 +- defragmentationInfo2.commandBuffer : pointer +- context (output) : pointer + +**vmaDefragmentationEnd** (min format version: 1.5) + +- context : pointer + # Data types **bool** diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 6b3615d..d555b2d 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -85,6 +85,8 @@ enum class VMA_FUNCTION GetAllocationInfo, MakePoolAllocationsLost, ResizeAllocation, + DefragmentationBegin, + DefragmentationEnd, Count }; static const char* VMA_FUNCTION_NAMES[] = { @@ -108,6 +110,8 @@ static const char* VMA_FUNCTION_NAMES[] = { "vmaGetAllocationInfo", "vmaMakePoolAllocationsLost", "vmaResizeAllocation", + "vmaDefragmentationBegin", + "vmaDefragmentationEnd", }; static_assert( _countof(VMA_FUNCTION_NAMES) == (size_t)VMA_FUNCTION::Count, @@ -839,7 +843,7 @@ void ConfigurationParser::CompareMemProps( static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_LUNARG_standard_validation"; -static bool g_MemoryAliasingWarningEnabled = true; +static const bool g_MemoryAliasingWarningEnabled = false; static VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback( VkDebugReportFlagsEXT flags, @@ -973,6 +977,7 @@ private: }; std::unordered_map m_Pools; std::unordered_map m_Allocations; + std::unordered_map m_DefragmentationContexts; struct Thread { @@ -1029,6 +1034,8 @@ private: void ExecuteGetAllocationInfo(size_t lineNumber, const CsvSplit& csvSplit); void ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit); void ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteDefragmentationBegin(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteDefragmentationEnd(size_t lineNumber, const CsvSplit& csvSplit); void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit, const char* functionName); @@ -1175,6 +1182,10 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) ExecuteMakePoolAllocationsLost(lineNumber, csvSplit); else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::ResizeAllocation])) ExecuteResizeAllocation(lineNumber, csvSplit); + else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DefragmentationBegin])) + ExecuteDefragmentationBegin(lineNumber, csvSplit); + else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DefragmentationEnd])) + ExecuteDefragmentationEnd(lineNumber, csvSplit); else { if(IssueWarning()) @@ -1611,6 +1622,21 @@ int Player::InitVulkan() void Player::FinalizeVulkan() { + if(!m_DefragmentationContexts.empty()) + { + printf("WARNING: Defragmentation contexts not destroyed: %zu.\n", m_DefragmentationContexts.size()); + + if(CLEANUP_LEAKED_OBJECTS) + { + for(const auto& it : m_DefragmentationContexts) + { + vmaDefragmentationEnd(m_Allocator, it.second); + } + } + + m_DefragmentationContexts.clear(); + } + if(!m_Allocations.empty()) { printf("WARNING: Allocations not destroyed: %zu.\n", m_Allocations.size()); @@ -1744,8 +1770,6 @@ void Player::Defragment() return; } - g_MemoryAliasingWarningEnabled = false; - const time_point timeBeg = std::chrono::high_resolution_clock::now(); VmaDefragmentationInfo2 defragInfo = {}; @@ -1835,8 +1859,6 @@ void Player::Defragment() } vkResetCommandPool(m_Device, m_CommandPool, 0); - - g_MemoryAliasingWarningEnabled = true; } void Player::PrintStats() @@ -2894,6 +2916,198 @@ void Player::ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit } } +void Player::ExecuteDefragmentationBegin(size_t lineNumber, const CsvSplit& csvSplit) +{ + m_Stats.RegisterFunctionCall(VMA_FUNCTION::DefragmentationBegin); + + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 9, false)) + { + VmaDefragmentationInfo2 defragInfo = {}; + std::vector allocationOrigPtrs; + std::vector poolOrigPtrs; + uint64_t cmdBufOrigPtr = 0; + uint64_t defragCtxOrigPtr = 0; + + if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), defragInfo.flags) && + StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), allocationOrigPtrs) && + StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), poolOrigPtrs) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), defragInfo.maxCpuBytesToMove) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), defragInfo.maxCpuAllocationsToMove) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), defragInfo.maxGpuBytesToMove) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), defragInfo.maxGpuAllocationsToMove) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), cmdBufOrigPtr) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), defragCtxOrigPtr)) + { + const size_t allocationOrigPtrCount = allocationOrigPtrs.size(); + std::vector allocations; + allocations.reserve(allocationOrigPtrCount); + for(size_t i = 0; i < allocationOrigPtrCount; ++i) + { + const auto it = m_Allocations.find(allocationOrigPtrs[i]); + if(it != m_Allocations.end() && it->second.allocation) + { + allocations.push_back(it->second.allocation); + } + } + if(!allocations.empty()) + { + defragInfo.allocationCount = (uint32_t)allocations.size(); + defragInfo.pAllocations = allocations.data(); + } + + const size_t poolOrigPtrCount = poolOrigPtrs.size(); + std::vector pools; + pools.reserve(poolOrigPtrCount); + for(size_t i = 0; i < poolOrigPtrCount; ++i) + { + const auto it = m_Pools.find(poolOrigPtrs[i]); + if(it != m_Pools.end() && it->second.pool) + { + pools.push_back(it->second.pool); + } + } + if(!pools.empty()) + { + defragInfo.poolCount = (uint32_t)pools.size(); + defragInfo.pPools = pools.data(); + } + + if(allocations.size() != allocationOrigPtrCount || + pools.size() != poolOrigPtrCount) + { + if(IssueWarning()) + { + printf("Line %zu: Passing %zu allocations and %zu pools to vmaDefragmentationBegin, while originally %zu allocations and %zu pools were passed.\n", + lineNumber, + allocations.size(), pools.size(), + allocationOrigPtrCount, poolOrigPtrCount); + } + } + + if(cmdBufOrigPtr) + { + VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VkResult res = vkBeginCommandBuffer(m_CommandBuffer, &cmdBufBeginInfo); + if(res == VK_SUCCESS) + { + defragInfo.commandBuffer = m_CommandBuffer; + } + else + { + printf("Line %zu: vkBeginCommandBuffer failed (%d)\n", lineNumber, res); + } + } + + VmaDefragmentationContext defragCtx = nullptr; + VkResult res = vmaDefragmentationBegin(m_Allocator, &defragInfo, nullptr, &defragCtx); + + if(defragInfo.commandBuffer) + { + vkEndCommandBuffer(m_CommandBuffer); + + VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &m_CommandBuffer; + vkQueueSubmit(m_TransferQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(m_TransferQueue); + } + + if(res >= VK_SUCCESS) + { + if(defragCtx) + { + if(defragCtxOrigPtr) + { + // We have defragmentation context, originally had defragmentation context: Store it. + m_DefragmentationContexts[defragCtxOrigPtr] = defragCtx; + } + else + { + // We have defragmentation context, originally it was null: End immediately. + vmaDefragmentationEnd(m_Allocator, defragCtx); + } + } + else + { + if(defragCtxOrigPtr) + { + // We have no defragmentation context, originally there was one: Store null. + m_DefragmentationContexts[defragCtxOrigPtr] = nullptr; + } + else + { + // We have no defragmentation context, originally there wasn't as well - nothing to do. + } + } + } + else + { + if(defragCtxOrigPtr) + { + // Currently failed, originally succeeded. + if(IssueWarning()) + { + printf("Line %zu: vmaDefragmentationBegin failed (%d), while originally succeeded.\n", lineNumber, res); + } + } + else + { + // Currently failed, originally don't know. + if(IssueWarning()) + { + printf("Line %zu: vmaDefragmentationBegin failed (%d).\n", lineNumber, res); + } + } + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Invalid parameters for vmaDefragmentationBegin.\n", lineNumber); + } + } + } +} + +void Player::ExecuteDefragmentationEnd(size_t lineNumber, const CsvSplit& csvSplit) +{ + m_Stats.RegisterFunctionCall(VMA_FUNCTION::DefragmentationEnd); + + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) + { + uint64_t origPtr = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr)) + { + if(origPtr != 0) + { + const auto it = m_DefragmentationContexts.find(origPtr); + if(it != m_DefragmentationContexts.end()) + { + vmaDefragmentationEnd(m_Allocator, it->second); + m_DefragmentationContexts.erase(it); + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Defragmentation context %llX not found.\n", lineNumber, origPtr); + } + } + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Invalid parameters for vmaDefragmentationEnd.\n", lineNumber); + } + } + } +} + //////////////////////////////////////////////////////////////////////////////// // Main functions diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 5a69ef2..f4b6383 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -6370,6 +6370,11 @@ public: VmaAllocation allocation); void RecordMakePoolAllocationsLost(uint32_t frameIndex, VmaPool pool); + void RecordDefragmentationBegin(uint32_t frameIndex, + const VmaDefragmentationInfo2& info, + VmaDefragmentationContext ctx); + void RecordDefragmentationEnd(uint32_t frameIndex, + VmaDefragmentationContext ctx); private: struct CallParams @@ -6397,7 +6402,21 @@ private: int64_t m_StartCounter; void GetBasicParams(CallParams& outParams); - void PrintPointerList(uint64_t count, const VmaAllocation* pItems); + + // T must be a pointer type, e.g. VmaAllocation, VmaPool. + template + void PrintPointerList(uint64_t count, const T* pItems) + { + if(count) + { + fprintf(m_File, "%p", pItems[0]); + for(uint64_t i = 1; i < count; ++i) + { + fprintf(m_File, " %p", pItems[i]); + } + } + } + void Flush(); }; @@ -13608,6 +13627,41 @@ void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex, Flush(); } +void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex, + const VmaDefragmentationInfo2& info, + VmaDefragmentationContext ctx) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex, + info.flags); + PrintPointerList(info.allocationCount, info.pAllocations); + fprintf(m_File, ","); + PrintPointerList(info.poolCount, info.pPools); + fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n", + info.maxCpuBytesToMove, + info.maxCpuAllocationsToMove, + info.maxGpuBytesToMove, + info.maxGpuAllocationsToMove, + info.commandBuffer, + ctx); + Flush(); +} + +void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex, + VmaDefragmentationContext ctx) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex, + ctx); + Flush(); +} + VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData) { if(pUserData != VMA_NULL) @@ -13683,18 +13737,6 @@ void VmaRecorder::GetBasicParams(CallParams& outParams) outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq; } -void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems) -{ - if(count) - { - fprintf(m_File, "%p", pItems[0]); - for(uint64_t i = 1; i < count; ++i) - { - fprintf(m_File, " %p", pItems[i]); - } - } -} - void VmaRecorder::Flush() { if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0) @@ -15987,7 +16029,17 @@ VkResult vmaDefragmentationBegin( VMA_DEBUG_GLOBAL_MUTEX_LOCK - return allocator->DefragmentationBegin(*pInfo, pStats, pContext); + VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordDefragmentationBegin( + allocator->GetCurrentFrameIndex(), *pInfo, *pContext); + } +#endif + + return res; } VkResult vmaDefragmentationEnd( @@ -16002,6 +16054,14 @@ VkResult vmaDefragmentationEnd( { VMA_DEBUG_GLOBAL_MUTEX_LOCK +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordDefragmentationEnd( + allocator->GetCurrentFrameIndex(), context); + } +#endif + return allocator->DefragmentationEnd(context); } else