From 3d90609e398391c1c3c28e8ff71ee27b452e38a7 Mon Sep 17 00:00:00 2001 From: Benson Joeris Date: Tue, 17 Dec 2019 12:03:16 -0500 Subject: [PATCH] Add ImageBarrierSequence `ImageBarrierSequence` represents a sequence of batches of `VkImageMemoryBarrier`s. The intended use case is setting image state, which may involve a combination of layout transitions and queue family transfers, and may require multiple barriers executed on different queue families, in the correct order. It is moderately expensive to execute a sequence of separate queue submissions, with synchronization to prevent overlapping execution. `ImageBarrierSequence` attempts to address this in two ways: - `ImageBarrierSequence::Merge` allows separate barrier sequences (e.g. on separate images) to be merged, so that, e.g. the first batches from each of the barrier sequences are all run together. - `InlineSetupImageBarriers`/`InlineCleanupImageBarriers` allow barriers to be inlined into an existing command buffer, when possible. E.g. to read the contents of an image, the barrier sequence may simply have a layout transition to `TRANSFER_SRC_OPTIMAL` (assuming the image is already on the correct queue family), and this can be inlined into the command buffer before executing the copy, avoiding the need for an extra queue submission. Change-Id: I7f2edc650d9ff66871c9be5711789bfe33ca8c5e --- renderdoc/driver/vulkan/CMakeLists.txt | 1 + .../driver/vulkan/renderdoc_vulkan.vcxproj | 1 + .../vulkan/renderdoc_vulkan.vcxproj.filters | 3 + renderdoc/driver/vulkan/vk_core.cpp | 116 +++++++++++++++ renderdoc/driver/vulkan/vk_core.h | 4 + renderdoc/driver/vulkan/vk_image_states.cpp | 134 ++++++++++++++++++ renderdoc/driver/vulkan/vk_resources.h | 24 ++++ 7 files changed, 283 insertions(+) create mode 100644 renderdoc/driver/vulkan/vk_image_states.cpp diff --git a/renderdoc/driver/vulkan/CMakeLists.txt b/renderdoc/driver/vulkan/CMakeLists.txt index 138fb9d59..00c01d529 100644 --- a/renderdoc/driver/vulkan/CMakeLists.txt +++ b/renderdoc/driver/vulkan/CMakeLists.txt @@ -22,6 +22,7 @@ set(sources vk_dispatchtables.h vk_dispatch_defs.h vk_hookset_defs.h + vk_image_states.cpp vk_info.cpp vk_info.h vk_initstate.cpp diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj index 078b7f00e..d09ab8a5e 100644 --- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj +++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj @@ -108,6 +108,7 @@ true + diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters index 20a2021d6..0850c0f75 100644 --- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters +++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters @@ -145,6 +145,9 @@ Replay + + Util + diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 625bba39b..1b18324b2 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -488,6 +488,122 @@ void WrappedVulkan::SubmitAndFlushExtQueue(uint32_t queueFamilyIdx) const ObjDisp(q)->QueueWaitIdle(Unwrap(q)); } +void WrappedVulkan::SubmitAndFlushImageStateBarriers(ImageBarrierSequence &barriers) +{ + if(barriers.empty()) + return; + + VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; + VkFence queueFamilyFences[ImageBarrierSequence::MAX_QUEUE_FAMILY_COUNT] = { + VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE}; + rdcarray submittedFences; + rdcarray batch; + VkResult vkr; + for(uint32_t batchIndex = 0; batchIndex < ImageBarrierSequence::MAX_BATCH_COUNT; ++batchIndex) + { + for(uint32_t queueFamilyIndex = 0; + queueFamilyIndex < ImageBarrierSequence::MAX_QUEUE_FAMILY_COUNT; ++queueFamilyIndex) + { + barriers.ExtractUnwrappedBatch(batchIndex, queueFamilyIndex, batch); + if(batch.empty()) + continue; + + VkCommandBuffer cmd = GetExtQueueCmd(queueFamilyIndex); + VkQueue queue = m_ExternalQueues[queueFamilyIndex].queue; + + VkCommandBuffer unwrappedCmd = Unwrap(cmd); + + VkSubmitInfo submitInfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + NULL, + 0, + NULL, + NULL, // wait semaphores + 1, + &unwrappedCmd, // command buffers + 0, + NULL, // signal semaphores + }; + +#if ENABLED(SINGLE_FLUSH_VALIDATE) + for(auto it = queueBatch.begin(); it != queueBatch.end(); ++it) + { + vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + DoPipelineBarrier(cmd, 1, it); + vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + vkr = ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + vkr = ObjDisp(queue)->QueueWaitIdle(Unwrap(queue)); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + } +#else + vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + DoPipelineBarrier(cmd, (uint32_t)batch.size(), batch.data()); + + vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + VkFence &fence = queueFamilyFences[queueFamilyIndex]; + if(fence == VK_NULL_HANDLE) + { + VkFenceCreateInfo fenceInfo = { + /* sType = */ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + /* pNext = */ NULL, + /* flags = */ 0, + }; + vkr = ObjDisp(m_Device)->CreateFence(Unwrap(m_Device), &fenceInfo, NULL, &fence); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + } + + vkr = ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, fence); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + submittedFences.push_back(fence); +#endif + batch.clear(); + } + if(!submittedFences.empty()) + { + vkr = ObjDisp(m_Device)->WaitForFences(Unwrap(m_Device), (uint32_t)submittedFences.size(), + submittedFences.data(), VK_TRUE, 1000000000); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + vkr = ObjDisp(m_Device)->ResetFences(Unwrap(m_Device), (uint32_t)submittedFences.size(), + submittedFences.data()); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + submittedFences.clear(); + } + } + for(uint32_t queueFamilyIndex = 0; + queueFamilyIndex < ImageBarrierSequence::MAX_QUEUE_FAMILY_COUNT; ++queueFamilyIndex) + { + if(queueFamilyFences[queueFamilyIndex] != VK_NULL_HANDLE) + ObjDisp(m_Device)->DestroyFence(Unwrap(m_Device), queueFamilyFences[queueFamilyIndex], NULL); + } +} + +void WrappedVulkan::InlineSetupImageBarriers(VkCommandBuffer cmd, ImageBarrierSequence &barriers) +{ + rdcarray batch; + barriers.ExtractLastUnwrappedBatchForQueue(m_QueueFamilyIdx, batch); + if(!batch.empty()) + DoPipelineBarrier(cmd, (uint32_t)batch.size(), batch.data()); +} + +void WrappedVulkan::InlineCleanupImageBarriers(VkCommandBuffer cmd, ImageBarrierSequence &barriers) +{ + rdcarray batch; + barriers.ExtractFirstUnwrappedBatchForQueue(m_QueueFamilyIdx, batch); + if(!batch.empty()) + DoPipelineBarrier(cmd, (uint32_t)batch.size(), batch.data()); +} + uint32_t WrappedVulkan::HandlePreCallback(VkCommandBuffer commandBuffer, DrawFlags type, uint32_t multiDrawOffset) { diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 004704330..6f894c889 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -435,6 +435,10 @@ private: VkCommandBuffer GetExtQueueCmd(uint32_t queueFamilyIdx) const; void SubmitAndFlushExtQueue(uint32_t queueFamilyIdx) const; + void SubmitAndFlushImageStateBarriers(ImageBarrierSequence &barriers); + void InlineSetupImageBarriers(VkCommandBuffer cmd, ImageBarrierSequence &batches); + void InlineCleanupImageBarriers(VkCommandBuffer cmd, ImageBarrierSequence &batches); + struct QueueRemap { uint32_t family; diff --git a/renderdoc/driver/vulkan/vk_image_states.cpp b/renderdoc/driver/vulkan/vk_image_states.cpp new file mode 100644 index 000000000..7b85d02cf --- /dev/null +++ b/renderdoc/driver/vulkan/vk_image_states.cpp @@ -0,0 +1,134 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "vk_resources.h" + +template +void BarrierSequence::AddWrapped(uint32_t batchIndex, uint32_t queueFamilyIndex, + const Barrier &barrier) +{ + RDCASSERT(batchIndex < MAX_BATCH_COUNT); + RDCASSERT(queueFamilyIndex < MAX_QUEUE_FAMILY_COUNT); + batches[batchIndex][queueFamilyIndex].push_back(barrier); + ++barrierCount; +} +template void BarrierSequence::AddWrapped(uint32_t batchIndex, + uint32_t queueFamilyIndex, + const VkImageMemoryBarrier &barrier); + +template +void BarrierSequence::Merge(const BarrierSequence &other) +{ + for(uint32_t batchIndex = 0; batchIndex < MAX_BATCH_COUNT; ++batchIndex) + { + rdcarray *batch = batches[batchIndex]; + const rdcarray *otherBatch = other.batches[batchIndex]; + for(uint32_t queueFamilyIndex = 0; queueFamilyIndex < MAX_QUEUE_FAMILY_COUNT; ++queueFamilyIndex) + { + rdcarray &barriers = batch[queueFamilyIndex]; + const rdcarray &otherBarriers = otherBatch[queueFamilyIndex]; + barriers.insert(barriers.size(), otherBarriers.begin(), otherBarriers.size()); + barrierCount += otherBarriers.size(); + } + } +} +template void BarrierSequence::Merge( + const BarrierSequence &other); + +template +bool BarrierSequence::IsBatchEmpty(uint32_t batchIndex) const +{ + if(batchIndex > MAX_BATCH_COUNT) + return true; + for(uint32_t queueFamilyIndex = 0; queueFamilyIndex < MAX_QUEUE_FAMILY_COUNT; ++queueFamilyIndex) + { + if(!batches[batchIndex][queueFamilyIndex].empty()) + return false; + } + return true; +} +template bool BarrierSequence::IsBatchEmpty(uint32_t batchIndex) const; + +template <> +void BarrierSequence::UnwrapBarriers(rdcarray &barriers) +{ + for(auto it = barriers.begin(); it != barriers.end(); ++it) + { + it->image = ::Unwrap(it->image); + } +} + +template +void BarrierSequence::ExtractUnwrappedBatch(uint32_t batchIndex, uint32_t queueFamilyIndex, + rdcarray &result) +{ + if(batchIndex >= MAX_BATCH_COUNT || queueFamilyIndex >= MAX_QUEUE_FAMILY_COUNT) + return; + rdcarray &batch = batches[batchIndex][queueFamilyIndex]; + batch.swap(result); + batch.clear(); + barrierCount -= result.size(); + UnwrapBarriers(result); +} +template void BarrierSequence::ExtractUnwrappedBatch( + uint32_t batchIndex, uint32_t queueFamilyIndex, rdcarray &result); + +template +void BarrierSequence::ExtractFirstUnwrappedBatchForQueue(uint32_t queueFamilyIndex, + rdcarray &result) +{ + for(uint32_t batchIndex = 0; batchIndex < MAX_BATCH_COUNT; ++batchIndex) + { + if(!IsBatchEmpty(batchIndex)) + { + batches[batchIndex][queueFamilyIndex].swap(result); + batches[batchIndex][queueFamilyIndex].clear(); + barrierCount -= result.size(); + UnwrapBarriers(result); + return; + } + } +} +template void BarrierSequence::ExtractFirstUnwrappedBatchForQueue( + uint32_t queueFamilyIndex, rdcarray &result); + +template +void BarrierSequence::ExtractLastUnwrappedBatchForQueue(uint32_t queueFamilyIndex, + rdcarray &result) +{ + for(uint32_t batchIndex = MAX_BATCH_COUNT; batchIndex > 0;) + { + --batchIndex; + if(!IsBatchEmpty(batchIndex)) + { + batches[batchIndex][queueFamilyIndex].swap(result); + batches[batchIndex][queueFamilyIndex].clear(); + barrierCount -= result.size(); + UnwrapBarriers(result); + return; + } + } +} +template void BarrierSequence::ExtractLastUnwrappedBatchForQueue( + uint32_t queueFamilyIndex, rdcarray &result); diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index 665026142..e7d5d9b8f 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -1260,6 +1260,30 @@ struct ImageSubresourceRange } }; +template +struct BarrierSequence +{ + static const uint32_t MAX_BATCH_COUNT = 4; + static const uint32_t MAX_QUEUE_FAMILY_COUNT = 3; + + // batches[batchIndex][queueFamilyIndex] = array of barriers to submit to queueFamilyIndex as part + // of batchIndex + rdcarray batches[MAX_BATCH_COUNT][MAX_QUEUE_FAMILY_COUNT]; + size_t barrierCount = 0; + void AddWrapped(uint32_t batchIndex, uint32_t queueFamilyIndex, const Barrier &barrier); + void Merge(const BarrierSequence &other); + bool IsBatchEmpty(uint32_t batchIndex) const; + void ExtractUnwrappedBatch(uint32_t batchIndex, uint32_t queueFamilyIndex, + rdcarray &result); + void ExtractFirstUnwrappedBatchForQueue(uint32_t queueFamilyIndex, rdcarray &result); + void ExtractLastUnwrappedBatchForQueue(uint32_t queueFamilyIndex, rdcarray &result); + inline bool empty() const { return barrierCount == 0; } + inline size_t size() const { return barrierCount; } + static void UnwrapBarriers(rdcarray &barriers); +}; + +using ImageBarrierSequence = BarrierSequence; + struct ImgRefs { rdcarray rangeRefs;