From a114077e726c95fbb21721ce4c7eb4fb2b7145ce Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Thu, 7 Oct 2021 12:53:33 -0400 Subject: [PATCH] Final touches on the first pass of imgui hierarchy debugger Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../MultiplayerDebugHierarchyDebugger.cpp | 198 ++++++++++++++++++ .../Debug/MultiplayerDebugHierarchyDebugger.h | 55 +++++ .../MultiplayerDebugHierarchyReporter.cpp | 45 +++- .../Debug/MultiplayerDebugHierarchyReporter.h | 13 +- 4 files changed, 298 insertions(+), 13 deletions(-) create mode 100644 Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyDebugger.cpp create mode 100644 Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyDebugger.h diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyDebugger.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyDebugger.cpp new file mode 100644 index 0000000000..393f67474b --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyDebugger.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "MultiplayerDebugHierarchyDebugger.h" + +#include +#include +#include +#include + +#if defined(IMGUI_ENABLED) +#include +#endif + +namespace Multiplayer +{ + MultiplayerDebugHierarchyReporter::MultiplayerDebugHierarchyReporter() + : m_updateDebugOverlay([this]() { UpdateDebugOverlay(); }, AZ::Name("UpdateHierarchyDebug")) + { + CollectHierarchyRoots(); + + AZ::EntitySystemBus::Handler::BusConnect(); + m_updateDebugOverlay.Enqueue(AZ::TimeMs{ 0 }, true); + } + + MultiplayerDebugHierarchyReporter::~MultiplayerDebugHierarchyReporter() + { + AZ::EntitySystemBus::Handler::BusDisconnect(); + } + + // -------------------------------------------------------------------------------------------- + void MultiplayerDebugHierarchyReporter::OnImGuiUpdate() + { +#if defined(IMGUI_ENABLED) + for (const auto& root : m_hierarchyRoots) + { + if (const auto* rootComponent = root.second.m_rootComponent) + { + if (rootComponent->IsHierarchicalRoot()) + { + const AZStd::vector& hierarchicalChildren = rootComponent->GetHierarchicalEntities(); + + if (ImGui::TreeNode(rootComponent->GetEntity()->GetName().c_str(), + "Hierarchy, Root entity name [%s]: %4zu members", + rootComponent->GetEntity()->GetName().c_str(), + hierarchicalChildren.size())) + { + ImGui::Separator(); + ImGui::Columns(4, "hierarchy_columns"); + ImGui::Text("EntityId"); + ImGui::NextColumn(); + ImGui::Text("NetEntityId"); + ImGui::NextColumn(); + ImGui::Text("Entity Name"); + ImGui::NextColumn(); + ImGui::Text("Role"); + ImGui::NextColumn(); + + ImGui::Separator(); + ImGui::Columns(4, "hierarchy child info"); + + bool firstEntity = true; + for (const AZ::Entity* entity : hierarchicalChildren) + { + ImGui::Text("%s", entity->GetId().ToString().c_str()); + ImGui::NextColumn(); + ImGui::Text("%u", GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId())); + ImGui::NextColumn(); + ImGui::Text("%s", entity->GetName().c_str()); + ImGui::NextColumn(); + + if (firstEntity) + { + ImGui::Text("Root node"); + } + else if (entity->FindComponent()) + { + ImGui::Text("Inner root node"); + } + else if (entity->FindComponent()) + { + ImGui::Text("Child node"); + } + ImGui::NextColumn(); + + firstEntity = false; + } + + ImGui::Columns(1); + ImGui::TreePop(); + } + } + } + } +#endif + } + + + void MultiplayerDebugHierarchyReporter::UpdateDebugOverlay() + { + if (!m_hierarchyRoots.empty()) + { + if (m_debugDisplay == nullptr) + { + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId); + m_debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + } + + const AZ::u32 stateBefore = m_debugDisplay->GetState(); + m_debugDisplay->SetColor(AZ::Colors::White); + + for (const auto& root : m_hierarchyRoots) + { + if (const auto* rootComponent = root.second.m_rootComponent) + { + if (rootComponent->IsHierarchicalRoot()) + { + const AZStd::vector& hierarchicalChildren = rootComponent->GetHierarchicalEntities(); + + azsprintf(m_statusBuffer, "Hierarchy [%s] %u members", rootComponent->GetEntity()->GetName().c_str(), + aznumeric_cast(hierarchicalChildren.size())); + + AZ::Vector3 entityPosition = rootComponent->GetEntity()->GetTransform()->GetWorldTranslation(); + constexpr bool centerText = true; + m_debugDisplay->DrawTextLabel(entityPosition, 1.0f, m_statusBuffer, centerText, 0, 0); + } + } + } + + m_debugDisplay->SetState(stateBefore); + } + } + + void MultiplayerDebugHierarchyReporter::OnEntityActivated(const AZ::EntityId& entityId) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(entityId)) + { + if (auto* rootComponent = childEntity->FindComponent()) + { + HierarchyRootInfo info; + info.m_rootComponent = rootComponent; + rootComponent->BindNetworkHierarchyChangedEventHandler(info.m_changedEvent); + rootComponent->BindNetworkHierarchyLeaveEventHandler(info.m_leaveEvent); + + m_hierarchyRoots.insert(AZStd::make_pair(rootComponent, info)); + } + } + } + + void MultiplayerDebugHierarchyReporter::OnEntityDeactivated(const AZ::EntityId& entityId) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(entityId)) + { + if (auto* rootComponent = childEntity->FindComponent()) + { + m_hierarchyRoots.erase(rootComponent); + } + } + } + + void MultiplayerDebugHierarchyReporter::CollectHierarchyRoots() + { + AZStd::vector gatheredEntries; + const AZ::Sphere awarenessSphere = AZ::Sphere(AZ::Vector3::CreateZero(), 1000.f); + AZ::Interface::Get()->GetDefaultVisibilityScene()->Enumerate(awarenessSphere, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) + { + gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size()); + for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries) + { + if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity) + { + gatheredEntries.push_back(visEntry); + } + } + } + ); + + for (const AzFramework::VisibilityEntry* entry : gatheredEntries) + { + const AZ::Entity* entity = static_cast(entry->m_userData); + if (auto* rootComponent = entity->FindComponent()) + { + HierarchyRootInfo info; + info.m_rootComponent = rootComponent; + rootComponent->BindNetworkHierarchyChangedEventHandler(info.m_changedEvent); + rootComponent->BindNetworkHierarchyLeaveEventHandler(info.m_leaveEvent); + + m_hierarchyRoots.insert(AZStd::make_pair(rootComponent, info)); + } + } + } +} diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyDebugger.h b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyDebugger.h new file mode 100644 index 0000000000..978664b96b --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyDebugger.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Multiplayer +{ + /** + * \brief Reports hierarchy information in the scene. + */ + class MultiplayerDebugHierarchyReporter + : public AZ::EntitySystemBus::Handler + { + public: + MultiplayerDebugHierarchyReporter(); + ~MultiplayerDebugHierarchyReporter() override; + + //! main update loop + void OnImGuiUpdate(); + + //! Draws hierarchy information over hierarchy root entities + void UpdateDebugOverlay(); + + //! EntitySystemBus overrides. + void OnEntityActivated(const AZ::EntityId& entityId) override; + void OnEntityDeactivated(const AZ::EntityId& entityId) override; + + private: + AZ::ScheduledEvent m_updateDebugOverlay; + + AzFramework::DebugDisplayRequests* m_debugDisplay = nullptr; + + struct HierarchyRootInfo + { + NetworkHierarchyRootComponent* m_rootComponent = nullptr; + NetworkHierarchyChangedEvent::Handler m_changedEvent; + NetworkHierarchyLeaveEvent::Handler m_leaveEvent; + }; + + AZStd::unordered_map m_hierarchyRoots; + void CollectHierarchyRoots(); + + char m_statusBuffer[100] = {}; + }; +} diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp index 32e9a776a4..7c38a340eb 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp @@ -8,6 +8,8 @@ #include "MultiplayerDebugHierarchyReporter.h" +#include +#include #include #include #include @@ -17,8 +19,6 @@ #include #endif -#pragma optimize("", off) - namespace Multiplayer { MultiplayerDebugHierarchyReporter::MultiplayerDebugHierarchyReporter() @@ -39,6 +39,9 @@ namespace Multiplayer void MultiplayerDebugHierarchyReporter::OnImGuiUpdate() { #if defined(IMGUI_ENABLED) + ImGui::Text("Hierarchies"); + ImGui::Separator(); + for (const auto& root : m_hierarchyRoots) { if (const auto* rootComponent = root.second.m_rootComponent) @@ -48,7 +51,7 @@ namespace Multiplayer const AZStd::vector& hierarchicalChildren = rootComponent->GetHierarchicalEntities(); if (ImGui::TreeNode(rootComponent->GetEntity()->GetName().c_str(), - "Hierarchy, Root entity name [%s]: %4zu members", + "[%s] %4zu members", rootComponent->GetEntity()->GetName().c_str(), hierarchicalChildren.size())) { @@ -99,6 +102,16 @@ namespace Multiplayer } } } + + ImGui::Separator(); + if (ImGui::InputFloat("Awareness Radius", &m_awarenessRadius)) + { + CollectHierarchyRoots(); + } + if (ImGui::Button("Refresh")) + { + CollectHierarchyRoots(); + } #endif } @@ -168,9 +181,18 @@ namespace Multiplayer void MultiplayerDebugHierarchyReporter::CollectHierarchyRoots() { + m_hierarchyRoots.clear(); + AZ::Sphere awarenessSphere(AZ::Vector3::CreateZero(), m_awarenessRadius); + + const auto viewportContextManager = AZ::Interface::Get(); + if (const auto viewportContext = viewportContextManager->GetDefaultViewportContext()) + { + awarenessSphere.SetCenter(viewportContext->GetCameraTransform().GetTranslation()); + } + AZStd::vector gatheredEntries; - const AZ::Sphere awarenessSphere = AZ::Sphere(AZ::Vector3::CreateZero(), 1000.f); - AZ::Interface::Get()->GetDefaultVisibilityScene()->Enumerate(awarenessSphere, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) + AZ::Interface::Get()->GetDefaultVisibilityScene()->Enumerate(awarenessSphere, + [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size()); for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries) @@ -188,12 +210,15 @@ namespace Multiplayer const AZ::Entity* entity = static_cast(entry->m_userData); if (auto* rootComponent = entity->FindComponent()) { - HierarchyRootInfo info; - info.m_rootComponent = rootComponent; - rootComponent->BindNetworkHierarchyChangedEventHandler(info.m_changedEvent); - rootComponent->BindNetworkHierarchyLeaveEventHandler(info.m_leaveEvent); + if (awarenessSphere.GetCenter().GetDistanceEstimate(entity->GetTransform()->GetWorldTranslation()) < m_awarenessRadius) + { + HierarchyRootInfo info; + info.m_rootComponent = rootComponent; + rootComponent->BindNetworkHierarchyChangedEventHandler(info.m_changedEvent); + rootComponent->BindNetworkHierarchyLeaveEventHandler(info.m_leaveEvent); - m_hierarchyRoots.insert(AZStd::make_pair(rootComponent, info)); + m_hierarchyRoots.insert(AZStd::make_pair(rootComponent, info)); + } } } } diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.h b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.h index 98a131614c..2ce7e055a0 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.h +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.h @@ -15,6 +15,9 @@ namespace Multiplayer { + /** + * /brief Provides ImGui and debug draw hierarchy information at runtime. + */ class MultiplayerDebugHierarchyReporter : public AZ::EntitySystemBus::Handler { @@ -22,15 +25,17 @@ namespace Multiplayer MultiplayerDebugHierarchyReporter(); ~MultiplayerDebugHierarchyReporter() override; - //! main update loop + //! Main update loop. void OnImGuiUpdate(); - //! Draws bandwidth text over entities + //! Draws hierarchy information over hierarchy root entities. void UpdateDebugOverlay(); //! EntitySystemBus overrides. + //! @{ void OnEntityActivated(const AZ::EntityId& entityId) override; void OnEntityDeactivated(const AZ::EntityId& entityId) override; + //! @} private: AZ::ScheduledEvent m_updateDebugOverlay; @@ -46,7 +51,9 @@ namespace Multiplayer AZStd::unordered_map m_hierarchyRoots; void CollectHierarchyRoots(); - + char m_statusBuffer[100] = {}; + + float m_awarenessRadius = 1000.f; }; }