Fix debug ImGui asserts I introduced in [b2c13b2]. (#6104)

Also allow different D3D12_ROOT_SIGNATURE flags to be set for each platform.

Signed-off-by: bosnichd <bosnichd@amazon.com>
This commit is contained in:
bosnichd
2021-12-02 14:17:12 -07:00
committed by GitHub
parent 82b1b6f337
commit 7a0282a534
5 changed files with 82 additions and 15 deletions
@@ -68,6 +68,8 @@ namespace AZ
: Base(descriptor)
, AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDebugUI() - 1) // Give ImGui manager priority over the pass
, AzFramework::InputTextEventListener(AzFramework::InputTextEventListener::GetPriorityDebugUI() - 1) // Give ImGui manager priority over the pass
, m_tickHandlerFrameStart(*this)
, m_tickHandlerFrameEnd(*this)
{
const ImGuiPassData* imguiPassData = RPI::PassUtils::GetPassData<ImGuiPassData>(descriptor);
@@ -102,7 +104,6 @@ namespace AZ
Init();
ImGui::NewFrame();
TickBus::Handler::BusConnect();
AzFramework::InputChannelEventListener::Connect();
AzFramework::InputTextEventListener::Connect();
}
@@ -127,7 +128,6 @@ namespace AZ
AzFramework::InputTextEventListener::BusDisconnect();
AzFramework::InputChannelEventListener::BusDisconnect();
TickBus::Handler::BusDisconnect();
}
ImGuiContext* ImGuiPass::GetContext()
@@ -140,23 +140,61 @@ namespace AZ
m_drawData.push_back(drawData);
}
int ImGuiPass::GetTickOrder()
ImGuiPass::TickHandlerFrameStart::TickHandlerFrameStart(ImGuiPass& imGuiPass)
: m_imGuiPass(imGuiPass)
{
TickBus::Handler::BusConnect();
}
int ImGuiPass::TickHandlerFrameStart::GetTickOrder()
{
// We have to call ImGui::NewFrame (which happens in ImGuiPass::OnTick) after setting
// ImGui::GetIO().NavInputs (which happens in ImGuiPass::OnInputChannelEventFiltered),
// but before ImGui::Render (which happens in ImGuiPass::SetupFrameGraphDependencies).
return AZ::ComponentTickBus::TICK_PRE_RENDER;
}
void ImGuiPass::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
void ImGuiPass::TickHandlerFrameStart::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
{
auto imguiContextScope = ImguiContextScope(m_imguiContext);
auto imguiContextScope = ImguiContextScope(m_imGuiPass.m_imguiContext);
ImGui::NewFrame();
auto& io = ImGui::GetIO();
io.DeltaTime = deltaTime;
}
ImGuiPass::TickHandlerFrameEnd::TickHandlerFrameEnd(ImGuiPass& imGuiPass)
: m_imGuiPass(imGuiPass)
{
TickBus::Handler::BusConnect();
}
int ImGuiPass::TickHandlerFrameEnd::GetTickOrder()
{
// ImGui::NewFrame() must be called (see ImGuiPass::TickHandlerFrameStart::OnTick) after populating
// ImGui::GetIO().NavInputs (see ImGuiPass::OnInputChannelEventFiltered), and paired with a call to
// ImGui::EndFrame() (see ImGuiPass::TickHandlerFrameEnd::OnTick); if this is not called explicitly
// then it will be called from inside ImGui::Render() (see ImGuiPass::SetupFrameGraphDependencies).
//
// ImGui::Render() gets called (indirectly) from OnSystemTick, so we cannot rely on it being paired
// with a matching call to ImGui::NewFrame() that gets called from OnTick, because OnSystemTick and
// OnTick can be called at different frequencies under some circumstances (namely from the editor).
//
// To account for this we must explicitly call ImGui::EndFrame() once a frame from OnTick to ensure
// that every call to ImGui::NewFrame() has been matched with a call to ImGui::EndFrame(), but only
// after ImGui::Render() has had the chance first (if so calling ImGui::EndFrame() again is benign).
//
// Because ImGui::Render() gets called (indirectly) from OnSystemTick, which usually happens at the
// start of every frame, we give TickHandlerFrameEnd::OnTick() the order of TICK_FIRST such that it
// will be called first on the regular tick bus, which is invoked immediately after the system tick.
//
// So while returning TICK_FIRST is incredibly counter-intuitive, hopefully that all explains why.
return AZ::ComponentTickBus::TICK_FIRST;
}
void ImGuiPass::TickHandlerFrameEnd::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
{
auto imguiContextScope = ImguiContextScope(m_imGuiPass.m_imguiContext);
ImGui::EndFrame();
}
bool ImGuiPass::OnInputTextEventFiltered(const AZStd::string& textUTF8)
{
auto imguiContextScope = ImguiContextScope(m_imguiContext);
@@ -54,7 +54,6 @@ namespace AZ
//! This pass owns and manages activation of an Imgui context.
class ImGuiPass
: public RPI::RenderPass
, private TickBus::Handler
, private AzFramework::InputChannelEventListener
, private AzFramework::InputTextEventListener
{
@@ -76,10 +75,6 @@ namespace AZ
//! Allows draw data from other imgui contexts to be rendered on this context.
void RenderImguiDrawData(const ImDrawData& drawData);
// TickBus::Handler overrides...
int GetTickOrder() override;
void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override;
// AzFramework::InputTextEventListener overrides...
bool OnInputTextEventFiltered(const AZStd::string& textUTF8) override;
@@ -99,6 +94,35 @@ namespace AZ
void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
private:
//! Class which connects to the tick handler using the tick order required at the start of an ImGui frame.
class TickHandlerFrameStart : protected TickBus::Handler
{
public:
TickHandlerFrameStart(ImGuiPass& imGuiPass);
protected:
// TickBus::Handler overrides...
int GetTickOrder() override;
void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override;
private:
ImGuiPass& m_imGuiPass;
};
//! Class which connects to the tick handler using the tick order required at the end of an ImGui frame.
class TickHandlerFrameEnd : protected TickBus::Handler
{
public:
TickHandlerFrameEnd(ImGuiPass& imGuiPass);
protected:
// TickBus::Handler overrides...
int GetTickOrder() override;
void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override;
private:
ImGuiPass& m_imGuiPass;
};
struct DrawInfo
{
@@ -112,6 +136,8 @@ namespace AZ
void Init();
ImGuiContext* m_imguiContext = nullptr;
TickHandlerFrameStart m_tickHandlerFrameStart;
TickHandlerFrameEnd m_tickHandlerFrameEnd;
RHI::Ptr<RPI::PipelineStateForDraw> m_pipelineState;
Data::Instance<RPI::Shader> m_shader;
@@ -56,6 +56,9 @@ AZ_POP_DISABLE_WARNING
// This define controls whether DXR ray tracing support is available on the platform.
#define AZ_DX12_DXR_SUPPORT
// This define is used to initialize the D3D12_ROOT_SIGNATURE_DESC::Flags property.
#define AZ_DX12_ROOT_SIGNATURE_FLAGS D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
using ID3D12CommandAllocatorX = ID3D12CommandAllocator;
using ID3D12CommandQueueX = ID3D12CommandQueue;
using ID3D12DeviceX = ID3D12Device5;
@@ -417,7 +417,7 @@ namespace AZ
}
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
rootSignatureDesc.Flags = AZ_DX12_ROOT_SIGNATURE_FLAGS;
rootSignatureDesc.NumParameters = static_cast<uint32_t>(parameters.size());
rootSignatureDesc.pParameters = parameters.data();
rootSignatureDesc.NumStaticSamplers = static_cast<uint32_t>(staticSamplers.size());
@@ -86,7 +86,7 @@ namespace AZ
AZStd::wstring shaderExportNameWstring;
AZStd::to_wstring(shaderExportNameWstring, record.m_shaderExportName.GetStringView());
void* shaderIdentifier = stateObjectProperties->GetShaderIdentifier(shaderExportNameWstring.c_str());
const void* shaderIdentifier = stateObjectProperties->GetShaderIdentifier(shaderExportNameWstring.c_str());
memcpy(mappedData, shaderIdentifier, D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES);
mappedData += D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;