Graphics/FrameGraph: Handle invalidation and flush barriers

This commit is contained in:
Jérôme Leclercq 2021-12-16 18:29:40 +01:00
parent 38b143ce8f
commit 07199301df
4 changed files with 59 additions and 65 deletions

View File

@ -50,7 +50,7 @@ namespace Nz
BakedFrameGraph(std::vector<PassData> passes, std::vector<TextureData> textures, AttachmentIdToTextureId attachmentIdToTextureMapping, PassIdToPhysicalPassIndex passIdToPhysicalPassMapping); BakedFrameGraph(std::vector<PassData> passes, std::vector<TextureData> textures, AttachmentIdToTextureId attachmentIdToTextureMapping, PassIdToPhysicalPassIndex passIdToPhysicalPassMapping);
struct TextureTransition struct TextureBarrier
{ {
std::size_t textureId; std::size_t textureId;
MemoryAccessFlags dstAccessMask; MemoryAccessFlags dstAccessMask;
@ -75,7 +75,7 @@ namespace Nz
std::vector<std::size_t> outputTextureIndices; std::vector<std::size_t> outputTextureIndices;
std::vector<CommandBufferBuilder::ClearValues> outputClearValues; std::vector<CommandBufferBuilder::ClearValues> outputClearValues;
std::vector<SubpassData> subpasses; std::vector<SubpassData> subpasses;
std::vector<TextureTransition> transitions; std::vector<TextureBarrier> invalidationBarriers;
FramePass::ExecutionCallback executionCallback; FramePass::ExecutionCallback executionCallback;
Recti renderRect; Recti renderRect;
bool forceCommandBufferRegeneration = true; bool forceCommandBufferRegeneration = true;

View File

@ -50,7 +50,7 @@ namespace Nz
using AttachmentIdToPassId = std::unordered_map<std::size_t /*attachmentId*/, std::size_t /*passId*/>; using AttachmentIdToPassId = std::unordered_map<std::size_t /*attachmentId*/, std::size_t /*passId*/>;
using AttachmentIdToTextureId = std::unordered_map<std::size_t /*attachmentId*/, std::size_t /*textureId*/>; using AttachmentIdToTextureId = std::unordered_map<std::size_t /*attachmentId*/, std::size_t /*textureId*/>;
using PassIdToPhysicalPassIndex = std::unordered_map<std::size_t /*passId*/, std::size_t /*physicalPassId*/>; using PassIdToPhysicalPassIndex = std::unordered_map<std::size_t /*passId*/, std::size_t /*physicalPassId*/>;
using TextureTransition = BakedFrameGraph::TextureTransition; using TextureBarrier = BakedFrameGraph::TextureBarrier;
struct AttachmentProxy struct AttachmentProxy
{ {
@ -80,7 +80,7 @@ namespace Nz
}; };
std::string name; std::string name;
std::vector<TextureTransition> textureTransitions; std::vector<TextureBarrier> textureBarrier;
std::vector<Subpass> passes; std::vector<Subpass> passes;
}; };

View File

