[SIG-Network] Google benchmarks for network hierarchy components.

First google benchmarks for hierarchy components.
This commit is contained in:
Olex Lozitskiy
2021-10-05 08:23:14 -04:00
committed by GitHub
6 changed files with 768 additions and 0 deletions
+4
View File
@@ -179,6 +179,10 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_googletest(
NAME Gem::Multiplayer.Tests
)
ly_add_googlebenchmark(
NAME Gem::Multiplayer.Benchmarks
TARGET Gem::Multiplayer.Tests
)
if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
@@ -193,6 +193,7 @@ namespace Multiplayer
friend class EntityReplicationManager;
friend class HierarchyTests;
friend class HierarchyBenchmarkBase;
};
bool NetworkRoleHasController(NetEntityRole networkRole);
@@ -96,5 +96,7 @@ namespace Multiplayer
//! Set to false when deactivating or otherwise not to be included in hierarchy considerations.
bool m_isHierarchyEnabled = true;
friend class HierarchyBenchmarkBase;
};
}
@@ -0,0 +1,640 @@
/*
* 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
#ifdef HAVE_BENCHMARK
#include <CommonHierarchySetup.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Console/Console.h>
#include <AzCore/Name/Name.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Components/TransformComponent.h>
#include <benchmark/benchmark.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <NetworkEntity/EntityReplication/EntityReplicator.h>
namespace Multiplayer
{
class BenchmarkComponentApplicationRequests : public AZ::ComponentApplicationRequests
{
public:
void RegisterComponentDescriptor([[maybe_unused]] const AZ::ComponentDescriptor* descriptor) override {}
void UnregisterComponentDescriptor([[maybe_unused]] const AZ::ComponentDescriptor* descriptor) override {}
AZ::ComponentApplication* GetApplication() override { return {}; }
void RegisterEntityAddedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
void RegisterEntityRemovedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
void RegisterEntityActivatedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
void RegisterEntityDeactivatedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
void SignalEntityActivated([[maybe_unused]] AZ::Entity* entity) override {}
void SignalEntityDeactivated([[maybe_unused]] AZ::Entity* entity) override {}
bool RemoveEntity([[maybe_unused]] AZ::Entity* entity) override { return {}; }
bool DeleteEntity([[maybe_unused]] const AZ::EntityId& id) override { return {}; }
void EnumerateEntities([[maybe_unused]] const EntityCallback& callback) override {}
AZ::SerializeContext* GetSerializeContext() override { return {}; }
AZ::BehaviorContext* GetBehaviorContext() override { return {}; }
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return {}; }
const char* GetAppRoot() const override { return {}; }
const char* GetEngineRoot() const override { return {}; }
const char* GetExecutableFolder() const override { return {}; }
void QueryApplicationType([[maybe_unused]] AZ::ApplicationTypeQuery& appType) const override {}
AZStd::map<AZ::EntityId, AZ::Entity*> m_entities;
bool AddEntity(AZ::Entity* entity) override
{
m_entities[entity->GetId()] = entity;
return true;
}
AZ::Entity* FindEntity(const AZ::EntityId& id) override
{
const auto iterator = m_entities.find(id);
if (iterator != m_entities.end())
{
return iterator->second;
}
return nullptr;
}
};
class BenchmarkConnectionListener : public AzNetworking::IConnectionListener
{
public:
ConnectResult ValidateConnect([[maybe_unused]] const IpAddress& remoteAddress, [[maybe_unused]] const IPacketHeader& packetHeader, [[maybe_unused]] ISerializer& serializer) override
{
return {};
}
void OnConnect([[maybe_unused]] IConnection* connection) override
{
}
PacketDispatchResult OnPacketReceived([[maybe_unused]] IConnection* connection, [[maybe_unused]] const IPacketHeader& packetHeader, [[maybe_unused]] ISerializer& serializer) override
{
return {};
}
void OnPacketLost([[maybe_unused]] IConnection* connection, [[maybe_unused]] PacketId packetId) override
{
}
void OnDisconnect([[maybe_unused]] IConnection* connection, [[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint) override
{
}
};
class BenchmarkTime : public AZ::ITime
{
public:
AZ::TimeMs GetElapsedTimeMs() const override
{
return {};
}
};
class BenchmarkNetworkTime : public Multiplayer::INetworkTime
{
public:
bool IsTimeRewound() const override
{
return false;
}
HostFrameId GetHostFrameId() const override
{
return {};
}
HostFrameId GetUnalteredHostFrameId() const override
{
return {};
}
void IncrementHostFrameId() override
{
}
AZ::TimeMs GetHostTimeMs() const override
{
return {};
}
float GetHostBlendFactor() const override
{
return 0;
}
AzNetworking::ConnectionId GetRewindingConnectionId() const override
{
return {};
}
void ForceSetTime([[maybe_unused]] HostFrameId frameId, [[maybe_unused]] AZ::TimeMs timeMs) override
{
}
void SyncEntitiesToRewindState([[maybe_unused]] const AZ::Aabb& rewindVolume) override
{
}
void ClearRewoundEntities() override
{
}
void AlterTime([[maybe_unused]] HostFrameId frameId, [[maybe_unused]] AZ::TimeMs timeMs, [[maybe_unused]] float blendFactor, [[maybe_unused]] AzNetworking::ConnectionId rewindConnectionId) override
{
}
};
class BenchmarkMultiplayerConnection : public IConnection
{
public:
BenchmarkMultiplayerConnection(ConnectionId connectionId, const IpAddress& address, [[maybe_unused]] ConnectionRole connectionRole)
: IConnection(connectionId, address)
{
;
}
~BenchmarkMultiplayerConnection() override = default;
bool SendReliablePacket([[maybe_unused]] const IPacket& packet) override
{
return false;
}
PacketId SendUnreliablePacket([[maybe_unused]] const IPacket& packet) override
{
return {};
}
bool WasPacketAcked([[maybe_unused]] PacketId packetId) const override
{
return false;
}
ConnectionState GetConnectionState() const override
{
return {};
}
ConnectionRole GetConnectionRole() const override
{
return {};
}
bool Disconnect([[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint) override
{
return false;
}
void SetConnectionMtu([[maybe_unused]] uint32_t connectionMtu) override
{
}
uint32_t GetConnectionMtu() const override
{
return 0;
}
};
class BenchmarkNetworkEntityManager : public Multiplayer::INetworkEntityManager
{
public:
BenchmarkNetworkEntityManager() : m_authorityTracker(*this) {}
NetworkEntityTracker* GetNetworkEntityTracker() override { return &m_tracker; }
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override { return &m_authorityTracker; }
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override { return &m_multiplayerComponentRegistry; }
HostId GetHostId() const override { return {}; }
EntityList CreateEntitiesImmediate(
[[maybe_unused]] const PrefabEntityId& prefabEntryId,
[[maybe_unused]] NetEntityRole netEntityRole,
[[maybe_unused]] const AZ::Transform& transform,
[[maybe_unused]] AutoActivate autoActivate) override {
return {};
}
EntityList CreateEntitiesImmediate(
[[maybe_unused]] const PrefabEntityId& prefabEntryId,
[[maybe_unused]] NetEntityId netEntityId,
[[maybe_unused]] NetEntityRole netEntityRole,
[[maybe_unused]] AutoActivate autoActivate,
[[maybe_unused]] const AZ::Transform& transform) override {
return {};
}
void SetupNetEntity(
[[maybe_unused]] AZ::Entity* netEntity,
[[maybe_unused]] PrefabEntityId prefabEntityId,
[[maybe_unused]] NetEntityRole netEntityRole) override {}
uint32_t GetEntityCount() const override { return {}; }
void MarkForRemoval(
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
bool IsMarkedForRemoval(
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const override {
return {};
}
void ClearEntityFromRemovalList(
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
void ClearAllEntities() override {}
void AddEntityMarkedDirtyHandler(
[[maybe_unused]] AZ::Event<>::Handler& entityMarkedDirtyHandle) override {}
void AddEntityNotifyChangesHandler(
[[maybe_unused]] AZ::Event<>::Handler& entityNotifyChangesHandle) override {}
void AddEntityExitDomainHandler(
[[maybe_unused]] EntityExitDomainEvent::Handler& entityExitDomainHandler) override {}
void AddControllersActivatedHandler(
[[maybe_unused]] ControllersActivatedEvent::Handler& controllersActivatedHandler) override {}
void AddControllersDeactivatedHandler(
[[maybe_unused]] ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) override {}
void NotifyEntitiesDirtied() override {}
void NotifyEntitiesChanged() override {}
void NotifyControllersActivated(
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
[[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
void NotifyControllersDeactivated(
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
[[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
void HandleLocalRpcMessage(
[[maybe_unused]] NetworkEntityRpcMessage& message) override {}
mutable AZStd::map<NetEntityId, AZ::Entity*> m_networkEntityMap;
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override
{
m_networkEntityMap[netEntityId] = entity;
return NetworkEntityHandle(entity, netEntityId, &m_tracker);
}
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override
{
AZ::Entity* entity = m_networkEntityMap[netEntityId];
return ConstNetworkEntityHandle(entity, &m_tracker);
}
NetEntityId GetNetEntityIdById(const AZ::EntityId& entityId) const override
{
for (const auto& pair : m_networkEntityMap)
{
if (pair.second->GetId() == entityId)
{
return pair.first;
}
}
return InvalidNetEntityId;
}
[[nodiscard]] AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
[[maybe_unused]] const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable,
[[maybe_unused]] const AZ::Transform& transform) override
{
return {};
}
NetworkEntityTracker m_tracker;
NetworkEntityAuthorityTracker m_authorityTracker;
MultiplayerComponentRegistry m_multiplayerComponentRegistry;
};
class BenchmarkMultiplayer : public Multiplayer::IMultiplayer
{
public:
BenchmarkMultiplayer(BenchmarkNetworkEntityManager& manager) : m_manager(manager) {}
MultiplayerAgentType GetAgentType() const override { return {}; }
void InitializeMultiplayer([[maybe_unused]] MultiplayerAgentType state) override {}
bool StartHosting([[maybe_unused]] uint16_t port, [[maybe_unused]] bool isDedicated) override { return {}; }
bool Connect([[maybe_unused]] AZStd::string remoteAddress, [[maybe_unused]] uint16_t port) override { return {}; }
void Terminate([[maybe_unused]] AzNetworking::DisconnectReason reason) override {}
void AddClientDisconnectedHandler([[maybe_unused]] ClientDisconnectedEvent::Handler& handler) override {}
void AddConnectionAcquiredHandler([[maybe_unused]] ConnectionAcquiredEvent::Handler& handler) override {}
void AddSessionInitHandler([[maybe_unused]] SessionInitEvent::Handler& handler) override {}
void AddSessionShutdownHandler([[maybe_unused]] SessionShutdownEvent::Handler& handler) override {}
void SendReadyForEntityUpdates([[maybe_unused]] bool readyForEntityUpdates) override {}
AZ::TimeMs GetCurrentHostTimeMs() const override { return {}; }
float GetCurrentBlendFactor() const override { return {}; }
INetworkTime* GetNetworkTime() override { return {}; }
INetworkEntityManager* GetNetworkEntityManager() override { return &m_manager; }
void SetFilterEntityManager([[maybe_unused]] IFilterEntityManager* entityFilter) override {}
IFilterEntityManager* GetFilterEntityManager() override { return {}; }
BenchmarkNetworkEntityManager& m_manager;
};
class HierarchyBenchmarkBase
: public benchmark::Fixture
, public AllocatorsBase
{
public:
void SetUp(const benchmark::State&) override
{
internalSetUp();
}
void SetUp(benchmark::State&) override
{
internalSetUp();
}
void TearDown(const benchmark::State&) override
{
internalTearDown();
}
void TearDown(benchmark::State&) override
{
internalTearDown();
}
virtual void internalSetUp()
{
SetupAllocator();
AZ::NameDictionary::Create();
m_ComponentApplicationRequests = AZStd::make_unique<BenchmarkComponentApplicationRequests>();
AZ::Interface<AZ::ComponentApplicationRequests>::Register(m_ComponentApplicationRequests.get());
// register components involved in testing
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
m_transformDescriptor.reset(AzFramework::TransformComponent::CreateDescriptor());
m_transformDescriptor->Reflect(m_serializeContext.get());
m_netBindDescriptor.reset(NetBindComponent::CreateDescriptor());
m_netBindDescriptor->Reflect(m_serializeContext.get());
m_hierarchyRootDescriptor.reset(NetworkHierarchyRootComponent::CreateDescriptor());
m_hierarchyRootDescriptor->Reflect(m_serializeContext.get());
m_hierarchyChildDescriptor.reset(NetworkHierarchyChildComponent::CreateDescriptor());
m_hierarchyChildDescriptor->Reflect(m_serializeContext.get());
m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor());
m_netTransformDescriptor->Reflect(m_serializeContext.get());
m_NetworkEntityManager = AZStd::make_unique<BenchmarkNetworkEntityManager>();
m_Multiplayer = AZStd::make_unique<BenchmarkMultiplayer>(*m_NetworkEntityManager);
AZ::Interface<IMultiplayer>::Register(m_Multiplayer.get());
// Create space for replication stats
// Without Multiplayer::RegisterMultiplayerComponents() the stats go to invalid id, which is fine for unit tests
GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0);
m_Time = AZStd::make_unique<BenchmarkTime>();
AZ::Interface<AZ::ITime>::Register(m_Time.get());
m_NetworkTime = AZStd::make_unique<BenchmarkNetworkTime>();
AZ::Interface<INetworkTime>::Register(m_NetworkTime.get());
EXPECT_NE(AZ::Interface<IMultiplayer>::Get()->GetNetworkEntityManager(), nullptr);
const IpAddress address("localhost", 1, ProtocolType::Udp);
m_Connection = AZStd::make_unique<BenchmarkMultiplayerConnection>(ConnectionId{ 1 }, address, ConnectionRole::Connector);
m_ConnectionListener = AZStd::make_unique<BenchmarkConnectionListener>();
m_entityReplicationManager = AZStd::make_unique<EntityReplicationManager>(*m_Connection, *m_ConnectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer);
m_console.reset(aznew AZ::Console());
AZ::Interface<AZ::IConsole>::Register(m_console.get());
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
RegisterMultiplayerComponents();
}
virtual void internalTearDown()
{
AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
m_console.reset();
m_entityReplicationManager.reset();
m_Connection.reset();
m_ConnectionListener.reset();
AZ::Interface<INetworkTime>::Unregister(m_NetworkTime.get());
AZ::Interface<AZ::ITime>::Unregister(m_Time.get());
AZ::Interface<IMultiplayer>::Unregister(m_Multiplayer.get());
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(m_ComponentApplicationRequests.get());
m_Time.reset();
m_NetworkEntityManager.reset();
m_Multiplayer.reset();
m_transformDescriptor.reset();
m_netTransformDescriptor.reset();
m_hierarchyRootDescriptor.reset();
m_hierarchyChildDescriptor.reset();
m_netBindDescriptor.reset();
m_serializeContext.reset();
m_ComponentApplicationRequests.reset();
AZ::NameDictionary::Destroy();
TeardownAllocator();
}
AZStd::unique_ptr<AZ::IConsole> m_console;
AZStd::unique_ptr<BenchmarkComponentApplicationRequests> m_ComponentApplicationRequests;
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_transformDescriptor;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_netBindDescriptor;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyRootDescriptor;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyChildDescriptor;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_netTransformDescriptor;
AZStd::unique_ptr<BenchmarkMultiplayer> m_Multiplayer;
AZStd::unique_ptr<BenchmarkNetworkEntityManager> m_NetworkEntityManager;
AZStd::unique_ptr<BenchmarkTime> m_Time;
AZStd::unique_ptr<BenchmarkNetworkTime> m_NetworkTime;
AZStd::unique_ptr<BenchmarkMultiplayerConnection> m_Connection;
AZStd::unique_ptr<BenchmarkConnectionListener> m_ConnectionListener;
AZStd::unique_ptr<EntityReplicationManager> m_entityReplicationManager;
void SetupEntity(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId netId, NetEntityRole role)
{
const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
EXPECT_NE(netBindComponent, nullptr);
netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role);
entity->Init();
}
static void StopEntity(const AZStd::unique_ptr<AZ::Entity>& entity)
{
const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
EXPECT_NE(netBindComponent, nullptr);
netBindComponent->StopEntity();
}
static void StopAndDeactivateEntity(AZStd::unique_ptr<AZ::Entity>& entity)
{
if (entity)
{
StopEntity(entity);
entity->Deactivate();
entity.reset();
}
}
void CreateEntityWithRootHierarchy(AZStd::unique_ptr<AZ::Entity>& rootEntity)
{
rootEntity->CreateComponent<AzFramework::TransformComponent>();
rootEntity->CreateComponent<NetBindComponent>();
rootEntity->CreateComponent<NetworkTransformComponent>();
rootEntity->CreateComponent<NetworkHierarchyRootComponent>();
}
void CreateEntityWithChildHierarchy(AZStd::unique_ptr<AZ::Entity>& childEntity)
{
childEntity->CreateComponent<AzFramework::TransformComponent>();
childEntity->CreateComponent<NetBindComponent>();
childEntity->CreateComponent<NetworkTransformComponent>();
childEntity->CreateComponent<NetworkHierarchyChildComponent>();
}
void SetParentIdOnNetworkTransform(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId netParentId)
{
/* Derived from NetworkTransformComponent.AutoComponent.xml */
constexpr int totalBits = 6 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::Count*/;
constexpr int parentIdBit = 4 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::parentEntityId_DirtyFlag*/;
ReplicationRecord currentRecord;
currentRecord.m_authorityToClient.AddBits(totalBits);
currentRecord.m_authorityToClient.SetBit(parentIdBit, true);
constexpr uint32_t bufferSize = 100;
AZStd::array<uint8_t, bufferSize> buffer = {};
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
inSerializer.Serialize(reinterpret_cast<uint32_t&>(netParentId),
"parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
ReplicationRecord notifyRecord = currentRecord;
entity->FindComponent<NetworkTransformComponent>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
entity->FindComponent<NetworkTransformComponent>()->NotifyStateDeltaChanges(notifyRecord);
}
template <typename Component>
void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId value)
{
/* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */
constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/;
constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/;
ReplicationRecord currentRecord;
currentRecord.m_authorityToClient.AddBits(totalBits);
currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true);
constexpr uint32_t bufferSize = 100;
AZStd::array<uint8_t, bufferSize> buffer = {};
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
inSerializer.Serialize(reinterpret_cast<uint32_t&>(value),
"hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
ReplicationRecord notifyRecord = currentRecord;
entity->FindComponent<Component>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
entity->FindComponent<Component>()->NotifyStateDeltaChanges(notifyRecord);
}
struct EntityInfo
{
enum class Role
{
Root,
Child,
None
};
EntityInfo(AZ::u64 entityId, const char* entityName, NetEntityId netId, Role role)
: m_entity(AZStd::make_unique<AZ::Entity>(AZ::EntityId(entityId), entityName))
, m_netId(netId)
, m_role(role)
{
}
~EntityInfo()
{
StopAndDeactivateEntity(m_entity);
}
AZStd::unique_ptr<AZ::Entity> m_entity;
NetEntityId m_netId;
AZStd::unique_ptr<EntityReplicator> m_replicator;
Role m_role = Role::None;
};
void PopulateHierarchicalEntity(const EntityInfo& entityInfo)
{
entityInfo.m_entity->CreateComponent<AzFramework::TransformComponent>();
entityInfo.m_entity->CreateComponent<NetBindComponent>();
entityInfo.m_entity->CreateComponent<NetworkTransformComponent>();
switch (entityInfo.m_role)
{
case EntityInfo::Role::Root:
entityInfo.m_entity->CreateComponent<NetworkHierarchyRootComponent>();
break;
case EntityInfo::Role::Child:
entityInfo.m_entity->CreateComponent<NetworkHierarchyChildComponent>();
break;
case EntityInfo::Role::None:
break;
}
}
void CreateParent(EntityInfo& parent)
{
PopulateHierarchicalEntity(parent);
SetupEntity(parent.m_entity, parent.m_netId, NetEntityRole::Authority);
// Create an entity replicator for the child entity
const NetworkEntityHandle childHandle(parent.m_entity.get(), m_NetworkEntityManager->GetNetworkEntityTracker());
parent.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_Connection.get(), NetEntityRole::Client, childHandle);
parent.m_replicator->Initialize(childHandle);
parent.m_entity->Activate();
}
void CreateChildForParent(EntityInfo& child, EntityInfo& parent)
{
PopulateHierarchicalEntity(child);
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority);
// we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
SetParentIdOnNetworkTransform(child.m_entity, parent.m_netId);
// Create an entity replicator for the child entity
const NetworkEntityHandle childHandle(child.m_entity.get(), m_NetworkEntityManager->GetNetworkEntityTracker());
child.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_Connection.get(), NetEntityRole::Client, childHandle);
child.m_replicator->Initialize(childHandle);
child.m_entity->Activate();
}
void ForceRebuildHierarchy(const AZStd::unique_ptr<AZ::Entity>& rootEntity)
{
if (NetworkHierarchyRootComponent* root = rootEntity->FindComponent<NetworkHierarchyRootComponent>())
{
root->RebuildHierarchy();
}
}
};
}
#endif
@@ -0,0 +1,119 @@
/*
* 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
*
*/
#ifdef HAVE_BENCHMARK
#include <CommonBenchmarkSetup.h>
#include <AzCore/std/smart_ptr/make_shared.h>
namespace Multiplayer
{
/*
* Hierarchy of 16 entities: Parent -> Child_2 -> .. -> Child_16
* By default the maximum size of a hierarchy is defined by bg_hierarchyEntityMaxLimit (16).
*/
class ServerDeepHierarchyBenchmark : public HierarchyBenchmarkBase
{
public:
const NetEntityId RootNetEntityId = NetEntityId{ 1 };
const NetEntityId ChildNetEntityId = NetEntityId{ 2 };
const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 };
void internalSetUp() override
{
HierarchyBenchmarkBase::internalSetUp();
m_root = AZStd::make_unique<EntityInfo>((1), "root", RootNetEntityId, EntityInfo::Role::Root);
CreateParent(*m_root);
m_children = AZStd::make_unique<AZStd::vector<AZStd::shared_ptr<EntityInfo>>>();
EntityInfo* parent = m_root.get();
for (int i = 0; i < 15; ++i)
{
m_children->push_back(AZStd::make_shared<EntityInfo>((i + 2), "child", ChildNetEntityId, EntityInfo::Role::Child));
CreateChildForParent(*m_children->back(), *parent);
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parent->m_entity->GetId());
parent = m_children->back().get();
}
}
void internalTearDown() override
{
m_children.reset();
m_root.reset();
HierarchyBenchmarkBase::internalTearDown();
}
AZStd::unique_ptr<EntityInfo> m_root;
AZStd::unique_ptr<AZStd::vector<AZStd::shared_ptr<EntityInfo>>> m_children;
};
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchy)(benchmark::State& state)
{
for ([[maybe_unused]] auto value : state)
{
ForceRebuildHierarchy(m_root->m_entity);
}
}
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchy)
->Unit(benchmark::kMicrosecond)
;
// Should be roughly twice the time of @RebuildHierarchy benchmark
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddLastChild)(benchmark::State& state)
{
const AZ::EntityId parentOfLastChild = m_children->at(m_children->size() - 2)->m_entity->GetId();
for ([[maybe_unused]] auto value : state)
{
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parentOfLastChild);
}
}
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddLastChild)
->Unit(benchmark::kMicrosecond)
;
// Should be roughly twice the time of @RebuildHierarchy benchmark
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddMiddleChild)(benchmark::State& state)
{
const AZ::EntityId parentOfMiddleChild = m_children->at(4)->m_entity->GetId();
for ([[maybe_unused]] auto value : state)
{
m_children->at(5)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
m_children->at(5)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parentOfMiddleChild);
}
}
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddMiddleChild)
->Unit(benchmark::kMicrosecond)
;
// Should be roughly twice the time of @RebuildHierarchy benchmark
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddFirstChild)(benchmark::State& state)
{
const AZ::EntityId rootId = m_children->front()->m_entity->GetId();
for ([[maybe_unused]] auto value : state)
{
m_children->at(1)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
m_children->at(1)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(rootId);
}
}
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddFirstChild)
->Unit(benchmark::kMicrosecond)
;
}
#endif
@@ -8,7 +8,9 @@
set(FILES
Tests/ClientHierarchyTests.cpp
Tests/ServerHierarchyBenchmarks.cpp
Tests/CommonHierarchySetup.h
Tests/CommonBenchmarkSetup.h
Tests/IMultiplayerConnectionMock.h
Tests/Main.cpp
Tests/MockInterfaces.h