From a9364ab7e25db1a535d00a36a37984526af74540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Thu, 8 Feb 2018 16:25:27 +0100 Subject: [PATCH] Sdk/World: Add profiler --- ChangeLog.md | 1 + SDK/include/NDK/World.hpp | 16 ++++ SDK/include/NDK/World.inl | 120 +++++++++++++++++++++++------ SDK/src/NDK/Lua/LuaBinding_SDK.cpp | 4 + SDK/src/NDK/World.cpp | 38 +++++++-- 5 files changed, 149 insertions(+), 30 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 4b5545f15..9cfcdf6d4 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -96,6 +96,7 @@ Nazara Development Kit: - Fix CollisionComponent3D initialization (teleportation to their real coordinates) which could sometimes mess up the physics scene. - ⚠️ Renamed World::Update() to World::Refresh() for more clarity and to differentiate it from World::Update(elapsedTime) - World entity ids are now reused from lowest to highest (they were previously reused in reverse order of death) +- World now has an internal profiler, allowing to measure the refresh and system update time # 0.4: diff --git a/SDK/include/NDK/World.hpp b/SDK/include/NDK/World.hpp index 1ee35979b..83ba86f12 100644 --- a/SDK/include/NDK/World.hpp +++ b/SDK/include/NDK/World.hpp @@ -29,6 +29,7 @@ namespace Ndk public: using EntityVector = std::vector; + struct ProfilerData; inline World(bool addDefaultSystems = true); World(const World&) = delete; @@ -46,8 +47,12 @@ namespace Ndk void Clear() noexcept; const EntityHandle& CloneEntity(EntityId id); + inline void DisableProfiler(); + inline void EnableProfiler(bool enable = true); + inline const EntityHandle& GetEntity(EntityId id); inline const EntityList& GetEntities() const; + inline const ProfilerData& GetProfilerData() const; inline BaseSystem& GetSystem(SystemIndex index); template SystemType& GetSystem(); @@ -59,18 +64,27 @@ namespace Ndk inline bool IsEntityValid(const Entity* entity) const; inline bool IsEntityIdValid(EntityId id) const; + inline bool IsProfilerEnabled() const; inline void RemoveAllSystems(); inline void RemoveSystem(SystemIndex index); template void RemoveSystem(); void Refresh(); + inline void ResetProfiler(); void Update(float elapsedTime); World& operator=(const World&) = delete; inline World& operator=(World&& world) noexcept; + struct ProfilerData + { + Nz::UInt64 refreshTime = 0; + std::vector updateTime; + std::size_t updateCount = 0; + }; + private: inline void Invalidate(); inline void Invalidate(EntityId id); @@ -97,10 +111,12 @@ namespace Ndk std::vector m_entityBlocks; std::vector> m_waitingEntities; EntityList m_aliveEntities; + ProfilerData m_profilerData; Nz::Bitset m_dirtyEntities; Nz::Bitset m_freeEntityIds; Nz::Bitset m_killedEntities; bool m_orderedSystemsUpdated; + bool m_isProfilerEnabled; }; } diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index 87aee67d3..32903502d 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequisites.hpp +#include #include #include @@ -14,7 +15,8 @@ namespace Ndk */ inline World::World(bool addDefaultSystems) : - m_orderedSystemsUpdated(false) + m_orderedSystemsUpdated(false), + m_isProfilerEnabled(false) { if (addDefaultSystems) AddDefaultSystems(); @@ -47,7 +49,10 @@ namespace Ndk // We must ensure that the vector is big enough to hold the new system if (index >= m_systems.size()) + { m_systems.resize(index + 1); + m_profilerData.updateTime.resize(index + 1, 0); + } // Affectation and return of system m_systems[index] = std::move(system); @@ -82,7 +87,6 @@ namespace Ndk * * \param count Number of entities to create */ - inline World::EntityVector World::CreateEntities(unsigned int count) { EntityVector list; @@ -94,16 +98,79 @@ namespace Ndk return list; } + /*! + * \brief Disables the profiler, clearing up results + * + * This is just a shortcut to EnableProfiler(false) + * + * \param enable Should the entity be enabled + * + * \see EnableProfiler + */ + inline void World::DisableProfiler() + { + EnableProfiler(false); + } + + /*! + * \brief Enables/Disables the internal profiler + * + * Worlds come with a built-in profiler, allowing to measure update count along with time passed in refresh and system updates. + * This is disabled by default as it adds an small overhead to the update process. + * + * \param enable Should the profiler be enabled + * + * \remark Disabling the profiler clears up results, as if ResetProfiler has been called + */ + inline void World::EnableProfiler(bool enable) + { + if (m_isProfilerEnabled != enable) + { + m_isProfilerEnabled = enable; + + if (enable) + ResetProfiler(); + } + } + + /*! + * \brief Gets an entity + * \return A constant reference to a handle of the entity + * + * \param id Identifier of the entity + * + * \remark Handle referenced by this function can move in memory when updating the world, do not keep a handle reference from a world update to another + * \remark If an invalid identifier is provided, an error got triggered and an invalid handle is returned + */ + inline const EntityHandle& World::GetEntity(EntityId id) + { + if (IsEntityIdValid(id)) + return m_entityBlocks[id]->handle; + else + { + NazaraError("Invalid ID"); + return EntityHandle::InvalidHandle; + } + } + /*! * \brief Gets every entities in the world * \return A constant reference to the entities */ - inline const EntityList& World::GetEntities() const { return m_aliveEntities; } + /*! + * \brief Gets the latest profiler data + * \return A constant reference to the profiler data + */ + inline const World::ProfilerData& World::GetProfilerData() const + { + return m_profilerData; + } + /*! * \brief Gets a system in the world by index * \return A reference to the system @@ -192,26 +259,6 @@ namespace Ndk KillEntity(entity); } - /*! - * \brief Gets an entity - * \return A constant reference to a handle of the entity - * - * \param id Identifier of the entity - * - * \remark Handle referenced by this function can move in memory when updating the world, do not keep a handle reference from a world update to another - * \remark If an invalid identifier is provided, an error got triggered and an invalid handle is returned - */ - inline const EntityHandle& World::GetEntity(EntityId id) - { - if (IsEntityIdValid(id)) - return m_entityBlocks[id]->handle; - else - { - NazaraError("Invalid ID"); - return EntityHandle::InvalidHandle; - } - } - /*! * \brief Checks whether or not an entity is valid * \return true If it is the case @@ -230,12 +277,22 @@ namespace Ndk * * \param id Identifier of the entity */ - inline bool World::IsEntityIdValid(EntityId id) const { return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid(); } + /*! + * \brief Checks whether or not the profiler is enabled + * \return true If it is the case + * + * \see EnableProfiler + */ + inline bool World::IsProfilerEnabled() const + { + return m_isProfilerEnabled; + } + /*! * \brief Removes each system from the world */ @@ -265,10 +322,21 @@ namespace Ndk } } + /*! + * \brief Clear profiler results + * + * This reset the profiler results, filling all counters with zero + */ + inline void World::ResetProfiler() + { + m_profilerData.refreshTime = 0; + m_profilerData.updateCount = 0; + std::fill(m_profilerData.updateTime.begin(), m_profilerData.updateTime.end(), 0); + } + /*! * \brief Removes a system from the world by type */ - template void World::RemoveSystem() { @@ -292,6 +360,8 @@ namespace Ndk m_killedEntities = std::move(world.m_killedEntities); m_orderedSystems = std::move(world.m_orderedSystems); m_orderedSystemsUpdated = world.m_orderedSystemsUpdated; + m_profilerData = std::move(world.m_profilerData); + m_isProfilerEnabled = m_isProfilerEnabled; m_entities = std::move(world.m_entities); for (EntityBlock& block : m_entities) diff --git a/SDK/src/NDK/Lua/LuaBinding_SDK.cpp b/SDK/src/NDK/Lua/LuaBinding_SDK.cpp index 36cccfb1a..e9d7fd9cc 100644 --- a/SDK/src/NDK/Lua/LuaBinding_SDK.cpp +++ b/SDK/src/NDK/Lua/LuaBinding_SDK.cpp @@ -179,6 +179,10 @@ namespace Ndk world.BindMethod("CreateEntity", &World::CreateEntity); world.BindMethod("CreateEntities", &World::CreateEntities); world.BindMethod("Clear", &World::Clear); + world.BindMethod("DisableProfiler", &World::DisableProfiler); + world.BindMethod("EnableProfiler", &World::EnableProfiler); + world.BindMethod("IsProfilerEnabled", &World::IsProfilerEnabled); + world.BindMethod("ResetProfiler", &World::ResetProfiler); world.BindMethod("IsValidHandle", &WorldHandle::IsValid); } diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index beb6a7d3b..6afb27665 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Prerequisites.hpp #include +#include #include #include #include @@ -177,6 +178,10 @@ namespace Ndk * - Destroying dead entities and allowing their ids to be used by newly created entities * - Update dirty entities, destroying their removed components and filtering them along systems * + * \remark Calling this outside of Update will not increase the profiler values + * + * \see GetProfilerData + * \see Update */ void World::Refresh() { @@ -257,16 +262,39 @@ namespace Ndk /*! * \brief Updates the world - * * \param elapsedTime Delta time used for the update + * + * This function Refreshes the world and calls the Update function of every active system part of it with the elapsedTime value. + * It also increase the profiler data with the elapsed time passed in Refresh and every system update. */ void World::Update(float elapsedTime) { - Refresh(); //< Update entities + if (m_isProfilerEnabled) + { + Nz::UInt64 t1 = Nz::GetElapsedMicroseconds(); + Refresh(); + Nz::UInt64 t2 = Nz::GetElapsedMicroseconds(); - // And then update systems - for (auto& systemPtr : m_orderedSystems) - systemPtr->Update(elapsedTime); + m_profilerData.refreshTime += t2 - t1; + + for (auto& systemPtr : m_orderedSystems) + { + systemPtr->Update(elapsedTime); + + Nz::UInt64 t3 = Nz::GetElapsedMicroseconds(); + m_profilerData.updateTime[systemPtr->GetIndex()] += t3 - t2; + t2 = t3; + } + + m_profilerData.updateCount++; + } + else + { + Refresh(); + + for (auto& systemPtr : m_orderedSystems) + systemPtr->Update(elapsedTime); + } } void World::ReorderSystems()