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;