MultiplayerDebugMultiplayerMetrics.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Source/Debug/MultiplayerDebugMultiplayerMetrics.h>
  9. #include <Multiplayer/IMultiplayer.h>
  10. namespace Multiplayer
  11. {
  12. #ifdef IMGUI_ENABLED
  13. void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond)
  14. {
  15. uint64_t summedCalls = 0;
  16. uint64_t summedBytes = 0;
  17. for (uint32_t index = 0; index < MultiplayerStats::RingbufferSamples; ++index)
  18. {
  19. summedCalls += metric.m_callHistory[index];
  20. summedBytes += metric.m_byteHistory[index];
  21. }
  22. const float totalTimeSeconds = static_cast<float>(stats.m_totalHistoryTimeMs) / 1000.0f;
  23. outCallsPerSecond += (summedCalls > 0 && totalTimeSeconds > 0.0f) ? aznumeric_cast<float>(summedCalls) / totalTimeSeconds : 0.0f;
  24. outBytesPerSecond += (summedBytes > 0 && totalTimeSeconds > 0.0f) ? aznumeric_cast<float>(summedBytes) / totalTimeSeconds : 0.0f;
  25. }
  26. bool DrawMetricsRow(const char* name, bool expandable, uint64_t totalCalls, uint64_t totalBytes, float callsPerSecond, float bytesPerSecond)
  27. {
  28. const ImGuiTreeNodeFlags flags = expandable
  29. ? ImGuiTreeNodeFlags_SpanFullWidth
  30. : (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
  31. ImGui::TableNextRow();
  32. ImGui::TableNextColumn();
  33. const bool open = ImGui::TreeNodeEx(name, flags);
  34. ImGui::TableNextColumn();
  35. ImGui::Text("%11llu", aznumeric_cast<AZ::u64>(totalCalls));
  36. ImGui::TableNextColumn();
  37. ImGui::Text("%11llu", aznumeric_cast<AZ::u64>(totalBytes));
  38. ImGui::TableNextColumn();
  39. ImGui::Text("%11.2f", callsPerSecond);
  40. ImGui::TableNextColumn();
  41. ImGui::Text("%11.2f", bytesPerSecond);
  42. return open;
  43. }
  44. bool DrawSummaryRow(const char* name, const MultiplayerStats& stats)
  45. {
  46. const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics();
  47. const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateTotalPropertyUpdateRecvMetrics();
  48. const MultiplayerStats::Metric rpcsSent = stats.CalculateTotalRpcsSentMetrics();
  49. const MultiplayerStats::Metric rpcsRecv = stats.CalculateTotalRpcsRecvMetrics();
  50. const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls;
  51. const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes;
  52. float callsPerSecond = 0.0f;
  53. float bytesPerSecond = 0.0f;
  54. AccumulatePerSecondValues(stats, propertyUpdatesSent, callsPerSecond, bytesPerSecond);
  55. AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond);
  56. AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond);
  57. AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
  58. return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
  59. }
  60. bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId)
  61. {
  62. const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
  63. const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId);
  64. const MultiplayerStats::Metric rpcsSent = stats.CalculateComponentRpcsSentMetrics(netComponentId);
  65. const MultiplayerStats::Metric rpcsRecv = stats.CalculateComponentRpcsRecvMetrics(netComponentId);
  66. const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls;
  67. const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes;
  68. float callsPerSecond = 0.0f;
  69. float bytesPerSecond = 0.0f;
  70. AccumulatePerSecondValues(stats, propertyUpdatesSent, callsPerSecond, bytesPerSecond);
  71. AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond);
  72. AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond);
  73. AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
  74. return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
  75. }
  76. void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId)
  77. {
  78. MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
  79. {
  80. const MultiplayerStats::Metric metric = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
  81. float callsPerSecond = 0.0f;
  82. float bytesPerSecond = 0.0f;
  83. AccumulatePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond);
  84. if (DrawMetricsRow("PropertyUpdates Sent", true, metric.m_totalCalls, metric.m_totalBytes, callsPerSecond, bytesPerSecond))
  85. {
  86. const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[aznumeric_cast<AZStd::size_t>(netComponentId)];
  87. for (AZStd::size_t index = 0; index < componentStats.m_propertyUpdatesSent.size(); ++index)
  88. {
  89. const PropertyIndex propertyIndex = aznumeric_cast<PropertyIndex>(index);
  90. const char* propertyName = componentRegistry->GetComponentPropertyName(netComponentId, propertyIndex);
  91. const MultiplayerStats::Metric& subMetric = componentStats.m_propertyUpdatesSent[index];
  92. callsPerSecond = 0.0f;
  93. bytesPerSecond = 0.0f;
  94. AccumulatePerSecondValues(stats, subMetric, callsPerSecond, bytesPerSecond);
  95. DrawMetricsRow(propertyName, false, subMetric.m_totalCalls, subMetric.m_totalBytes, callsPerSecond, bytesPerSecond);
  96. }
  97. ImGui::TreePop();
  98. }
  99. }
  100. {
  101. const MultiplayerStats::Metric metric = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId);
  102. float callsPerSecond = 0.0f;
  103. float bytesPerSecond = 0.0f;
  104. AccumulatePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond);
  105. if (DrawMetricsRow("PropertyUpdates Recv", true, metric.m_totalCalls, metric.m_totalBytes, callsPerSecond, bytesPerSecond))
  106. {
  107. const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[aznumeric_cast<AZStd::size_t>(netComponentId)];
  108. for (AZStd::size_t index = 0; index < componentStats.m_propertyUpdatesRecv.size(); ++index)
  109. {
  110. const PropertyIndex propertyIndex = aznumeric_cast<PropertyIndex>(index);
  111. const char* propertyName = componentRegistry->GetComponentPropertyName(netComponentId, propertyIndex);
  112. const MultiplayerStats::Metric& subMetric = componentStats.m_propertyUpdatesRecv[index];
  113. callsPerSecond = 0.0f;
  114. bytesPerSecond = 0.0f;
  115. AccumulatePerSecondValues(stats, subMetric, callsPerSecond, bytesPerSecond);
  116. DrawMetricsRow(propertyName, false, subMetric.m_totalCalls, subMetric.m_totalBytes, callsPerSecond, bytesPerSecond);
  117. }
  118. ImGui::TreePop();
  119. }
  120. }
  121. {
  122. const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsSentMetrics(netComponentId);
  123. float callsPerSecond = 0.0f;
  124. float bytesPerSecond = 0.0f;
  125. AccumulatePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond);
  126. if (DrawMetricsRow("RemoteProcedures Sent", true, metric.m_totalCalls, metric.m_totalBytes, callsPerSecond, bytesPerSecond))
  127. {
  128. const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[aznumeric_cast<AZStd::size_t>(netComponentId)];
  129. for (AZStd::size_t index = 0; index < componentStats.m_rpcsSent.size(); ++index)
  130. {
  131. const RpcIndex rpcIndex = aznumeric_cast<RpcIndex>(index);
  132. const char* rpcName = componentRegistry->GetComponentRpcName(netComponentId, rpcIndex);
  133. const MultiplayerStats::Metric& subMetric = componentStats.m_rpcsSent[index];
  134. callsPerSecond = 0.0f;
  135. bytesPerSecond = 0.0f;
  136. AccumulatePerSecondValues(stats, subMetric, callsPerSecond, bytesPerSecond);
  137. DrawMetricsRow(rpcName, false, subMetric.m_totalCalls, subMetric.m_totalBytes, callsPerSecond, bytesPerSecond);
  138. }
  139. ImGui::TreePop();
  140. }
  141. }
  142. {
  143. const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsRecvMetrics(netComponentId);
  144. float callsPerSecond = 0.0f;
  145. float bytesPerSecond = 0.0f;
  146. AccumulatePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond);
  147. if (DrawMetricsRow("RemoteProcedures Recv", true, metric.m_totalCalls, metric.m_totalBytes, callsPerSecond, bytesPerSecond))
  148. {
  149. const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[aznumeric_cast<AZStd::size_t>(netComponentId)];
  150. for (AZStd::size_t index = 0; index < componentStats.m_rpcsRecv.size(); ++index)
  151. {
  152. const RpcIndex rpcIndex = aznumeric_cast<RpcIndex>(index);
  153. const char* rpcName = componentRegistry->GetComponentRpcName(netComponentId, rpcIndex);
  154. const MultiplayerStats::Metric& subMetric = componentStats.m_rpcsRecv[index];
  155. callsPerSecond = 0.0f;
  156. bytesPerSecond = 0.0f;
  157. AccumulatePerSecondValues(stats, subMetric, callsPerSecond, bytesPerSecond);
  158. DrawMetricsRow(rpcName, false, subMetric.m_totalCalls, subMetric.m_totalBytes, callsPerSecond, bytesPerSecond);
  159. }
  160. ImGui::TreePop();
  161. }
  162. }
  163. }
  164. void MultiplayerDebugMultiplayerMetrics::OnImGuiUpdate()
  165. {
  166. const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
  167. IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
  168. MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
  169. const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
  170. ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType()));
  171. ImGui::Text("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
  172. ImGui::Text("Total client connections: %llu", aznumeric_cast<AZ::u64>(stats.m_clientConnectionCount));
  173. ImGui::Text("Total server connections: %llu", aznumeric_cast<AZ::u64>(stats.m_serverConnectionCount));
  174. ImGui::NewLine();
  175. static ImGuiTableFlags flags = ImGuiTableFlags_BordersV
  176. | ImGuiTableFlags_BordersOuterH
  177. | ImGuiTableFlags_Resizable
  178. | ImGuiTableFlags_RowBg
  179. | ImGuiTableFlags_NoBordersInBody;
  180. if (ImGui::BeginTable("Multiplayer Metrics", 5, flags))
  181. {
  182. // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
  183. ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
  184. ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
  185. ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
  186. ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
  187. ImGui::TableSetupColumn("Bytes/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
  188. ImGui::TableHeadersRow();
  189. if (DrawSummaryRow("Totals", stats))
  190. {
  191. for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index)
  192. {
  193. const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
  194. using StringLabel = AZStd::fixed_string<128>;
  195. const StringLabel gemName = componentRegistry->GetComponentGemName(netComponentId);
  196. const StringLabel componentName = componentRegistry->GetComponentName(netComponentId);
  197. const StringLabel label = gemName + "::" + componentName;
  198. if (DrawComponentRow(label.c_str(), stats, netComponentId))
  199. {
  200. DrawComponentDetails(stats, netComponentId);
  201. ImGui::TreePop();
  202. }
  203. }
  204. }
  205. ImGui::EndTable();
  206. ImGui::NewLine();
  207. }
  208. ImGui::End();
  209. }
  210. #endif
  211. }