Graphics/FrameGraph: Reuse textures if possible

This commit is contained in:
Jérôme Leclercq 2021-12-05 16:53:02 +01:00
parent 3b1bf480e6
commit 4eb96849db
4 changed files with 75 additions and 5 deletions

View File

@ -45,6 +45,7 @@ namespace Nz
using BarrierList = std::vector<PassBarriers>;
using PassList = std::vector<std::size_t /*PassIndex*/>;
using AttachmentIdToPassMap = std::unordered_map<std::size_t /*resourceIndex*/, PassList /*passIndexes*/>;
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 PassIdToPhysicalPassIndex = std::unordered_map<std::size_t /*passId*/, std::size_t /*physicalPassId*/>;
using TextureTransition = BakedFrameGraph::TextureTransition;
@ -88,6 +89,7 @@ namespace Nz
std::vector<std::shared_ptr<RenderPass>> renderPasses;
std::vector<PhysicalPassData> physicalPasses;
std::vector<TextureData> textures;
AttachmentIdToPassId attachmentLastUse;
AttachmentIdToPassMap attachmentReadList;
AttachmentIdToPassMap attachmentWriteList;
AttachmentIdToTextureId attachmentToTextures;

View File

@ -46,6 +46,8 @@ namespace Nz
inline std::size_t AddInput(std::size_t attachmentId);
inline std::size_t AddOutput(std::size_t attachmentId);
template<typename F> void ForEachAttachment(F&& func) const;
inline const CommandCallback& GetCommandCallback() const;
inline const std::optional<DepthStencilClear>& GetDepthStencilClear() const;
inline std::size_t GetDepthStencilInput() const;

View File

@ -38,6 +38,25 @@ namespace Nz
return outputIndex;
}
template<typename F>
void FramePass::ForEachAttachment(F&& func) const
{
for (const auto& input : m_inputs)
func(input.attachmentId);
for (const auto& output : m_outputs)
func(output.attachmentId);
if (m_depthStencilInput != FramePass::InvalidAttachmentId)
{
func(m_depthStencilInput);
if (m_depthStencilOutput != FramePass::InvalidAttachmentId && m_depthStencilOutput != m_depthStencilInput)
func(m_depthStencilOutput);
}
else if (m_depthStencilOutput != FramePass::InvalidAttachmentId)
func(m_depthStencilOutput);
}
inline auto FramePass::GetCommandCallback() const -> const CommandCallback&
{

View File

@ -179,17 +179,38 @@ namespace Nz
void FrameGraph::AssignPhysicalTextures()
{
std::vector<std::size_t> texturePool;
auto RegisterTexture = [&](std::size_t attachmentIndex)
{
if (auto it = m_pending.attachmentToTextures.find(attachmentIndex); it == m_pending.attachmentToTextures.end())
{
const auto& attachmentData = m_attachments[attachmentIndex];
// Fetch from reuse pool if possible
for (auto it = texturePool.begin(); it != texturePool.end(); ++it)
{
std::size_t textureId = *it;
TextureData& data = m_pending.textures[textureId];
if (data.format != attachmentData.format ||
data.width != attachmentData.width ||
data.height != attachmentData.height)
continue;
texturePool.erase(it);
m_pending.attachmentToTextures.emplace(attachmentIndex, textureId);
return textureId;
}
std::size_t textureId = m_pending.textures.size();
m_pending.attachmentToTextures.emplace(attachmentIndex, textureId);
TextureData& data = m_pending.textures.emplace_back();
data.format = m_attachments[attachmentIndex].format;
data.width = m_attachments[attachmentIndex].width;
data.height = m_attachments[attachmentIndex].height;
data.format = attachmentData.format;
data.width = attachmentData.width;
data.height = attachmentData.height;
return textureId;
}
@ -197,6 +218,16 @@ namespace Nz
return it->second;
};
// Assign last use pass index for every attachment
for (std::size_t passIndex : m_pending.passList)
{
const FramePass& framePass = m_framePasses[passIndex];
framePass.ForEachAttachment([&](std::size_t attachmentId)
{
m_pending.attachmentLastUse[attachmentId] = passIndex;
});
}
for (std::size_t passIndex : m_pending.passList)
{
const FramePass& framePass = m_framePasses[passIndex];
@ -240,6 +271,24 @@ namespace Nz
TextureData& attachmentData = m_pending.textures[textureId];
attachmentData.usage |= TextureUsage::DepthStencilAttachment;
}
framePass.ForEachAttachment([&](std::size_t attachmentId)
{
std::size_t lastUsingPassId = Retrieve(m_pending.attachmentLastUse, attachmentId);
// If this pass is the last one where this attachment is used, push the texture to the reuse pool
if (passIndex == lastUsingPassId)
{
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId);
// For input/output depth-stencil buffer, the same texture can be used
if (texturePool.empty() || texturePool.back() != textureId)
{
assert(std::find(texturePool.begin(), texturePool.end(), textureId) == texturePool.end());
texturePool.push_back(textureId);
}
}
});
}
// Add TextureUsage::Sampled to backbuffer output
@ -274,7 +323,6 @@ namespace Nz
return barrier;
}
};
for (std::size_t passId : m_pending.passList)
@ -779,7 +827,6 @@ namespace Nz
const FramePass& framePass = m_framePasses[subpass.passIndex];
std::size_t dsInputAttachment = framePass.GetDepthStencilInput();
std::size_t dsOutputAttachement = framePass.GetDepthStencilOutput();
bool depthRead = false;
if (dsInputAttachment != FramePass::InvalidAttachmentId && dsOutputAttachement != FramePass::InvalidAttachmentId)
{