ATOM-16656 PassTree tool: ParentPass image attachment preview doesn't work (#5032)

Move imageAttachmentCopy instance from RenderPass to Pass so it can support preview image for all passes but not only for RenderPass.
Fixed an issue with image attachment preview when switching render pipeline with attachment preview on.

Signed-off-by: Qing Tao <qingtao@amazon.com>
This commit is contained in:
Qing Tao
2021-10-27 11:28:21 -07:00
committed by GitHub
parent 30c366366e
commit 9e0756f3c1
9 changed files with 118 additions and 77 deletions
@@ -12,7 +12,7 @@
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/RenderPass.h>
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/ViewportContextManager.h>
@@ -12,6 +12,7 @@
#include <Atom/RPI.Public/GpuQuery/GpuQuerySystemInterface.h>
#include <Atom/RPI.Reflect/Image/Image.h>
#include <Atom/RPI.Public/Image/AttachmentImage.h>
#include <Atom/RPI.Public/Image/AttachmentImage.h>
#include <Atom/RPI.Public/Pass/PassAttachment.h>
#include <Atom/RPI.Public/Pass/PassDefines.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
@@ -59,6 +60,7 @@ namespace AZ
struct PassRequest;
struct PassValidationResults;
class AttachmentReadback;
class ImageAttachmentCopy;
using SortedPipelineViewTags = AZStd::set<PipelineViewTag, AZNameSortAscending>;
using PassesByDrawList = AZStd::map<RHI::DrawListTag, const Pass*>;
@@ -94,6 +96,8 @@ namespace AZ
{
AZ_RPI_PASS(Pass);
friend class ImageAttachmentPreviewPass;
public:
using ChildPassIndex = RHI::Handle<uint32_t, class ChildPass>;
@@ -369,6 +373,9 @@ namespace AZ
void UpdateReadbackAttachment(FramePrepareParams params, bool beforeAddScopes);
// Setup ImageAttachmentCopy
void UpdateAttachmentCopy(FramePrepareParams params);
// --- Protected Members ---
const Name PassNameThis{"This"};
@@ -466,6 +473,9 @@ namespace AZ
AZStd::shared_ptr<AttachmentReadback> m_attachmentReadback;
PassAttachmentReadbackOption m_readbackOption;
// For image attachment preview
AZStd::weak_ptr<ImageAttachmentCopy> m_attachmentCopy;
private:
// Return the Timestamp result of this pass
virtual TimestampResult GetTimestampResultInternal() const;
@@ -13,9 +13,7 @@
#include <Atom/RHI/DrawList.h>
#include <Atom/RHI/ScopeProducer.h>
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
#include <Atom/RPI.Public/Pass/Pass.h>
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
namespace AZ
@@ -29,7 +27,6 @@ namespace AZ
namespace RPI
{
class ImageAttachmentCopy;
class RenderPass;
class Query;
@@ -41,8 +38,6 @@ namespace AZ
{
AZ_RPI_PASS(RenderPass);
friend class ImageAttachmentPreviewPass;
using ScopeQuery = AZStd::array<RHI::Ptr<Query>, static_cast<size_t>(ScopeQueryType::Count)>;
public:
@@ -143,8 +138,6 @@ namespace AZ
// Readback the results from the ScopeQueries
void ReadbackScopeQueryResults();
AZStd::weak_ptr<ImageAttachmentCopy> m_attachmentCopy;
// Readback results from the Timestamp queries
TimestampResult m_timestampResult;
// Readback results from the PipelineStatistics queries
@@ -78,7 +78,7 @@ namespace AZ
~ImageAttachmentPreviewPass();
//! Preview the PassAttachment of a pass' PassAttachmentBinding
void PreviewImageAttachmentForPass(RenderPass* pass, const PassAttachment* passAttachment);
void PreviewImageAttachmentForPass(Pass* pass, const PassAttachment* passAttachment);
//! Set the output color attachment for this pass
void SetOutputColorAttachment(RHI::Ptr<PassAttachment> outputImageAttachment);
@@ -26,6 +26,7 @@
#include <Atom/RPI.Public/Pass/PassLibrary.h>
#include <Atom/RPI.Public/Pass/PassDefines.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
@@ -1215,6 +1216,12 @@ namespace AZ
m_queueState = PassQueueState::NoQueue;
InitializeInternal();
// Need to recreate the dest attachment because the source attachment might be changed
if (!m_attachmentCopy.expired())
{
m_attachmentCopy.lock()->InvalidateDestImage();
}
m_state = PassState::Initialized;
}
@@ -1301,6 +1308,9 @@ namespace AZ
// readback attachment with output state
UpdateReadbackAttachment(params, false);
// update attachment copy for preview
UpdateAttachmentCopy(params);
UpdateConnectedOutputBindings();
}
@@ -1489,6 +1499,14 @@ namespace AZ
}
}
void Pass::UpdateAttachmentCopy(FramePrepareParams params)
{
if (!m_attachmentCopy.expired())
{
m_attachmentCopy.lock()->FrameBegin(params);
}
}
bool Pass::IsTimestampQueryEnabled() const
{
return m_flags.m_timestampQueryEnabled;
@@ -177,12 +177,6 @@ namespace AZ
}
}
}
// Need to recreate the dest attachment because the source attachment might be changed
if (!m_attachmentCopy.expired())
{
m_attachmentCopy.lock()->InvalidateDestImage();
}
}
void RenderPass::FrameBeginInternal(FramePrepareParams params)
@@ -196,11 +190,7 @@ namespace AZ
// Read back the ScopeQueries submitted from previous frames
ReadbackScopeQueryResults();
if (!m_attachmentCopy.expired())
{
m_attachmentCopy.lock()->FrameBegin(params);
}
CollectSrgs();
PassSystemInterface::Get()->IncrementFrameRenderPassCount();
@@ -14,7 +14,7 @@
#include <Atom/RPI.Public/Buffer/Buffer.h>
#include <Atom/RPI.Public/Image/AttachmentImagePool.h>
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
#include <Atom/RPI.Public/Pass/RenderPass.h>
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
#include <Atom/RPI.Public/Pass/ParentPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
@@ -131,7 +131,7 @@ namespace AZ
Data::AssetBus::Handler::BusDisconnect();
}
void ImageAttachmentPreviewPass::PreviewImageAttachmentForPass(RenderPass* pass, const PassAttachment* passAttachment)
void ImageAttachmentPreviewPass::PreviewImageAttachmentForPass(Pass* pass, const PassAttachment* passAttachment)
{
if (passAttachment->GetAttachmentType() != RHI::AttachmentType::Image)
{
@@ -39,6 +39,8 @@ namespace AZ
bool m_showAttachments = false;
AZ::RPI::Pass* m_selectedPass = nullptr;
AZ::RPI::Pass* m_lastSelectedPass = nullptr;
AZ::Name m_selectedPassPath;
AZ::RHI::AttachmentId m_attachmentId;
AZ::Name m_slotName;
bool m_selectedChanged = false;
@@ -31,7 +31,7 @@
namespace AZ::Render
{
inline AZ::RPI::PassAttachment* FindPassAttachment(AZ::RPI::RenderPass* pass, AZ::RHI::AttachmentId attachmentId)
inline AZ::RPI::PassAttachment* FindPassAttachment(AZ::RPI::Pass* pass, AZ::RHI::AttachmentId attachmentId)
{
for (auto& binding : pass->GetAttachmentBindings())
{
@@ -47,6 +47,10 @@ namespace AZ::Render
{
using namespace AZ;
// always set m_selectedPass to empty and use m_selectedPassPath to find it when render the pass tree
m_selectedPass = nullptr;
bool needSaveAttachment = false;
ImGui::SetNextWindowSize(ImVec2(200.f, 200.f), ImGuiCond_FirstUseEver);
if (ImGui::Begin("PassTree View", &draw, ImGuiWindowFlags_None))
{
@@ -83,60 +87,16 @@ namespace AZ::Render
if (Scriptable_ImGui::Button("Save Attachment"))
{
m_attachmentReadbackInfo = "";
if (!m_readback)
{
m_readback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "AttachmentReadback" });
m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1));
}
if (m_selectedPass && !m_slotName.IsEmpty())
{
bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName);
if (!readbackResult)
{
AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr());
}
}
needSaveAttachment = true;
}
ImGui::TextWrapped("%s", m_attachmentReadbackInfo.c_str());
}
if (m_previewAttachment && m_selectedChanged)
{
m_selectedChanged = false;
if (!m_attachmentId.IsEmpty() && m_selectedPass)
{
AZ::RPI::RenderPass* renderPass = azrtti_cast<AZ::RPI::RenderPass*>(m_selectedPass);
if (renderPass)
{
if (!m_previewPass->GetParent())
{
RPI::PassSystemInterface::Get()->GetRootPass()->AddChild(m_previewPass);
}
AZ::RPI::PassAttachment* attachment = FindPassAttachment(renderPass, m_attachmentId);
if (attachment)
{
// Reset output attachment to empty so the preview will use pass's owner render pipeline's output
m_previewPass->SetOutputColorAttachment(nullptr);
m_previewPass->PreviewImageAttachmentForPass(renderPass, attachment);
}
}
else
{
m_previewPass->ClearPreviewAttachment();
if (m_previewPass->GetParent())
{
m_previewPass->QueueForRemoval();
}
}
}
}
ImGui::End();
// Draw the hierarchical view
// It will assign m_seletedPass if there is a pass matches m_seletedPassPath
ImGui::SetNextWindowPos(ImVec2(300, 60), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(300, 500), ImGuiCond_FirstUseEver);
if (ImGui::Begin("PassTree", nullptr, ImGuiWindowFlags_None))
@@ -144,6 +104,63 @@ namespace AZ::Render
DrawTreeView(rootPass);
}
ImGui::End();
// It's possible that the pass pointer changed but selected pass path wasn't changed
if (m_selectedPass != m_lastSelectedPass)
{
m_selectedChanged = true;
if (m_selectedPass == nullptr)
{
m_selectedPassPath = AZ::Name{};
}
}
m_lastSelectedPass = m_selectedPass;
if (m_previewAttachment && m_selectedChanged)
{
m_selectedChanged = false;
if (!m_attachmentId.IsEmpty() && m_selectedPass)
{
if (!m_previewPass->GetParent())
{
RPI::PassSystemInterface::Get()->GetRootPass()->AddChild(m_previewPass);
}
AZ::RPI::PassAttachment* attachment = FindPassAttachment(m_selectedPass, m_attachmentId);
if (attachment)
{
// Reset output attachment to empty so the preview will use pass's owner render pipeline's output
m_previewPass->SetOutputColorAttachment(nullptr);
m_previewPass->PreviewImageAttachmentForPass(m_selectedPass, attachment);
}
}
else
{
m_previewPass->ClearPreviewAttachment();
if (m_previewPass->GetParent())
{
m_previewPass->QueueForRemoval();
}
}
}
if (needSaveAttachment)
{
m_attachmentReadbackInfo = "";
if (!m_readback)
{
m_readback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "AttachmentReadback" });
m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1));
}
if (m_selectedPass && !m_slotName.IsEmpty())
{
bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName);
if (!readbackResult)
{
AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr());
}
}
}
}
inline void ImGuiPassTree::DrawPassAttachments(AZ::RPI::Pass* pass)
@@ -202,6 +219,7 @@ namespace AZ::Render
if (Scriptable_ImGui::Selectable(label.c_str(), m_attachmentId == binding.m_attachment->GetAttachmentId()))
{
m_selectedPassPath = pass->GetPathName();
m_selectedPass = pass;
m_attachmentId = binding.m_attachment->GetAttachmentId();
m_slotName = binding.m_name;
@@ -232,9 +250,9 @@ namespace AZ::Render
if (!m_showAttachments)
{
// Only draw the leaf pass as selectable if we are not showing attachments as its children
if (Scriptable_ImGui::Selectable(pass->GetName().GetCStr(), m_selectedPass == pass))
if (Scriptable_ImGui::Selectable(pass->GetName().GetCStr(), m_selectedPassPath == pass->GetPathName()))
{
m_selectedPass = pass;
m_selectedPassPath = pass->GetPathName();
m_attachmentId = AZ::RHI::AttachmentId{};
m_slotName = AZ::Name{};
m_selectedChanged = true;
@@ -244,13 +262,13 @@ namespace AZ::Render
{
// Draw the pass as a tree node which has attachments as its children
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen
| ((m_selectedPass == pass) ? ImGuiTreeNodeFlags_Selected : 0);
| ((m_selectedPassPath == pass->GetPathName()) ? ImGuiTreeNodeFlags_Selected : 0);
bool nodeOpen = Scriptable_ImGui::TreeNodeEx(pass->GetName().GetCStr(), flags);
if (ImGui::IsItemClicked())
{
m_selectedPass = pass;
m_selectedPassPath = pass->GetPathName();
m_attachmentId = AZ::RHI::AttachmentId{};
m_slotName = AZ::Name{};
m_selectedChanged = true;
@@ -259,7 +277,6 @@ namespace AZ::Render
if (nodeOpen)
{
DrawPassAttachments(pass);
Scriptable_ImGui::TreePop();
}
}
@@ -268,13 +285,13 @@ namespace AZ::Render
{
// For a ParentPasse, draw it as a tree node
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen
| ((m_selectedPass == pass) ? ImGuiTreeNodeFlags_Selected : 0);
| ((m_selectedPassPath == pass->GetPathName()) ? ImGuiTreeNodeFlags_Selected : 0);
bool nodeOpen = ImGui::TreeNodeEx(pass->GetName().GetCStr(), flags);
if (ImGui::IsItemClicked())
{
m_selectedPass = pass;
m_selectedPassPath = pass->GetPathName();
m_attachmentId = AZ::RHI::AttachmentId{};
m_slotName = AZ::Name{};
m_selectedChanged = true;
@@ -282,7 +299,10 @@ namespace AZ::Render
if (nodeOpen)
{
DrawPassAttachments(pass);
if (m_showAttachments)
{
DrawPassAttachments(pass);
}
for (const auto& child : asParent->GetChildren())
{
DrawTreeView(child.get());
@@ -296,6 +316,12 @@ namespace AZ::Render
{
ImGui::PopStyleColor();
}
// set m_selectedPass if pass path matches
if (pass->GetPathName() == m_selectedPassPath)
{
m_selectedPass = pass;
}
}
inline void ImGuiPassTree::ReadbackCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
@@ -364,7 +390,9 @@ namespace AZ::Render
m_previewAttachment = false;
m_showAttachments = false;
m_selectedPassPath = AZ::Name{};
m_selectedPass = nullptr;
m_lastSelectedPass = nullptr;
m_attachmentId = AZ::RHI::AttachmentId{};
m_slotName = AZ::Name{};
m_selectedChanged = false;