diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 8dd0cb4a9..c4d5676e4 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -2696,6 +2696,65 @@ void WrappedVulkan::ApplyInitialContents() SubmitCmds(); FlushQ(); SubmitAndFlushImageStateBarriers(m_cleanupImageBarriers); + + // reset any queries to a valid copy-able state if they need to be copied. + if(!m_ResetQueries.empty()) + { + // sort all pools together + std::sort(m_ResetQueries.begin(), m_ResetQueries.end(), + [](const ResetQuery &a, const ResetQuery &b) { return a.pool < b.pool; }); + + cmd = GetNextCmd(); + + vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + uint32_t i = 0; + for(const ResetQuery &r : m_ResetQueries) + { + ObjDisp(cmd)->CmdResetQueryPool(Unwrap(cmd), Unwrap(r.pool), r.firstQuery, r.queryCount); + + for(uint32_t q = 0; q < r.queryCount; q++) + { + // Timestamps are easy - we can do these without needing to render + if(m_CreationInfo.m_QueryPool[GetResID(r.pool)].queryType == VK_QUERY_TYPE_TIMESTAMP) + { + ObjDisp(cmd)->CmdWriteTimestamp(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + Unwrap(r.pool), r.firstQuery + q); + } + else + { + ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), Unwrap(r.pool), r.firstQuery + q, 0); + ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), Unwrap(r.pool), r.firstQuery + q); + } + + i++; + + // split the command buffer and flush if the number of queries is massive + if(i > 0 && (i % (128 * 1024)) == 0) + { + vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + SubmitCmds(); + FlushQ(); + + cmd = GetNextCmd(); + + vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + } + } + } + + vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + m_ResetQueries.clear(); + + SubmitCmds(); + FlushQ(); + } } bool WrappedVulkan::ContextProcessChunk(ReadSerialiser &ser, VulkanChunk chunk) diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index f73cc1d0f..a3a47d2bc 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -551,6 +551,19 @@ private: rdcarray m_CleanupEvents; rdcarray m_PersistentEvents; + // reset queries must be restored to a valid state on next replay to ensure any query pool + // results copies that refer to previous frames have valid data. We initialised all of the + // query pool at creation time, but any queries that are reset might not be recorded within + // the frame - certainly not if we're doing partial replays. So we record all queries that are + // reset to re-initialise them. + struct ResetQuery + { + VkQueryPool pool; + uint32_t firstQuery; + uint32_t queryCount; + }; + rdcarray m_ResetQueries; + const VkFormatProperties &GetFormatProperties(VkFormat f); struct BakedCmdBufferInfo diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index 776d67d2b..5e4bfaadc 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -1264,6 +1264,14 @@ void VulkanCreationInfo::ShaderModuleReflection::PopulateDisassembly(const rdcsp disassembly = spirv.Disassemble(refl.entryPoint.c_str(), instructionLines); } +void VulkanCreationInfo::QueryPool::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, + const VkQueryPoolCreateInfo *pCreateInfo) +{ + queryType = pCreateInfo->queryType; + queryCount = pCreateInfo->queryCount; + pipelineStatistics = pCreateInfo->pipelineStatistics; +} + void VulkanCreationInfo::DescSetPool::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, const VkDescriptorPoolCreateInfo *pCreateInfo) diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index d9a08f03f..6fcd563eb 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -609,6 +609,17 @@ struct VulkanCreationInfo }; std::map m_DescSetPool; + struct QueryPool + { + void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, + const VkQueryPoolCreateInfo *pCreateInfo); + + VkQueryType queryType; + uint32_t queryCount; + VkQueryPipelineStatisticFlags pipelineStatistics; + }; + std::map m_QueryPool; + std::map m_Names; std::map m_SwapChain; std::map m_DescSetLayout; @@ -619,6 +630,7 @@ struct VulkanCreationInfo void erase(ResourceId id) { + m_QueryPool.erase(id); m_Pipeline.erase(id); m_PipelineLayout.erase(id); m_RenderPass.erase(id); diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index e2b820f8d..a7276db11 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -3781,6 +3781,8 @@ bool WrappedVulkan::Serialise_vkCmdResetQueryPool(SerialiserType &ser, VkCommand { ObjDisp(commandBuffer) ->CmdResetQueryPool(Unwrap(commandBuffer), Unwrap(queryPool), firstQuery, queryCount); + + m_ResetQueries.push_back({queryPool, firstQuery, queryCount}); } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index 445da6d33..29fb328f9 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -1375,6 +1375,8 @@ bool WrappedVulkan::Serialise_vkCreateQueryPool(SerialiserType &ser, VkDevice de ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), pool); GetResourceManager()->AddLiveResource(QueryPool, pool); + m_CreationInfo.m_QueryPool[live].Init(GetResourceManager(), m_CreationInfo, &CreateInfo); + // We fill the query pool with valid but empty data, just so that future copies of query // results don't read from invalid data. diff --git a/util/test/demos/vk/vk_query_pool.cpp b/util/test/demos/vk/vk_query_pool.cpp index e5f052d13..295623d58 100644 --- a/util/test/demos/vk/vk_query_pool.cpp +++ b/util/test/demos/vk/vk_query_pool.cpp @@ -61,10 +61,9 @@ RD_TEST(VK_Query_Pool, VulkanGraphicsTest) VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo); - AllocatedBuffer vb( - this, vkh::BufferCreateInfo(sizeof(DefaultTri), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT), - VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + AllocatedBuffer vb(this, vkh::BufferCreateInfo(4096, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); vb.upload(DefaultTri); @@ -78,12 +77,44 @@ RD_TEST(VK_Query_Pool, VulkanGraphicsTest) VkQueryPool pool; vkCreateQueryPool(device, &poolInfo, NULL, &pool); + // populate the query we'll read the first frame + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + vkCmdResetQueryPool(cmd, pool, 100, 4); + + vkCmdBeginQuery(cmd, pool, 100, 0); + vkCmdEndQuery(cmd, pool, 100); + + vkEndCommandBuffer(cmd); + + Submit(99, 99, {cmd}); + + vkQueueWaitIdle(queue); + } + while(Running()) { VkCommandBuffer cmd = GetCommandBuffer(); vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + // use a query from a previous frame then immediately reset it + uint32_t prevQueryIdx = (curFrame % 4); + + vkCmdCopyQueryPoolResults(cmd, pool, 100 + prevQueryIdx, 1, vb.buffer, + 1024 + sizeof(uint64_t) * prevQueryIdx, sizeof(uint64_t), + VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + vkCmdResetQueryPool(cmd, pool, 100 + prevQueryIdx, 1); + + uint32_t curQueryIdx = ((curFrame + 1) % 4); + + // fill the next query + vkCmdBeginQuery(cmd, pool, 100 + curQueryIdx, 0); + vkCmdEndQuery(cmd, pool, 100 + curQueryIdx); + vkCmdResetQueryPool(cmd, pool, 123456, 1); VkImage swapimg =