diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp index 7197b95917..d3615bfaa9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp @@ -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(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); diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h index 6c9dd11914..b774144ba3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h @@ -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 m_pipelineState; Data::Instance m_shader; diff --git a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/DX12_Windows.h b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/DX12_Windows.h index 2eef783932..225b039c66 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/DX12_Windows.h +++ b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/DX12_Windows.h @@ -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; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLayout.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLayout.cpp index 3fdeec1c51..5a54282e70 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLayout.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLayout.cpp @@ -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(parameters.size()); rootSignatureDesc.pParameters = parameters.data(); rootSignatureDesc.NumStaticSamplers = static_cast(staticSamplers.size()); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingShaderTable.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingShaderTable.cpp index 0a9ea63d2f..c84c440c92 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingShaderTable.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingShaderTable.cpp @@ -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;