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

View File

@ -46,6 +46,8 @@ namespace Nz
inline std::size_t AddInput(std::size_t attachmentId); inline std::size_t AddInput(std::size_t attachmentId);
inline std::size_t AddOutput(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 CommandCallback& GetCommandCallback() const;
inline const std::optional<DepthStencilClear>& GetDepthStencilClear() const; inline const std::optional<DepthStencilClear>& GetDepthStencilClear() const;
inline std::size_t GetDepthStencilInput() const; inline std::size_t GetDepthStencilInput() const;

View File

@ -38,6 +38,25 @@ namespace Nz
return outputIndex; 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& inline auto FramePass::GetCommandCallback() const -> const CommandCallback&
{ {

View File

@ -179,17 +179,38 @@ namespace Nz
void FrameGraph::AssignPhysicalTextures() void FrameGraph::AssignPhysicalTextures()
{ {
std::vector<std::size_t> texturePool;
auto RegisterTexture = [&](std::size_t attachmentIndex) auto RegisterTexture = [&](std::size_t attachmentIndex)
{ {
if (auto it = m_pending.attachmentToTextures.find(attachmentIndex); it == m_pending.attachmentToTextures.end()) 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(); std::size_t textureId = m_pending.textures.size();
m_pending.attachmentToTextures.emplace(attachmentIndex, textureId); m_pending.attachmentToTextures.emplace(attachmentIndex, textureId);
TextureData& data = m_pending.textures.emplace_back(); TextureData& data = m_pending.textures.emplace_back();
data.format = m_attachments[attachmentIndex].format; data.format = attachmentData.format;
data.width = m_attachments[attachmentIndex].width; data.width = attachmentData.width;
data.height = m_attachments[attachmentIndex].height; data.height = attachmentData.height;
return textureId; return textureId;
} }
@ -197,6 +218,16 @@ namespace Nz
return it->second; 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) for (std::size_t passIndex : m_pending.passList)
{ {
const FramePass& framePass = m_framePasses[passIndex]; const FramePass& framePass = m_framePasses[passIndex];
@ -240,6 +271,24 @@ namespace Nz
TextureData& attachmentData = m_pending.textures[textureId]; TextureData& attachmentData = m_pending.textures[textureId];
attachmentData.usage |= TextureUsage::DepthStencilAttachment; 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 // Add TextureUsage::Sampled to backbuffer output
@ -274,7 +323,6 @@ namespace Nz
return barrier; return barrier;
} }
}; };
for (std::size_t passId : m_pending.passList) for (std::size_t passId : m_pending.passList)
@ -779,7 +827,6 @@ namespace Nz
const FramePass& framePass = m_framePasses[subpass.passIndex]; const FramePass& framePass = m_framePasses[subpass.passIndex];
std::size_t dsInputAttachment = framePass.GetDepthStencilInput(); std::size_t dsInputAttachment = framePass.GetDepthStencilInput();
std::size_t dsOutputAttachement = framePass.GetDepthStencilOutput(); std::size_t dsOutputAttachement = framePass.GetDepthStencilOutput();
bool depthRead = false;
if (dsInputAttachment != FramePass::InvalidAttachmentId && dsOutputAttachement != FramePass::InvalidAttachmentId) if (dsInputAttachment != FramePass::InvalidAttachmentId && dsOutputAttachement != FramePass::InvalidAttachmentId)
{ {