@ -56,7 +56,7 @@ namespace Nz
passData.commandBuffer = m_commandPool->BuildCommandBuffer([&](CommandBufferBuilder& builder) passData.commandBuffer = m_commandPool->BuildCommandBuffer([&](CommandBufferBuilder& builder)
{ {
for (auto& textureTransition : passData.transitions) for (auto& textureTransition : passData.invalidationBarriers)
{ {
const std::shared_ptr<Texture>& texture = m_textures[textureTransition.textureId].texture; const std::shared_ptr<Texture>& texture = m_textures[textureTransition.textureId].texture;
builder.TextureBarrier(textureTransition.srcStageMask, textureTransition.dstStageMask, textureTransition.srcAccessMask, textureTransition.dstAccessMask, textureTransition.oldLayout, textureTransition.newLayout, *texture); builder.TextureBarrier(textureTransition.srcStageMask, textureTransition.dstStageMask, textureTransition.srcAccessMask, textureTransition.dstAccessMask, textureTransition.oldLayout, textureTransition.newLayout, *texture);

View File

@ -67,8 +67,8 @@ namespace Nz
AssignPhysicalTextures(); AssignPhysicalTextures();
AssignPhysicalPasses(); AssignPhysicalPasses();
BuildPhysicalPasses(); BuildPhysicalPasses();
//BuildBarriers(); BuildBarriers();
//BuildPhysicalBarriers(); BuildPhysicalBarriers();
std::vector<BakedFrameGraph::PassData> bakedPasses; std::vector<BakedFrameGraph::PassData> bakedPasses;
bakedPasses.reserve(m_pending.physicalPasses.size()); bakedPasses.reserve(m_pending.physicalPasses.size());
@ -79,7 +79,7 @@ namespace Nz
auto& bakedPass = bakedPasses.emplace_back(); auto& bakedPass = bakedPasses.emplace_back();
bakedPass.name = std::move(physicalPass.name); bakedPass.name = std::move(physicalPass.name);
bakedPass.renderPass = std::move(m_pending.renderPasses[renderPassIndex++]); bakedPass.renderPass = std::move(m_pending.renderPasses[renderPassIndex++]);
bakedPass.transitions = std::move(physicalPass.textureTransitions); bakedPass.invalidationBarriers = std::move(physicalPass.textureBarrier);
for (auto& subpass : physicalPass.passes) for (auto& subpass : physicalPass.passes)
{ {
@ -274,7 +274,7 @@ namespace Nz
auto GetBarrier = [&](std::vector<Barrier>& barriers, std::size_t attachmentId) -> Barrier& auto GetBarrier = [&](std::vector<Barrier>& barriers, std::size_t attachmentId) -> Barrier&
{ {
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId); std::size_t textureId = Retrieve(m_pending.attachmentToTextures, ResolveAttachmentIndex(attachmentId));
auto it = std::find_if(barriers.begin(), barriers.end(), [&](const Barrier& barrier) { return barrier.textureId == textureId; }); auto it = std::find_if(barriers.begin(), barriers.end(), [&](const Barrier& barrier) { return barrier.textureId == textureId; });
if (it != barriers.end()) if (it != barriers.end())
@ -335,7 +335,7 @@ namespace Nz
invalidationBarrier.access = MemoryAccess::DepthStencilRead | MemoryAccess::DepthStencilWrite; invalidationBarrier.access = MemoryAccess::DepthStencilRead | MemoryAccess::DepthStencilWrite;
invalidationBarrier.stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate; invalidationBarrier.stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate;
auto& flushBarrier = GetInvalidationBarrier(dsOutputAttachement); auto& flushBarrier = GetFlushBarrier(dsOutputAttachement);
flushBarrier.layout = TextureLayout::DepthStencilReadWrite; flushBarrier.layout = TextureLayout::DepthStencilReadWrite;
flushBarrier.access = MemoryAccess::DepthStencilWrite; flushBarrier.access = MemoryAccess::DepthStencilWrite;
flushBarrier.stages = PipelineStage::FragmentTestsLate; flushBarrier.stages = PipelineStage::FragmentTestsLate;
@ -354,7 +354,7 @@ namespace Nz
else if (dsOutputAttachement != FramePass::InvalidAttachmentId) else if (dsOutputAttachement != FramePass::InvalidAttachmentId)
{ {
// DS output-only // DS output-only
auto& flushBarrier = GetInvalidationBarrier(dsOutputAttachement); auto& flushBarrier = GetFlushBarrier(dsOutputAttachement);
if (flushBarrier.layout != TextureLayout::Undefined) if (flushBarrier.layout != TextureLayout::Undefined)
throw std::runtime_error("layout mismatch"); throw std::runtime_error("layout mismatch");
@ -367,7 +367,7 @@ namespace Nz
void FrameGraph::BuildPhysicalBarriers() void FrameGraph::BuildPhysicalBarriers()
{ {
struct TextureStates struct PassTextureStates
{ {
MemoryAccessFlags invalidatedAccesses; MemoryAccessFlags invalidatedAccesses;
MemoryAccessFlags flushedAccesses; MemoryAccessFlags flushedAccesses;
@ -377,13 +377,21 @@ namespace Nz
TextureLayout finalLayout = TextureLayout::Undefined; TextureLayout finalLayout = TextureLayout::Undefined;
}; };
std::vector<TextureStates> textureStates; struct TextureStates
{
MemoryAccessFlags flushedAccesses;
PipelineStageFlags flushedStages;
TextureLayout currentLayout = TextureLayout::Undefined;
};
std::vector<TextureStates> textureStates(m_pending.textures.size());
std::vector<PassTextureStates> passTextureStates;
auto barriersIt = m_pending.barrierList.begin(); auto barriersIt = m_pending.barrierList.begin();
for (auto& physicalPass : m_pending.physicalPasses) for (auto& physicalPass : m_pending.physicalPasses)
{ {
textureStates.clear(); passTextureStates.clear();
textureStates.resize(m_pending.textures.size()); passTextureStates.resize(m_pending.textures.size());
for (auto& subpass : physicalPass.passes) for (auto& subpass : physicalPass.passes)
{ {
@ -391,9 +399,12 @@ namespace Nz
for (auto& invalidation : barriers.invalidationBarriers) for (auto& invalidation : barriers.invalidationBarriers)
{ {
auto& states = textureStates[invalidation.textureId]; auto& states = passTextureStates[invalidation.textureId];
if (states.initialLayout == TextureLayout::Undefined) if (states.initialLayout == TextureLayout::Undefined)
{ {
// First use in this pass
states.invalidatedAccesses |= invalidation.access; states.invalidatedAccesses |= invalidation.access;
states.invalidatedStages |= invalidation.stages; states.invalidatedStages |= invalidation.stages;
states.initialLayout = invalidation.layout; states.initialLayout = invalidation.layout;
@ -406,7 +417,7 @@ namespace Nz
for (auto& flush : barriers.flushBarriers) for (auto& flush : barriers.flushBarriers)
{ {
auto& states = textureStates[flush.textureId]; auto& states = passTextureStates[flush.textureId];
states.flushedAccesses |= flush.access; states.flushedAccesses |= flush.access;
states.flushedStages |= flush.stages; states.flushedStages |= flush.stages;
states.finalLayout = flush.layout; states.finalLayout = flush.layout;
@ -418,6 +429,11 @@ namespace Nz
states.invalidatedAccesses = flush.access; states.invalidatedAccesses = flush.access;
states.invalidatedStages = flush.stages; states.invalidatedStages = flush.stages;
textureStates[flush.textureId].currentLayout = flush.layout;
if (states.invalidatedStages & PipelineStage::FragmentTestsLate)
states.invalidatedStages |= PipelineStage::FragmentTestsEarly;
if (states.invalidatedAccesses & MemoryAccess::ColorWrite) if (states.invalidatedAccesses & MemoryAccess::ColorWrite)
states.invalidatedAccesses |= MemoryAccess::ColorRead; states.invalidatedAccesses |= MemoryAccess::ColorRead;
@ -433,22 +449,33 @@ namespace Nz
} }
for (std::size_t textureId = 0; textureId < textureStates.size(); ++textureId) for (std::size_t textureId = 0; textureId < passTextureStates.size(); ++textureId)
{ {
const auto& state = textureStates[textureId]; const auto& state = passTextureStates[textureId];
if (state.initialLayout == TextureLayout::Undefined && state.finalLayout == TextureLayout::Undefined) if (state.initialLayout == TextureLayout::Undefined && state.finalLayout == TextureLayout::Undefined)
continue; //< Texture wasn't touched in this pass continue; //< Texture wasn't touched in this pass
assert(state.finalLayout != TextureLayout::Undefined); assert(state.finalLayout != TextureLayout::Undefined);
// TODO: Register invalidation if (textureStates[textureId].flushedAccesses != 0)
{
auto& invalidationBarrier = physicalPass.textureBarrier.emplace_back();
invalidationBarrier.textureId = textureId;
invalidationBarrier.srcAccessMask = textureStates[textureId].flushedAccesses;
invalidationBarrier.srcStageMask = textureStates[textureId].flushedStages;
invalidationBarrier.dstAccessMask = state.invalidatedAccesses;
invalidationBarrier.dstStageMask = state.invalidatedStages;
invalidationBarrier.oldLayout = textureStates[textureId].currentLayout;
invalidationBarrier.newLayout = state.initialLayout;
if (state.flushedAccesses) textureStates[textureId].flushedAccesses = 0;
; // TODO: Register flush textureStates[textureId].flushedStages = 0;
}
if (state.invalidatedAccesses) textureStates[textureId].currentLayout = state.finalLayout;
; // TODO: Register flush textureStates[textureId].flushedAccesses |= state.flushedAccesses;
textureStates[textureId].flushedStages |= state.flushedStages;
} }
} }
} }
@ -645,7 +672,7 @@ namespace Nz
{ {
const std::shared_ptr<RenderDevice>& renderDevice = Graphics::Instance()->GetRenderDevice(); const std::shared_ptr<RenderDevice>& renderDevice = Graphics::Instance()->GetRenderDevice();
std::unordered_map<std::size_t /*textureId*/, TextureLayout> textureLayouts; std::vector<TextureLayout> textureLayouts(m_pending.textures.size(), TextureLayout::Undefined);
std::size_t physicalPassIndex = 0; std::size_t physicalPassIndex = 0;
for (auto& physicalPass : m_pending.physicalPasses) for (auto& physicalPass : m_pending.physicalPasses)
@ -662,24 +689,8 @@ namespace Nz
{ {
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, input.attachmentId); std::size_t textureId = Retrieve(m_pending.attachmentToTextures, input.attachmentId);
auto it = textureLayouts.find(textureId); TextureLayout& textureLayout = textureLayouts[textureId];
assert(it != textureLayouts.end()); assert(textureLayouts[textureId] != TextureLayout::Undefined);
TextureLayout& textureLayout = it->second;
if (textureLayout != TextureLayout::ColorInput)
{
auto& transition = physicalPass.textureTransitions.emplace_back();
transition.textureId = textureId;
transition.srcAccessMask = MemoryAccess::ColorWrite;
transition.srcStageMask = PipelineStage::ColorOutput;
transition.dstStageMask = PipelineStage::ColorOutput;
transition.dstAccessMask = MemoryAccess::ColorRead | MemoryAccess::ColorWrite;
transition.oldLayout = textureLayout;
transition.newLayout = TextureLayout::ColorInput;
}
textureLayout = TextureLayout::ColorInput; textureLayout = TextureLayout::ColorInput;
}; };
@ -688,15 +699,8 @@ namespace Nz
{ {
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, output.attachmentId); std::size_t textureId = Retrieve(m_pending.attachmentToTextures, output.attachmentId);
TextureLayout initialLayout = TextureLayout::Undefined; TextureLayout initialLayout = textureLayouts[textureId];
auto layoutIt = textureLayouts.find(textureId); textureLayouts[textureId] = TextureLayout::ColorOutput;
if (layoutIt != textureLayouts.end())
{
initialLayout = layoutIt->second;
layoutIt->second = TextureLayout::ColorOutput;
}
else
textureLayouts.emplace(textureId, TextureLayout::ColorOutput);
auto it = usedTextureAttachments.find(textureId); auto it = usedTextureAttachments.find(textureId);
if (it != usedTextureAttachments.end()) if (it != usedTextureAttachments.end())
@ -735,15 +739,8 @@ namespace Nz
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId); std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId);
TextureLayout initialLayout = TextureLayout::Undefined; TextureLayout initialLayout = textureLayouts[textureId];
auto layoutIt = textureLayouts.find(textureId); textureLayouts[textureId] = textureLayout;
if (layoutIt != textureLayouts.end())
{
initialLayout = layoutIt->second;
layoutIt->second = textureLayout;
}
else
textureLayouts.emplace(textureId, textureLayout);
depthStencilAttachmentId = attachmentId; depthStencilAttachmentId = attachmentId;
depthStencilAttachmentIndex = renderPassAttachments.size(); depthStencilAttachmentIndex = renderPassAttachments.size();
@ -882,11 +879,8 @@ namespace Nz
// Assign final layout (TODO: Use this to perform layouts useful for future passes?) // Assign final layout (TODO: Use this to perform layouts useful for future passes?)
for (const auto& [textureId, attachmentIndex] : usedTextureAttachments) for (const auto& [textureId, attachmentIndex] : usedTextureAttachments)
{ {
auto layoutIt = textureLayouts.find(textureId);
assert(layoutIt != textureLayouts.end());
auto& attachment = renderPassAttachments[attachmentIndex]; auto& attachment = renderPassAttachments[attachmentIndex];
attachment.finalLayout = layoutIt->second; attachment.finalLayout = textureLayouts[textureId];
} }
BuildPhysicalPassDependencies(colorAttachmentCount, depthStencilAttachmentIndex.has_value(), renderPassAttachments, subpassesDesc, subpassesDeps); BuildPhysicalPassDependencies(colorAttachmentCount, depthStencilAttachmentIndex.has_value(), renderPassAttachments, subpassesDesc, subpassesDeps);