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
This commit is contained in:
Benson Joeris
2020-01-27 20:44:54 +00:00
committed by Baldur Karlsson
parent c84fcc2aae
commit 3d90609e39
7 changed files with 283 additions and 0 deletions
+1
View File
@@ -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
@@ -108,6 +108,7 @@
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="vk_bindless_feedback.cpp" />
<ClCompile Include="vk_image_states.cpp" />
<ClCompile Include="vk_msaa_array_conv.cpp" />
<ClCompile Include="vk_next_chains.cpp" />
<ClCompile Include="vk_outputwindow.cpp" />
@@ -145,6 +145,9 @@
<ClCompile Include="vk_pixelhistory.cpp">
<Filter>Replay</Filter>
</ClCompile>
<ClCompile Include="vk_image_states.cpp">
<Filter>Util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="vk_replay.h">
+116
View File
@@ -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<VkFence> submittedFences;
rdcarray<VkImageMemoryBarrier> 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<VkImageMemoryBarrier> 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<VkImageMemoryBarrier> 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)
{
+4
View File
@@ -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;
+134
View File
@@ -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 <typename Barrier>
void BarrierSequence<Barrier>::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<VkImageMemoryBarrier>::AddWrapped(uint32_t batchIndex,
uint32_t queueFamilyIndex,
const VkImageMemoryBarrier &barrier);
template <typename Barrier>
void BarrierSequence<Barrier>::Merge(const BarrierSequence<Barrier> &other)
{
for(uint32_t batchIndex = 0; batchIndex < MAX_BATCH_COUNT; ++batchIndex)
{
rdcarray<Barrier> *batch = batches[batchIndex];
const rdcarray<Barrier> *otherBatch = other.batches[batchIndex];
for(uint32_t queueFamilyIndex = 0; queueFamilyIndex < MAX_QUEUE_FAMILY_COUNT; ++queueFamilyIndex)
{
rdcarray<Barrier> &barriers = batch[queueFamilyIndex];
const rdcarray<Barrier> &otherBarriers = otherBatch[queueFamilyIndex];
barriers.insert(barriers.size(), otherBarriers.begin(), otherBarriers.size());
barrierCount += otherBarriers.size();
}
}
}
template void BarrierSequence<VkImageMemoryBarrier>::Merge(
const BarrierSequence<VkImageMemoryBarrier> &other);
template <typename Barrier>
bool BarrierSequence<Barrier>::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<VkImageMemoryBarrier>::IsBatchEmpty(uint32_t batchIndex) const;
template <>
void BarrierSequence<VkImageMemoryBarrier>::UnwrapBarriers(rdcarray<VkImageMemoryBarrier> &barriers)
{
for(auto it = barriers.begin(); it != barriers.end(); ++it)
{
it->image = ::Unwrap(it->image);
}
}
template <typename Barrier>
void BarrierSequence<Barrier>::ExtractUnwrappedBatch(uint32_t batchIndex, uint32_t queueFamilyIndex,
rdcarray<Barrier> &result)
{
if(batchIndex >= MAX_BATCH_COUNT || queueFamilyIndex >= MAX_QUEUE_FAMILY_COUNT)
return;
rdcarray<Barrier> &batch = batches[batchIndex][queueFamilyIndex];
batch.swap(result);
batch.clear();
barrierCount -= result.size();
UnwrapBarriers(result);
}
template void BarrierSequence<VkImageMemoryBarrier>::ExtractUnwrappedBatch(
uint32_t batchIndex, uint32_t queueFamilyIndex, rdcarray<VkImageMemoryBarrier> &result);
template <typename Barrier>
void BarrierSequence<Barrier>::ExtractFirstUnwrappedBatchForQueue(uint32_t queueFamilyIndex,
rdcarray<Barrier> &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<VkImageMemoryBarrier>::ExtractFirstUnwrappedBatchForQueue(
uint32_t queueFamilyIndex, rdcarray<VkImageMemoryBarrier> &result);
template <typename Barrier>
void BarrierSequence<Barrier>::ExtractLastUnwrappedBatchForQueue(uint32_t queueFamilyIndex,
rdcarray<Barrier> &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<VkImageMemoryBarrier>::ExtractLastUnwrappedBatchForQueue(
uint32_t queueFamilyIndex, rdcarray<VkImageMemoryBarrier> &result);
+24
View File
@@ -1260,6 +1260,30 @@ struct ImageSubresourceRange
}
};
template <typename Barrier>
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<Barrier> 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<Barrier> &other);
bool IsBatchEmpty(uint32_t batchIndex) const;
void ExtractUnwrappedBatch(uint32_t batchIndex, uint32_t queueFamilyIndex,
rdcarray<Barrier> &result);
void ExtractFirstUnwrappedBatchForQueue(uint32_t queueFamilyIndex, rdcarray<Barrier> &result);
void ExtractLastUnwrappedBatchForQueue(uint32_t queueFamilyIndex, rdcarray<Barrier> &result);
inline bool empty() const { return barrierCount == 0; }
inline size_t size() const { return barrierCount; }
static void UnwrapBarriers(rdcarray<Barrier> &barriers);
};
using ImageBarrierSequence = BarrierSequence<VkImageMemoryBarrier>;
struct ImgRefs
{
rdcarray<FrameRefType> rangeRefs;