Brought over LY legacy imgui code
Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "MultiplayerDebugByteReporter.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <AzCore/std/sort.h>
|
||||
|
||||
namespace MultiplayerDiagnostics
|
||||
{
|
||||
void MultiplayerDebugByteReporter::ReportBytes(size_t byteSize)
|
||||
{
|
||||
m_count++;
|
||||
m_totalBytes += byteSize;
|
||||
m_totalBytesThisSecond += byteSize;
|
||||
m_minBytes = AZStd::min(m_minBytes, byteSize);
|
||||
m_maxBytes = AZStd::max(m_maxBytes, byteSize);
|
||||
}
|
||||
|
||||
void MultiplayerDebugByteReporter::AggregateBytes(size_t byteSize)
|
||||
{
|
||||
m_aggregateBytes += byteSize;
|
||||
}
|
||||
|
||||
void MultiplayerDebugByteReporter::ReportAggregateBytes()
|
||||
{
|
||||
ReportBytes(m_aggregateBytes);
|
||||
m_aggregateBytes = 0;
|
||||
}
|
||||
|
||||
float MultiplayerDebugByteReporter::GetAverageBytes() const
|
||||
{
|
||||
if (m_count == 0)
|
||||
{
|
||||
AZ_Warning("MultiplayerDebugByteReporter", m_totalBytes == 0, "Attempted to average bytes with a zero count.");
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return (1.0f * m_totalBytes) / m_count;
|
||||
}
|
||||
|
||||
size_t MultiplayerDebugByteReporter::GetMaxBytes() const
|
||||
{
|
||||
return m_maxBytes;
|
||||
}
|
||||
|
||||
size_t MultiplayerDebugByteReporter::GetMinBytes() const
|
||||
{
|
||||
return m_minBytes;
|
||||
}
|
||||
|
||||
size_t MultiplayerDebugByteReporter::GetTotalBytes() const
|
||||
{
|
||||
return m_totalBytes;
|
||||
}
|
||||
|
||||
float MultiplayerDebugByteReporter::GetKbitsPerSecond()
|
||||
{
|
||||
auto now = AZStd::chrono::monotonic_clock::now();
|
||||
|
||||
// Check the amount of time elapsed and update totals if necessary.
|
||||
// Time here is measured in whole seconds from the epoch, providing synchronization in
|
||||
// reporting intervals across all byte reporters.
|
||||
AZStd::chrono::seconds nowSeconds = AZStd::chrono::duration_cast<AZStd::chrono::seconds>(now.time_since_epoch());
|
||||
AZStd::chrono::seconds secondsSinceLastUpdate = nowSeconds -
|
||||
AZStd::chrono::duration_cast<AZStd::chrono::seconds>(m_lastUpdateTime.time_since_epoch());
|
||||
if (secondsSinceLastUpdate.count())
|
||||
{
|
||||
// normalize over elapsed milliseconds
|
||||
const int k_millisecondsPerSecond = 1000;
|
||||
auto msSinceLastUpdate = AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(now - m_lastUpdateTime);
|
||||
m_totalBytesLastSecond = k_millisecondsPerSecond * (1.f * m_totalBytesThisSecond / msSinceLastUpdate.count());
|
||||
m_totalBytesThisSecond = 0;
|
||||
m_lastUpdateTime = now;
|
||||
}
|
||||
|
||||
const float k_bitsPerByte = 8.0f;
|
||||
const int k_bitsPerKilobit = 1024;
|
||||
return k_bitsPerByte * m_totalBytesLastSecond / k_bitsPerKilobit;
|
||||
}
|
||||
|
||||
void MultiplayerDebugByteReporter::Combine(const MultiplayerDebugByteReporter& other)
|
||||
{
|
||||
m_count += other.m_count;
|
||||
m_totalBytes += other.m_totalBytes;
|
||||
m_totalBytesThisSecond += other.m_totalBytesThisSecond;
|
||||
m_minBytes = AZStd::GetMin(m_minBytes, other.m_minBytes);
|
||||
m_maxBytes = AZStd::GetMax(m_maxBytes, other.m_maxBytes);
|
||||
}
|
||||
|
||||
void MultiplayerDebugByteReporter::Reset()
|
||||
{
|
||||
m_count = 0;
|
||||
m_totalBytes = 0;
|
||||
m_totalBytesThisSecond = 0;
|
||||
m_totalBytesLastSecond = 0;
|
||||
m_minBytes = std::numeric_limits<decltype(m_minBytes)>::max();
|
||||
m_maxBytes = 0;
|
||||
m_aggregateBytes = 0;
|
||||
}
|
||||
|
||||
void ComponentReporter::ReportField(const char* fieldName, size_t byteSize)
|
||||
{
|
||||
MultiplayerDebugByteReporter::AggregateBytes(byteSize);
|
||||
m_fieldReports[fieldName].ReportBytes(byteSize);
|
||||
}
|
||||
|
||||
void ComponentReporter::ReportFragmentEnd()
|
||||
{
|
||||
MultiplayerDebugByteReporter::ReportAggregateBytes();
|
||||
m_componentDirtyBytes.ReportAggregateBytes();
|
||||
}
|
||||
|
||||
AZStd::vector<ComponentReporter::Report> ComponentReporter::GetFieldReports()
|
||||
{
|
||||
AZStd::vector<Report> copy;
|
||||
for (auto field = m_fieldReports.begin(); field != m_fieldReports.end(); ++field)
|
||||
{
|
||||
copy.emplace_back(field->first, &field->second);
|
||||
}
|
||||
|
||||
auto sortByFrequency = [](const Report& a, const Report& b)
|
||||
{
|
||||
return a.second->GetTotalCount() > b.second->GetTotalCount();
|
||||
};
|
||||
|
||||
AZStd::sort(copy.begin(), copy.end(), sortByFrequency);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void ComponentReporter::Combine(const ComponentReporter& other)
|
||||
{
|
||||
MultiplayerDebugByteReporter::Combine(other);
|
||||
|
||||
for (const auto& fieldIter : other.m_fieldReports)
|
||||
{
|
||||
m_fieldReports[fieldIter.first].Combine(fieldIter.second);
|
||||
}
|
||||
|
||||
m_componentDirtyBytes.Combine(other.m_componentDirtyBytes);
|
||||
}
|
||||
|
||||
void EntityReporter::ReportField(AZ::u32 index, const char* componentName,
|
||||
const char* fieldName, size_t byteSize)
|
||||
{
|
||||
if (m_currentComponentReport == nullptr)
|
||||
{
|
||||
std::stringstream component;
|
||||
component << "[" << std::setw(2) << std::setfill('0') << static_cast<int>(index) << "]" << " " << componentName;
|
||||
m_currentComponentReport = &m_componentReports[component.str().c_str()];
|
||||
}
|
||||
|
||||
m_currentComponentReport->ReportField(fieldName, byteSize);
|
||||
MultiplayerDebugByteReporter::AggregateBytes(byteSize);
|
||||
}
|
||||
|
||||
void EntityReporter::ReportDirtyBits(AZ::u32 index, const char* componentName, size_t byteSize)
|
||||
{
|
||||
const char* const prefix = "MB::";
|
||||
if (strncmp(prefix, componentName, 4) == 0)
|
||||
{
|
||||
componentName += strlen(prefix);
|
||||
}
|
||||
|
||||
if (m_currentComponentReport == nullptr)
|
||||
{
|
||||
std::stringstream component;
|
||||
component << "[" << std::setw(2) << std::setfill('0') << static_cast<int>(index) << "]" << " " << componentName;
|
||||
m_currentComponentReport = &m_componentReports[component.str().c_str()];
|
||||
}
|
||||
|
||||
m_currentComponentReport->ReportDirtyBits(byteSize);
|
||||
m_gdeDirtyBytes.AggregateBytes(byteSize);
|
||||
}
|
||||
|
||||
void EntityReporter::ReportFragmentEnd()
|
||||
{
|
||||
if (m_currentComponentReport)
|
||||
{
|
||||
m_currentComponentReport->ReportFragmentEnd();
|
||||
m_currentComponentReport = nullptr;
|
||||
}
|
||||
|
||||
m_gdeDirtyBytes.ReportAggregateBytes();
|
||||
MultiplayerDebugByteReporter::ReportAggregateBytes();
|
||||
}
|
||||
|
||||
void EntityReporter::Combine(const EntityReporter& other)
|
||||
{
|
||||
MultiplayerDebugByteReporter::Combine(other);
|
||||
|
||||
for (const auto& componentIter : other.m_componentReports)
|
||||
{
|
||||
m_componentReports[componentIter.first].Combine(componentIter.second);
|
||||
}
|
||||
|
||||
m_gdeDirtyBytes.Combine(other.m_gdeDirtyBytes);
|
||||
}
|
||||
|
||||
void EntityReporter::Reset()
|
||||
{
|
||||
MultiplayerDebugByteReporter::Reset();
|
||||
|
||||
m_componentReports.clear();
|
||||
m_gdeDirtyBytes.Reset();
|
||||
}
|
||||
|
||||
AZStd::map<AZStd::string, ComponentReporter>& EntityReporter::GetComponentReports()
|
||||
{
|
||||
return m_componentReports;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/chrono/chrono.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
namespace MultiplayerDiagnostics
|
||||
{
|
||||
class MultiplayerDebugByteReporter
|
||||
{
|
||||
public:
|
||||
MultiplayerDebugByteReporter() { MultiplayerDebugByteReporter::Reset(); }
|
||||
virtual ~MultiplayerDebugByteReporter() = default;
|
||||
|
||||
void ReportBytes(size_t byteSize);
|
||||
void AggregateBytes(size_t byteSize);
|
||||
void ReportAggregateBytes();
|
||||
|
||||
float GetAverageBytes() const;
|
||||
size_t GetMaxBytes() const;
|
||||
size_t GetMinBytes() const;
|
||||
size_t GetTotalBytes() const;
|
||||
float GetKbitsPerSecond();
|
||||
|
||||
void Combine(const MultiplayerDebugByteReporter& other);
|
||||
virtual void Reset();
|
||||
|
||||
size_t GetTotalCount() const { return m_count; }
|
||||
|
||||
private:
|
||||
size_t m_count;
|
||||
size_t m_totalBytes;
|
||||
size_t m_totalBytesThisSecond;
|
||||
float m_totalBytesLastSecond;
|
||||
size_t m_minBytes;
|
||||
size_t m_maxBytes;
|
||||
size_t m_aggregateBytes;
|
||||
|
||||
AZStd::chrono::monotonic_clock::time_point m_lastUpdateTime;
|
||||
};
|
||||
|
||||
class ComponentReporter : public MultiplayerDebugByteReporter
|
||||
{
|
||||
public:
|
||||
ComponentReporter() = default;
|
||||
|
||||
void ReportField(const char* fieldName, size_t byteSize);
|
||||
void ReportDirtyBits(size_t byteSize) { m_componentDirtyBytes.AggregateBytes(byteSize); }
|
||||
void ReportFragmentEnd();
|
||||
|
||||
using Report = AZStd::pair<AZStd::string, MultiplayerDebugByteReporter*>;
|
||||
AZStd::vector<Report> GetFieldReports();
|
||||
AZStd::size_t GetTotalDirtyBits() const { return m_componentDirtyBytes.GetTotalBytes(); }
|
||||
float GetAvgDirtyBits() const { return m_componentDirtyBytes.GetAverageBytes(); }
|
||||
|
||||
void Combine(const ComponentReporter& other);
|
||||
|
||||
private:
|
||||
AZStd::map<AZStd::string, MultiplayerDebugByteReporter> m_fieldReports;
|
||||
MultiplayerDebugByteReporter m_componentDirtyBytes;
|
||||
};
|
||||
|
||||
class EntityReporter : public MultiplayerDebugByteReporter
|
||||
{
|
||||
public:
|
||||
EntityReporter() = default;
|
||||
|
||||
void ReportField(AZ::u32 index, const char* componentName, const char* fieldName, size_t byteSize);
|
||||
void ReportDirtyBits(AZ::u32 index, const char* componentName, size_t byteSize);
|
||||
void ReportFragmentEnd();
|
||||
|
||||
void Combine(const EntityReporter& other);
|
||||
void Reset() override;
|
||||
|
||||
AZStd::map<AZStd::string, ComponentReporter>& GetComponentReports();
|
||||
AZStd::size_t GetTotalDirtyBits() const { return m_gdeDirtyBytes.GetTotalBytes(); }
|
||||
float GetAvgDirtyBits() const { return m_gdeDirtyBytes.GetAverageBytes(); }
|
||||
|
||||
private:
|
||||
ComponentReporter* m_currentComponentReport = nullptr;
|
||||
AZStd::map<AZStd::string, ComponentReporter> m_componentReports;
|
||||
MultiplayerDebugByteReporter m_gdeDirtyBytes;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "MultiplayerDebugPerEntityReporter.h"
|
||||
#include <GridMate/Replica/ReplicaChunk.h>
|
||||
#include <GridMate/Replica/ReplicaChunkDescriptor.h>
|
||||
#include <GridMate/Replica/RemoteProcedureCall.h>
|
||||
|
||||
#if defined(IMGUI_ENABLED)
|
||||
#include <imgui/imgui.h>
|
||||
#endif
|
||||
|
||||
namespace MultiplayerDiagnostics
|
||||
{
|
||||
#if defined(IMGUI_ENABLED)
|
||||
static const ImVec4 k_ImGuiTomato = ImVec4(1.0f, 0.4f, 0.3f, 1.0f);
|
||||
static const ImVec4 k_ImGuiKhaki = ImVec4(0.9f, 0.8f, 0.5f, 1.0f);
|
||||
static const ImVec4 k_ImGuiCyan = ImVec4(0.5f, 1.0f, 1.0f, 1.0f);
|
||||
static const ImVec4 k_ImGuiDusk = ImVec4(0.7f, 0.7f, 1.0f, 1.0f);
|
||||
static const ImVec4 k_ImGuiWhite = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
template <typename Reporter>
|
||||
bool ReplicatedStateTreeNode(const AZStd::string& name, Reporter& report, const ImVec4& color, int depth = 0)
|
||||
{
|
||||
const int defaultPadAmount = 55;
|
||||
const int depthReduction = 3;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
|
||||
const bool expanded = ImGui::TreeNode(name.c_str(),
|
||||
"%-*s %7.2f kbps %7.2f B Avg. %4zu B Max %10zu B Payload",
|
||||
defaultPadAmount - depthReduction * depth,
|
||||
name.c_str(),
|
||||
report.GetKbitsPerSecond(),
|
||||
report.GetAverageBytes(),
|
||||
report.GetMaxBytes(),
|
||||
report.GetTotalBytes());
|
||||
ImGui::PopStyleColor();
|
||||
return expanded;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
void DisplayReplicatedStateReport(AZStd::map<AZStd::string, ComponentReporter>& componentReports, float kbpsWarn, float maxWarn)
|
||||
{
|
||||
for (auto& componentPair : componentReports)
|
||||
{
|
||||
ImGui::Separator();
|
||||
ComponentReporter& componentReport = componentPair.second;
|
||||
|
||||
if (ReplicatedStateTreeNode(componentPair.first, componentReport, k_ImGuiCyan, 1))
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::Columns(6, "replicated_field_columns");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("kbps");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Avg. Bytes");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Min Bytes");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Max Bytes");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Total Bytes");
|
||||
ImGui::NextColumn();
|
||||
|
||||
auto fieldReports = componentReport.GetFieldReports();
|
||||
for (auto& fieldPair : fieldReports)
|
||||
{
|
||||
MultiplayerDebugByteReporter& fieldReport = *fieldPair.second;
|
||||
const float kbitsLastSecond = fieldReport.GetKbitsPerSecond();
|
||||
|
||||
const ImVec4* textColor = &k_ImGuiWhite;
|
||||
if (fieldReport.GetMaxBytes() > maxWarn)
|
||||
{
|
||||
textColor = &k_ImGuiKhaki;
|
||||
}
|
||||
|
||||
if (kbitsLastSecond > kbpsWarn)
|
||||
{
|
||||
textColor = &k_ImGuiTomato;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, *textColor);
|
||||
|
||||
ImGui::Text("%s", fieldPair.first.c_str());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%.2f", kbitsLastSecond);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%.2f", fieldReport.GetAverageBytes());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%zu", fieldReport.GetMinBytes());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%zu", fieldReport.GetMaxBytes());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%zu", fieldReport.GetTotalBytes());
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MultiplayerDebugPerEntityReporter::MultiplayerDebugPerEntityReporter ()
|
||||
{
|
||||
GridMate::Debug::CarrierDrillerBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
MultiplayerDebugPerEntityReporter::~MultiplayerDebugPerEntityReporter()
|
||||
{
|
||||
GridMate::Debug::ReplicaDrillerBus::Handler::BusDisconnect();
|
||||
GridMate::Debug::CarrierDrillerBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnReceiveReplicaBegin(GridMate::Replica*, const void*, size_t)
|
||||
{
|
||||
m_currentReceivingEntityReport.Reset();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnReceiveReplicaEnd(GridMate::Replica* replica)
|
||||
{
|
||||
m_receivingEntityReports[replica->GetDebugName()].Combine(m_currentReceivingEntityReport);
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnReceiveReplicaChunkEnd(GridMate::ReplicaChunkBase* chunk, AZ::u32 chunkIndex)
|
||||
{
|
||||
AZ_UNUSED(chunk);
|
||||
AZ_UNUSED(chunkIndex);
|
||||
m_currentReceivingEntityReport.ReportFragmentEnd();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnReceiveDataSet(GridMate::ReplicaChunkBase* chunk, AZ::u32 chunkIndex,
|
||||
GridMate::DataSetBase* dataSet, GridMate::PeerId, GridMate::PeerId, const void*, size_t len)
|
||||
{
|
||||
m_currentReceivingEntityReport.ReportField(chunkIndex, chunk->GetDescriptor()->GetChunkName(), chunk->GetDescriptor()->GetDataSetName(chunk, dataSet), len);
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnReceiveRpc (GridMate::ReplicaChunkBase* chunk,
|
||||
AZ::u32 chunkIndex,
|
||||
GridMate::Internal::RpcRequest* rpc,
|
||||
GridMate::PeerId from,
|
||||
GridMate::PeerId to,
|
||||
const void* data,
|
||||
size_t len)
|
||||
{
|
||||
AZ_UNUSED( from );
|
||||
AZ_UNUSED( to );
|
||||
AZ_UNUSED( data );
|
||||
|
||||
m_currentReceivingEntityReport.ReportField(chunkIndex, chunk->GetDescriptor()->GetChunkName(), chunk->GetDescriptor()->GetRpcName(chunk, rpc->m_rpc), len);
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnSendReplicaBegin (GridMate::Replica*)
|
||||
{
|
||||
m_currentSendingEntityReport.Reset();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnSendReplicaEnd (GridMate::Replica* replica, const void*, size_t)
|
||||
{
|
||||
m_sendingEntityReports[replica->GetDebugName()].Combine(m_currentSendingEntityReport);
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnSendReplicaChunkEnd (GridMate::ReplicaChunkBase* chunk,
|
||||
AZ::u32 chunkIndex,
|
||||
const void*,
|
||||
size_t)
|
||||
{
|
||||
AZ_UNUSED(chunk);
|
||||
AZ_UNUSED(chunkIndex);
|
||||
m_currentSendingEntityReport.ReportFragmentEnd();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnSendDataSet (GridMate::ReplicaChunkBase* chunk,
|
||||
AZ::u32 chunkIndex,
|
||||
GridMate::DataSetBase* dataSet,
|
||||
GridMate::PeerId,
|
||||
GridMate::PeerId,
|
||||
const void*,
|
||||
size_t len)
|
||||
{
|
||||
m_currentSendingEntityReport.ReportField(chunkIndex, chunk->GetDescriptor()->GetChunkName(), chunk->GetDescriptor()->GetDataSetName(chunk, dataSet), len);
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnSendRpc (GridMate::ReplicaChunkBase* chunk,
|
||||
AZ::u32 chunkIndex,
|
||||
GridMate::Internal::RpcRequest* rpc,
|
||||
GridMate::PeerId,
|
||||
GridMate::PeerId,
|
||||
const void*,
|
||||
size_t len)
|
||||
{
|
||||
m_currentSendingEntityReport.ReportField(chunkIndex, chunk->GetDescriptor()->GetChunkName(), chunk->GetDescriptor()->GetRpcName(chunk, rpc->m_rpc), len);
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnIncomingConnection (GridMate::Carrier*, GridMate::ConnectionID)
|
||||
{
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnFailedToConnect (GridMate::Carrier*,
|
||||
GridMate::ConnectionID,
|
||||
GridMate::CarrierDisconnectReason)
|
||||
{
|
||||
m_lastSecondStats.clear();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnConnectionEstablished (GridMate::Carrier*, GridMate::ConnectionID)
|
||||
{
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnDisconnect (GridMate::Carrier*,
|
||||
GridMate::ConnectionID,
|
||||
GridMate::CarrierDisconnectReason)
|
||||
{
|
||||
/*
|
||||
* CarrierDrillerBus doesn't provide enough information to correctly keep track of network traffic for all peers.
|
||||
* This is a work around until that is fixed to at least not over report the bandwidth amount.
|
||||
*/
|
||||
m_lastSecondStats.clear();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnDriverError (GridMate::Carrier*,
|
||||
GridMate::ConnectionID,
|
||||
const GridMate::DriverError&)
|
||||
{
|
||||
m_lastSecondStats.clear();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnSecurityError (GridMate::Carrier*,
|
||||
GridMate::ConnectionID,
|
||||
const GridMate::SecurityError&)
|
||||
{
|
||||
m_lastSecondStats.clear();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnUpdateStatistics (const GridMate::string& address,
|
||||
const GridMate::TrafficControl::Statistics&,
|
||||
const GridMate::TrafficControl::Statistics&,
|
||||
const GridMate::TrafficControl::Statistics& effectiveLastSecond,
|
||||
const GridMate::TrafficControl::Statistics&)
|
||||
{
|
||||
m_lastSecondStats[address] = effectiveLastSecond;
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::OnConnectionStateChanged (GridMate::Carrier*,
|
||||
GridMate::ConnectionID,
|
||||
GridMate::Carrier::ConnectionStates)
|
||||
{
|
||||
m_lastSecondStats.clear();
|
||||
}
|
||||
|
||||
void MultiplayerDebugPerEntityReporter::UpdateTrafficStatistics()
|
||||
{
|
||||
#if defined(IMGUI_ENABLED)
|
||||
AZ::u32 dataReceived = 0, dataSent = 0;
|
||||
|
||||
for (auto& perConnection : m_lastSecondStats)
|
||||
{
|
||||
dataReceived += perConnection.second.m_dataReceived;
|
||||
dataSent += perConnection.second.m_dataSend;
|
||||
}
|
||||
|
||||
if (dataReceived !=0 || dataSent != 0)
|
||||
{
|
||||
ImGui::Text("Total bandwidth: Sent %u kbps Received %u kbps.", dataSent * 8 / 1000, dataReceived * 8 / 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("Total bandwidth: Sent -- kbps Received -- kbps.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
void MultiplayerDebugPerEntityReporter::OnImGuiUpdate()
|
||||
{
|
||||
#if defined(IMGUI_ENABLED)
|
||||
if (ImGui::BeginMainMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("GridMate"))
|
||||
{
|
||||
if (m_showServerReportWindow)
|
||||
{
|
||||
if (ImGui::MenuItem("Hide Multiplayer Analytics Window"))
|
||||
{
|
||||
m_showServerReportWindow = false;
|
||||
}
|
||||
}
|
||||
else if (ImGui::MenuItem("Show Multiplayer Analytics Window"))
|
||||
{
|
||||
m_showServerReportWindow = true;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
if (m_showServerReportWindow)
|
||||
{
|
||||
if (ImGui::Begin("Multiplayer Analytics", &m_showServerReportWindow))
|
||||
{
|
||||
// General carrier stats
|
||||
UpdateTrafficStatistics();
|
||||
|
||||
if (ImGui::Checkbox("Analyze network traffic", &m_isTrackingMessages))
|
||||
{
|
||||
if (m_isTrackingMessages)
|
||||
{
|
||||
GridMate::Debug::ReplicaDrillerBus::Handler::BusConnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
GridMate::Debug::ReplicaDrillerBus::Handler::BusDisconnect();
|
||||
|
||||
m_currentReceivingEntityReport.Reset();
|
||||
m_receivingEntityReports.clear();
|
||||
|
||||
m_currentSendingEntityReport.Reset();
|
||||
m_sendingEntityReports.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_isTrackingMessages)
|
||||
{
|
||||
ImGui::Separator();
|
||||
|
||||
static ImGuiTextFilter filter;
|
||||
filter.Draw();
|
||||
|
||||
if (ImGui::CollapsingHeader("Received replicas per type"))
|
||||
{
|
||||
for (auto& entityPair : m_receivingEntityReports)
|
||||
{
|
||||
if (!filter.PassFilter(entityPair.first.c_str()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ReplicatedStateTreeNode(entityPair.first, entityPair.second, k_ImGuiDusk))
|
||||
{
|
||||
DisplayReplicatedStateReport(entityPair.second.GetComponentReports(), m_replicatedStateKbpsWarn, m_replicatedStateMaxSizeWarn);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Sent replicas per type"))
|
||||
{
|
||||
for (auto& entityPair : m_sendingEntityReports)
|
||||
{
|
||||
if (!filter.PassFilter(entityPair.first.c_str()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ReplicatedStateTreeNode(entityPair.first, entityPair.second, k_ImGuiDusk))
|
||||
{
|
||||
DisplayReplicatedStateReport(entityPair.second.GetComponentReports(), m_replicatedStateKbpsWarn, m_replicatedStateMaxSizeWarn);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <GridMate/Drillers/ReplicaDriller.h>
|
||||
#include "MultiplayerDebugByteReporter.h"
|
||||
#include <GridMate/Drillers/CarrierDriller.h>
|
||||
|
||||
namespace MultiplayerDiagnostics
|
||||
{
|
||||
/**
|
||||
* \brief GridMate network live analysis tool via ImGui.
|
||||
*/
|
||||
class MultiplayerDebugPerEntityReporter
|
||||
: public GridMate::Debug::ReplicaDrillerBus::Handler
|
||||
, public GridMate::Debug::CarrierDrillerBus::Handler
|
||||
{
|
||||
public:
|
||||
MultiplayerDebugPerEntityReporter();
|
||||
virtual ~MultiplayerDebugPerEntityReporter();
|
||||
|
||||
// main update loop
|
||||
void OnImGuiUpdate();
|
||||
|
||||
// ReplicaDrillerBus - receive
|
||||
|
||||
void OnReceiveReplicaBegin(GridMate::Replica* replica, const void* data, size_t len) override;
|
||||
void OnReceiveReplicaEnd(GridMate::Replica* replica) override;
|
||||
void OnReceiveReplicaChunkEnd(GridMate::ReplicaChunkBase* chunk, AZ::u32 chunkIndex) override;
|
||||
void OnReceiveDataSet(GridMate::ReplicaChunkBase* chunk, AZ::u32 chunkIndex, GridMate::DataSetBase* dataSet, GridMate::PeerId from, GridMate::PeerId to, const void* data, size_t len) override;
|
||||
void OnReceiveRpc(GridMate::ReplicaChunkBase* chunk, AZ::u32 chunkIndex, GridMate::Internal::RpcRequest* rpc, GridMate::PeerId from, GridMate::PeerId to, const void* data, size_t len) override;
|
||||
|
||||
// ReplicaDrillerBus - sending
|
||||
|
||||
void OnSendReplicaBegin(GridMate::Replica* replica) override;
|
||||
void OnSendReplicaEnd(GridMate::Replica* replica, const void* data, size_t len) override;
|
||||
void OnSendReplicaChunkEnd(GridMate::ReplicaChunkBase* chunk, AZ::u32 chunkIndex, const void* data, size_t len) override;
|
||||
void OnSendDataSet(GridMate::ReplicaChunkBase* chunk, AZ::u32 chunkIndex, GridMate::DataSetBase* dataSet, GridMate::PeerId from, GridMate::PeerId to, const void* data, size_t len) override;
|
||||
void OnSendRpc(GridMate::ReplicaChunkBase* chunk, AZ::u32 chunkIndex, GridMate::Internal::RpcRequest* rpc, GridMate::PeerId from, GridMate::PeerId to, const void* data, size_t len) override;
|
||||
|
||||
// CarrierDrillerBus
|
||||
void OnIncomingConnection (GridMate::Carrier* carrier, GridMate::ConnectionID id) override;
|
||||
void OnFailedToConnect (GridMate::Carrier* carrier,
|
||||
GridMate::ConnectionID id,
|
||||
GridMate::CarrierDisconnectReason reason) override;
|
||||
void OnConnectionEstablished (GridMate::Carrier* carrier, GridMate::ConnectionID id) override;
|
||||
void OnDisconnect (GridMate::Carrier* carrier,
|
||||
GridMate::ConnectionID id,
|
||||
GridMate::CarrierDisconnectReason reason) override;
|
||||
void OnDriverError (GridMate::Carrier* carrier,
|
||||
GridMate::ConnectionID id,
|
||||
const GridMate::DriverError& error) override;
|
||||
void OnSecurityError (GridMate::Carrier* carrier,
|
||||
GridMate::ConnectionID id,
|
||||
const GridMate::SecurityError& error) override;
|
||||
void OnUpdateStatistics (const GridMate::string& address,
|
||||
const GridMate::TrafficControl::Statistics& lastSecond,
|
||||
const GridMate::TrafficControl::Statistics& lifeTime,
|
||||
const GridMate::TrafficControl::Statistics& effectiveLastSecond,
|
||||
const GridMate::TrafficControl::Statistics& effectiveLifeTime) override;
|
||||
void OnConnectionStateChanged (GridMate::Carrier* carrier,
|
||||
GridMate::ConnectionID id,
|
||||
GridMate::Carrier::ConnectionStates newState) override;
|
||||
|
||||
private:
|
||||
|
||||
bool m_showServerReportWindow = false;
|
||||
bool m_isTrackingMessages = false;
|
||||
|
||||
AZStd::map<AZStd::string, EntityReporter> m_sendingEntityReports{};
|
||||
EntityReporter m_currentSendingEntityReport;
|
||||
|
||||
AZStd::map<AZStd::string, EntityReporter> m_receivingEntityReports{};
|
||||
EntityReporter m_currentReceivingEntityReport;
|
||||
|
||||
float m_replicatedStateKbpsWarn = 10.f;
|
||||
float m_replicatedStateMaxSizeWarn = 30.f;
|
||||
|
||||
void UpdateTrafficStatistics();
|
||||
AZStd::map<GridMate::string, GridMate::TrafficControl::Statistics> m_lastSecondStats;
|
||||
};
|
||||
}
|
||||
@@ -53,6 +53,11 @@ namespace Multiplayer
|
||||
#endif
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::OnImGuiInitialize()
|
||||
{
|
||||
m_reporter = AZStd::make_unique<MultiplayerDiagnostics::MultiplayerDebugPerEntityReporter>();
|
||||
}
|
||||
|
||||
#ifdef IMGUI_ENABLED
|
||||
void MultiplayerDebugSystemComponent::OnImGuiMainMenuUpdate()
|
||||
{
|
||||
@@ -318,6 +323,12 @@ namespace Multiplayer
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_reporter)
|
||||
{
|
||||
m_reporter->OnImGuiUpdate();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <Debug/MultiplayerDebugPerEntityReporter.h>
|
||||
|
||||
#ifdef IMGUI_ENABLED
|
||||
# include <imgui/imgui.h>
|
||||
@@ -42,6 +43,7 @@ namespace Multiplayer
|
||||
#ifdef IMGUI_ENABLED
|
||||
//! ImGui::ImGuiUpdateListenerBus overrides
|
||||
//! @{
|
||||
void OnImGuiInitialize() override;
|
||||
void OnImGuiMainMenuUpdate() override;
|
||||
void OnImGuiUpdate() override;
|
||||
//! @}
|
||||
@@ -49,5 +51,7 @@ namespace Multiplayer
|
||||
private:
|
||||
bool m_displayNetworkingStats = false;
|
||||
bool m_displayMultiplayerStats = false;
|
||||
|
||||
AZStd::unique_ptr<MultiplayerDiagnostics::MultiplayerDebugPerEntityReporter> m_reporter;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Source/Debug/MultiplayerDebugByteReporter.cpp
|
||||
Source/Debug/MultiplayerDebugByteReporter.h
|
||||
Source/Debug/MultiplayerDebugPerEntityReporter.cpp
|
||||
Source/Debug/MultiplayerDebugPerEntityReporter.h
|
||||
Source/Debug/MultiplayerDebugModule.cpp
|
||||
Source/Debug/MultiplayerDebugModule.h
|
||||
Source/Debug/MultiplayerDebugSystemComponent.cpp
|
||||
|
||||
Reference in New Issue
Block a user