diff --git a/Client/mods/deathmatch/StdInc.h b/Client/mods/deathmatch/StdInc.h index 143e7a3498d..f5b701f6258 100644 --- a/Client/mods/deathmatch/StdInc.h +++ b/Client/mods/deathmatch/StdInc.h @@ -150,6 +150,7 @@ #include "CLatentTransferManager.h" #include "CDebugHookManager.h" #include "lua/CLuaShared.h" +#include "profiler/CPerformanceRecorder.h" // Deathmatch includes #include "ClientCommands.h" diff --git a/Client/mods/deathmatch/logic/CClientEntity.cpp b/Client/mods/deathmatch/logic/CClientEntity.cpp index 61be4ca269d..36052806c69 100644 --- a/Client/mods/deathmatch/logic/CClientEntity.cpp +++ b/Client/mods/deathmatch/logic/CClientEntity.cpp @@ -732,6 +732,7 @@ bool CClientEntity::AddEvent(CLuaMain* pLuaMain, const char* szName, const CLuaF bool CClientEntity::CallEvent(const char* szName, const CLuaArguments& Arguments, bool bCallOnChildren) { + CPerformanceRecorder::EventSample sample(szName); if (!g_pClientGame->GetDebugHookManager()->OnPreEvent(szName, Arguments, this, NULL)) return false; diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index e5250402cd5..78e5ba771ad 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -184,6 +184,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_pScriptKeyBinds = new CScriptKeyBinds; m_pRemoteCalls = new CRemoteCalls(); m_pResourceFileDownloadManager = new CResourceFileDownloadManager(); + m_pPerformanceRecorder = std::make_unique(); // Create our net API m_pNetAPI = new CNetAPI(m_pManager); @@ -731,6 +732,7 @@ void CClientGame::DoPulsePreFrame() void CClientGame::DoPulsePreHUDRender(bool bDidUnminimize, bool bDidRecreateRenderTargets) { + CPerformanceRecorder::Sample sample("CClientGame::DoPulsePreHUDRender"); // Allow scripted dxSetRenderTarget for old scripts g_pCore->GetGraphics()->GetRenderItemManager()->EnableSetRenderTargetOldVer(true); @@ -763,6 +765,7 @@ void CClientGame::DoPulsePreHUDRender(bool bDidUnminimize, bool bDidRecreateRend void CClientGame::DoPulsePostFrame() { + CPerformanceRecorder::Sample sample("CClientGame::DoPulsePostFrame"); TIMING_CHECKPOINT("+CClientGame::DoPulsePostFrame"); #ifdef DEBUG_KEYSTATES // Get the controller state @@ -940,6 +943,7 @@ void CClientGame::DoPulses() g_pCore->ApplyFrameRateLimit(); + CPerformanceRecorder::Sample sample("CClientGame::DoPulses"); TIMING_CHECKPOINT("+CClientGame::DoPulses"); m_BuiltCollisionMapThisFrame = false; @@ -1313,6 +1317,7 @@ void CClientGame::DoPulses() // Extrapolation test void CClientGame::DoPulses2(bool bCalledFromIdle) { + CPerformanceRecorder::Sample sample("CClientGame::DoPulses2"); bool bIsUsingAlternatePulseOrder = IsUsingAlternatePulseOrder(!bCalledFromIdle); // Figure out which pulses to do @@ -1343,11 +1348,12 @@ void CClientGame::DoPulses2(bool bCalledFromIdle) // be rounded to look more like what is expected ChangeFloatPrecision(true); - // Pulse the network interface - TIMING_CHECKPOINT("+NetPulse"); - g_pNet->DoPulse(); - TIMING_CHECKPOINT("-NetPulse"); - + { + // Pulse the network interface + TIMING_CHECKPOINT("+NetPulse"); + g_pNet->DoPulse(); + TIMING_CHECKPOINT("-NetPulse"); + } // Change precision back, and check we are in low precision mode 4 sure ChangeFloatPrecision(false); assert(!IsHighFloatPrecision()); diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 7633479508e..4efaafacce5 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -299,6 +299,7 @@ class CClientGame CLatentTransferManager* GetLatentTransferManager() { return m_pLatentTransferManager; } CClientModelCacheManager* GetModelCacheManager() { return m_pModelCacheManager; } CDebugHookManager* GetDebugHookManager() { return m_pDebugHookManager; } + CPerformanceRecorder* GetPerformanceRecorder() const { return m_pPerformanceRecorder.get(); } CElementDeleter* GetElementDeleter() { return &m_ElementDeleter; } CObjectRespawner* GetObjectRespawner() { return &m_ObjectRespawner; } @@ -703,7 +704,8 @@ class CClientGame CScriptDebugging* m_pScriptDebugging; CRegisteredCommands m_RegisteredCommands; - std::unique_ptr m_ServerInfo; + std::unique_ptr m_ServerInfo; + std::unique_ptr m_pPerformanceRecorder; // Map statuses SString m_strCurrentMapName; diff --git a/Client/mods/deathmatch/logic/CClientModelCacheManager.cpp b/Client/mods/deathmatch/logic/CClientModelCacheManager.cpp index 42c84cf0c68..4d32f31773a 100644 --- a/Client/mods/deathmatch/logic/CClientModelCacheManager.cpp +++ b/Client/mods/deathmatch/logic/CClientModelCacheManager.cpp @@ -101,6 +101,7 @@ CClientModelCacheManagerImpl::~CClientModelCacheManagerImpl() /////////////////////////////////////////////////////////////// void CClientModelCacheManagerImpl::DoPulse() { + CPerformanceRecorder::Sample sample("CClientModelCacheManager::DoPulse"); m_TickCountNow = CTickCount::Now(); ClearStats(); diff --git a/Client/mods/deathmatch/logic/CElementDeleter.cpp b/Client/mods/deathmatch/logic/CElementDeleter.cpp index 147fa4835a2..44dc6fbd109 100644 --- a/Client/mods/deathmatch/logic/CElementDeleter.cpp +++ b/Client/mods/deathmatch/logic/CElementDeleter.cpp @@ -88,6 +88,7 @@ void CElementDeleter::DeleteRecursive(class CClientEntity* pElement) void CElementDeleter::DoDeleteAll() { + CPerformanceRecorder::Sample sample("CElementDeleter::DoDeleteAll"); // Make sure elements won't call us back and screw with our list (would crash) m_bAllowUnreference = false; diff --git a/Client/mods/deathmatch/logic/CMovingObjectsManager.cpp b/Client/mods/deathmatch/logic/CMovingObjectsManager.cpp index d64b45e4d17..2a230f5171c 100644 --- a/Client/mods/deathmatch/logic/CMovingObjectsManager.cpp +++ b/Client/mods/deathmatch/logic/CMovingObjectsManager.cpp @@ -14,6 +14,7 @@ void CMovingObjectsManager::DoPulse() { + CPerformanceRecorder::Sample sample("CMovingObjectsManager::DoPulse"); using Iterator = std::list::iterator; for (Iterator iter = m_List.begin(); iter != m_List.end(); /* manual increment */) diff --git a/Client/mods/deathmatch/logic/CPedSync.cpp b/Client/mods/deathmatch/logic/CPedSync.cpp index d11e8f526f0..41ab2cc2e46 100644 --- a/Client/mods/deathmatch/logic/CPedSync.cpp +++ b/Client/mods/deathmatch/logic/CPedSync.cpp @@ -55,6 +55,7 @@ bool CPedSync::ProcessPacket(unsigned char ucPacketID, NetBitStreamInterface& Bi void CPedSync::DoPulse() { + CPerformanceRecorder::Sample sample("CPedSync::DoPulse"); // Got any items? if (m_List.size() > 0) { diff --git a/Client/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp b/Client/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp index e341edcbed4..a96035be5e7 100644 --- a/Client/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp +++ b/Client/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp @@ -56,6 +56,7 @@ bool CUnoccupiedVehicleSync::ProcessPacket(unsigned char ucPacketID, NetBitStrea void CUnoccupiedVehicleSync::DoPulse() { + CPerformanceRecorder::Sample sample("CUnoccupiedVehicleSync::DoPulse"); // Check all our vehicles for damage UpdateDamageModels(); diff --git a/Client/mods/deathmatch/logic/lua/CLuaMain.cpp b/Client/mods/deathmatch/logic/lua/CLuaMain.cpp index 4f97dc44ba4..589d96c850c 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaMain.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaMain.cpp @@ -508,6 +508,7 @@ const SString& CLuaMain::GetFunctionTag(int iLuaFunction) /////////////////////////////////////////////////////////////// int CLuaMain::PCall(lua_State* L, int nargs, int nresults, int errfunc) { + CPerformanceRecorder::Sample sample("CLuaMain::PCall"); TIMING_CHECKPOINT("+pcall"); g_pClientGame->ChangeFloatPrecision(true); g_pClientGame->GetScriptDebugging()->PushLuaMain(this); diff --git a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp index 0f41e7396e7..2696eab49db 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp @@ -110,6 +110,8 @@ void CLuaManager::ProcessPendingDeleteList() void CLuaManager::DoPulse() { + CPerformanceRecorder::Sample sample("CLuaManager::DoPulse"); + sample.SetArg("Virtual machines count", m_virtualMachines.size()); list::iterator iter = m_virtualMachines.begin(); for (; iter != m_virtualMachines.end(); ++iter) { diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp index c3b7def7b66..b70908bcef5 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp @@ -1711,6 +1711,7 @@ int CLuaElementDefs::SetElementID(lua_State* luaVM) int CLuaElementDefs::SetElementData(lua_State* luaVM) { + CPerformanceRecorder::FunctionSample sample("SetElementData"); // bool setElementData ( element theElement, string key, var value, [bool synchronize = true] ) CClientEntity* pEntity; SString strKey; @@ -1735,7 +1736,8 @@ int CLuaElementDefs::SetElementData(lua_State* luaVM) *SString("string length reduced to %d characters at argument 2", MAX_CUSTOMDATA_NAME_LENGTH))); strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); } - + sample.SetArg("Key", strKey); + sample.SetBool("Synchronized", bSynchronize); if (CStaticFunctionDefinitions::SetElementData(*pEntity, strKey, value, bSynchronize)) { lua_pushboolean(luaVM, true); diff --git a/Client/mods/deathmatch/premake5.lua b/Client/mods/deathmatch/premake5.lua index e5bb371fa02..0dc78dbf4f0 100644 --- a/Client/mods/deathmatch/premake5.lua +++ b/Client/mods/deathmatch/premake5.lua @@ -54,6 +54,7 @@ project "Client Deathmatch" "../../../Shared/mods/deathmatch/logic/**.h", "../../../Shared/animation/CEasingCurve.cpp", "../../../Shared/animation/CPositionRotationAnimation.cpp", + "../../../Shared/sdk/profiler/CPerformanceRecorder.cpp", -- Todo: Replace these two by using the CryptoPP functions instead "../../../vendor/bochs/bochs_internal/bochs_crc32.cpp" } diff --git a/Server/mods/deathmatch/StdInc.h b/Server/mods/deathmatch/StdInc.h index 4fe445cd8c7..01d71dc8e28 100644 --- a/Server/mods/deathmatch/StdInc.h +++ b/Server/mods/deathmatch/StdInc.h @@ -177,6 +177,7 @@ struct SAclRequest; // Logic includes #include "ASE.h" +#include "profiler/CPerformanceRecorder.h" #include "ASEQuerySDK.h" #include "CAccessControlList.h" #include "CAccessControlListGroup.h" diff --git a/Server/mods/deathmatch/logic/ASE.cpp b/Server/mods/deathmatch/logic/ASE.cpp index a7636980298..bec9c350bc8 100644 --- a/Server/mods/deathmatch/logic/ASE.cpp +++ b/Server/mods/deathmatch/logic/ASE.cpp @@ -134,6 +134,7 @@ bool ASE::SetPortEnabled(bool bInternetEnabled, bool bLanEnabled) void ASE::DoPulse() { + CPerformanceRecorder::Sample sample("ASE::DoPulse"); if (m_SocketList.empty()) return; diff --git a/Server/mods/deathmatch/logic/CAccessControlListManager.cpp b/Server/mods/deathmatch/logic/CAccessControlListManager.cpp index c5b89d43bb8..f897e08d989 100644 --- a/Server/mods/deathmatch/logic/CAccessControlListManager.cpp +++ b/Server/mods/deathmatch/logic/CAccessControlListManager.cpp @@ -304,6 +304,7 @@ CAccessControlList* CAccessControlListManager::GetACL(const char* szACLName) void CAccessControlListManager::DoPulse() { + CPerformanceRecorder::Sample sample("CAccessControlListManager::DoPulse"); // Clear cache every 12 hours or if dirty if (m_bReadCacheDirty || GetTickCount64_() - m_llLastTimeReadCacheCleared > 1000 * 60 * 60 * 12) ClearReadCache(); diff --git a/Server/mods/deathmatch/logic/CAccountManager.cpp b/Server/mods/deathmatch/logic/CAccountManager.cpp index 101b9d66281..c0a77675bac 100644 --- a/Server/mods/deathmatch/logic/CAccountManager.cpp +++ b/Server/mods/deathmatch/logic/CAccountManager.cpp @@ -135,6 +135,7 @@ void CAccountManager::DoPulse() // Save it only once in a while whenever something has changed if (m_bChangedSinceSaved && GetTickCount64_() > m_llLastTimeSaved + 15000) { + CPerformanceRecorder::Sample sample("CAccountManager::DoPulse"); // Save it Save(); } diff --git a/Server/mods/deathmatch/logic/CBanManager.cpp b/Server/mods/deathmatch/logic/CBanManager.cpp index 369378daea6..69712392986 100644 --- a/Server/mods/deathmatch/logic/CBanManager.cpp +++ b/Server/mods/deathmatch/logic/CBanManager.cpp @@ -39,6 +39,7 @@ CBanManager::~CBanManager() void CBanManager::DoPulse() { + CPerformanceRecorder::Sample sample("CBanManager::DoPulse"); time_t tTime = time(NULL); if (tTime > m_tUpdate) diff --git a/Server/mods/deathmatch/logic/CBlendedWeather.cpp b/Server/mods/deathmatch/logic/CBlendedWeather.cpp index 15b4d6d128e..1e42561de8d 100644 --- a/Server/mods/deathmatch/logic/CBlendedWeather.cpp +++ b/Server/mods/deathmatch/logic/CBlendedWeather.cpp @@ -27,6 +27,7 @@ CBlendedWeather::CBlendedWeather(CClock* pClock) void CBlendedWeather::DoPulse() { + CPerformanceRecorder::Sample sample("CBlendedWeather::DoPulse"); Update(); } diff --git a/Server/mods/deathmatch/logic/CDatabaseManager.cpp b/Server/mods/deathmatch/logic/CDatabaseManager.cpp index b8b6311adfe..82b6a94c004 100644 --- a/Server/mods/deathmatch/logic/CDatabaseManager.cpp +++ b/Server/mods/deathmatch/logic/CDatabaseManager.cpp @@ -119,6 +119,7 @@ CDatabaseManagerImpl::~CDatabaseManagerImpl() /////////////////////////////////////////////////////////////// void CDatabaseManagerImpl::DoPulse() { + CPerformanceRecorder::Sample sample("CDatabaseManager::DoPulse"); m_JobQueue->DoPulse(); } diff --git a/Server/mods/deathmatch/logic/CElement.cpp b/Server/mods/deathmatch/logic/CElement.cpp index 9ba24b51c12..4fa02ca5876 100644 --- a/Server/mods/deathmatch/logic/CElement.cpp +++ b/Server/mods/deathmatch/logic/CElement.cpp @@ -422,6 +422,7 @@ bool CElement::AddEvent(CLuaMain* pLuaMain, const char* szName, const CLuaFuncti bool CElement::CallEvent(const char* szName, const CLuaArguments& Arguments, CPlayer* pCaller) { + CPerformanceRecorder::EventSample sample(szName); if (!g_pGame->GetDebugHookManager()->OnPreEvent(szName, Arguments, this, pCaller)) return false; diff --git a/Server/mods/deathmatch/logic/CElementDeleter.cpp b/Server/mods/deathmatch/logic/CElementDeleter.cpp index 9a6aa286986..64a3d452eb4 100644 --- a/Server/mods/deathmatch/logic/CElementDeleter.cpp +++ b/Server/mods/deathmatch/logic/CElementDeleter.cpp @@ -44,6 +44,8 @@ void CElementDeleter::Delete(class CElement* pElement, bool bUnlink, bool bUpdat void CElementDeleter::DoDeleteAll() { + CPerformanceRecorder::Sample sample("CElementDeleter::DoDeleteAll"); + sample.SetArg("Deleted elements", m_List.size()); // This depends on CElementDeleter::Unreference() being called in ~CElement() while (!m_List.empty()) delete *m_List.begin(); diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index fc843054d8a..3766a046933 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -141,6 +141,7 @@ CGame::CGame() : m_FloodProtect(4, 30000, 30000) // Max of 4 connecti m_pBuildingRemovalManager = NULL; m_pCustomWeaponManager = NULL; m_pFunctionUseLogger = NULL; + m_pPerformanceRecorder = nullptr; #ifdef WITH_OBJECT_SYNC m_pObjectSync = NULL; #endif @@ -320,6 +321,7 @@ CGame::~CGame() SAFE_DELETE(m_pSettings); SAFE_DELETE(m_pRPCFunctions); SAFE_DELETE(m_pWaterManager); + SAFE_DELETE(m_pPerformanceRecorder); SAFE_DELETE(m_pWeaponStatsManager); SAFE_DELETE(m_pBuildingRemovalManager); SAFE_DELETE(m_pCustomWeaponManager); @@ -407,19 +409,32 @@ void CGame::DoPulse() CSimControl::DoPulse(); CNetBufferWatchDog::DoPulse(); + CPerformanceRecorder::Sample sample("DoPulse"); CLOCK_SET_SECTION("CGame::DoPulse"); - CLOCK1("HTTPDownloadManager"); - GetRemoteCalls()->ProcessQueuedFiles(); - g_pNetServer->GetHTTPDownloadManager(EDownloadMode::ASE)->ProcessQueuedFiles(); - UNCLOCK1("HTTPDownloadManager"); + { + CPerformanceRecorder::Sample sample("HTTPDownloadManager"); + + CLOCK1("HTTPDownloadManager"); + GetRemoteCalls()->ProcessQueuedFiles(); + g_pNetServer->GetHTTPDownloadManager(EDownloadMode::ASE)->ProcessQueuedFiles(); + UNCLOCK1("HTTPDownloadManager"); + } - CLOCK_CALL1(m_pPlayerManager->DoPulse();); + { + CPerformanceRecorder::Sample sample("CPlayerManager::DoPulse"); + CLOCK_CALL1(m_pPlayerManager->DoPulse();); - // Pulse the net interface - CLOCK_CALL1(g_pNetServer->DoPulse();); + } + + { + CPerformanceRecorder::Sample sample("CNetServerBuffer::DoPulse"); + // Pulse the net interface + CLOCK_CALL1(g_pNetServer->DoPulse();); + } if (m_pLanBroadcast) { + CPerformanceRecorder::Sample sample("Lan broadcast"); CLOCK_CALL1(m_pLanBroadcast->DoPulse();); } @@ -438,6 +453,7 @@ void CGame::DoPulse() // Handle the traffic light sync if (m_bTrafficLightsLocked == false) { + CPerformanceRecorder::Sample sample("ProcessTrafficLights"); CLOCK_CALL1(ProcessTrafficLights(llCurrentTime);); } @@ -464,17 +480,26 @@ void CGame::DoPulse() CLOCK_CALL1(CPerfStatManager::GetSingleton()->DoPulse();); if (m_pMasterServerAnnouncer) + { + CPerformanceRecorder::Sample sample("CMasterServerAnnouncer::Pulse"); m_pMasterServerAnnouncer->Pulse(); + } if (m_pHqComms) + { + CPerformanceRecorder::Sample sample("CHqComms::Pulse"); m_pHqComms->Pulse(); + } CLOCK_CALL1(m_pFunctionUseLogger->Pulse();); CLOCK_CALL1(m_lightsyncManager.DoPulse();); CLOCK_CALL1(m_pLatentTransferManager->DoPulse();); - CLOCK_CALL1(m_pAsyncTaskScheduler->CollectResults()); + { + CPerformanceRecorder::Sample sample("CAsyncTaskScheduler::CollectResults"); + CLOCK_CALL1(m_pAsyncTaskScheduler->CollectResults()); + } CLOCK_CALL1(m_pMapManager->GetWeather()->DoPulse();); @@ -511,6 +536,7 @@ bool CGame::Start(int iArgumentCount, char* szArguments[]) m_pTeamManager = new CTeamManager; m_pPedManager = new CPedManager; m_pWaterManager = new CWaterManager; + m_pPerformanceRecorder = new CPerformanceRecorder; m_pScriptDebugging = new CScriptDebugging(); m_pMapManager = new CMapManager(m_pBlipManager, m_pObjectManager, m_pPickupManager, m_pPlayerManager, m_pRadarAreaManager, m_pMarkerManager, m_pVehicleManager, m_pTeamManager, m_pPedManager, m_pColManager, m_pWaterManager, m_pClock, m_pGroups, diff --git a/Server/mods/deathmatch/logic/CGame.h b/Server/mods/deathmatch/logic/CGame.h index f8ab006cb05..00c402aa3a7 100644 --- a/Server/mods/deathmatch/logic/CGame.h +++ b/Server/mods/deathmatch/logic/CGame.h @@ -102,6 +102,7 @@ class CVehicleManager; class CZoneNames; class CLanBroadcast; class CWaterManager; +class CPerformanceRecorder; class CTrainTrackManager; class CWeaponStatManager; class CBuildingRemovalManager; @@ -245,6 +246,7 @@ class CGame CZoneNames* GetZoneNames() { return m_pZoneNames; } CClock* GetClock() { return m_pClock; } CWaterManager* GetWaterManager() { return m_pWaterManager; } + CPerformanceRecorder* GetPerformanceRecorder() { return m_pPerformanceRecorder; } CLightsyncManager* GetLightSyncManager() { return &m_lightsyncManager; } CWeaponStatManager* GetWeaponStatManager() { return m_pWeaponStatsManager; } CBuildingRemovalManager* GetBuildingRemovalManager() { return m_pBuildingRemovalManager; } @@ -555,6 +557,7 @@ class CGame CRPCFunctions* m_pRPCFunctions; CLanBroadcast* m_pLanBroadcast; CWaterManager* m_pWaterManager; + CPerformanceRecorder* m_pPerformanceRecorder; CWeaponStatManager* m_pWeaponStatsManager; CBuildingRemovalManager* m_pBuildingRemovalManager; diff --git a/Server/mods/deathmatch/logic/CLightsyncManager.cpp b/Server/mods/deathmatch/logic/CLightsyncManager.cpp index 056497519d9..7e9acce1025 100644 --- a/Server/mods/deathmatch/logic/CLightsyncManager.cpp +++ b/Server/mods/deathmatch/logic/CLightsyncManager.cpp @@ -76,6 +76,8 @@ void CLightsyncManager::DoPulse() // are also storing the delta context in what it happened, so we only consider the health unchanged // when we find the marker with the same context value. + CPerformanceRecorder::Sample sample("CLightsyncManager::DoPulse"); + if (g_pBandwidthSettings->bLightSyncEnabled == false) return; diff --git a/Server/mods/deathmatch/logic/CMapManager.cpp b/Server/mods/deathmatch/logic/CMapManager.cpp index 4debe64d676..e575c95cf42 100644 --- a/Server/mods/deathmatch/logic/CMapManager.cpp +++ b/Server/mods/deathmatch/logic/CMapManager.cpp @@ -55,6 +55,7 @@ CMapManager::~CMapManager() void CMapManager::DoPulse() { + CPerformanceRecorder::Sample sample("CMapManager::DoPulse"); // Do the respawning checks DoRespawning(); } diff --git a/Server/mods/deathmatch/logic/CPedSync.cpp b/Server/mods/deathmatch/logic/CPedSync.cpp index 490a137de44..1978dc9e8b1 100644 --- a/Server/mods/deathmatch/logic/CPedSync.cpp +++ b/Server/mods/deathmatch/logic/CPedSync.cpp @@ -19,6 +19,7 @@ CPedSync::CPedSync(CPlayerManager* pPlayerManager, CPedManager* pPedManager) void CPedSync::DoPulse() { + CPerformanceRecorder::Sample sample("CPedSync::DoPulse"); // Time to check for players that should no longer be syncing a ped or peds that should be synced? if (m_UpdateTimer.Get() > 500) { diff --git a/Server/mods/deathmatch/logic/CPlayerManager.cpp b/Server/mods/deathmatch/logic/CPlayerManager.cpp index 2d10e3693ef..3b83349fdbc 100644 --- a/Server/mods/deathmatch/logic/CPlayerManager.cpp +++ b/Server/mods/deathmatch/logic/CPlayerManager.cpp @@ -26,6 +26,7 @@ CPlayerManager::~CPlayerManager() void CPlayerManager::DoPulse() { + CPerformanceRecorder::Sample sample("CPlayerManager::DoPulse"); PulseZombieCheck(); list::const_iterator iter = m_Players.begin(); @@ -33,6 +34,7 @@ void CPlayerManager::DoPulse() { (*iter)->DoPulse(); } + sample.SetArg("Players", m_Players.size()); } void CPlayerManager::PulseZombieCheck() diff --git a/Server/mods/deathmatch/logic/CRegistryManager.cpp b/Server/mods/deathmatch/logic/CRegistryManager.cpp index a992039a2df..628601a1c0c 100644 --- a/Server/mods/deathmatch/logic/CRegistryManager.cpp +++ b/Server/mods/deathmatch/logic/CRegistryManager.cpp @@ -21,6 +21,7 @@ CRegistryManager::~CRegistryManager() void CRegistryManager::DoPulse() { + CPerformanceRecorder::Sample sample("CRegistryManager::DoPulse"); // End automatic transactions started in the previous pulse for (unsigned int i = 0; i < m_RegistryList.size(); i++) m_RegistryList[i]->EndAutomaticTransaction(); diff --git a/Server/mods/deathmatch/logic/CResourceManager.cpp b/Server/mods/deathmatch/logic/CResourceManager.cpp index 801fbe6081b..44ff0b5beed 100644 --- a/Server/mods/deathmatch/logic/CResourceManager.cpp +++ b/Server/mods/deathmatch/logic/CResourceManager.cpp @@ -747,6 +747,8 @@ void CResourceManager::QueueResource(CResource* pResource, eResourceQueue eQueue void CResourceManager::ProcessQueue() { + CPerformanceRecorder::Sample sample("CResourceManager::ProcessQueue"); + sample.SetArg("Queue size", m_resourceQueue.size()); // While we have queuestuff to process while (m_resourceQueue.size() > 0) { diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index fe6ed2a7b9f..4683fff0c74 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -886,6 +886,7 @@ bool CStaticFunctionDefinitions::SetElementData(CElement* pElement, const char* { if (syncType != ESyncType::LOCAL) { + CPerformanceRecorder::Sample sample("Broadcast"); // Tell our clients to update their data unsigned short usNameLength = static_cast(strlen(szName)); CBitStream BitStream; diff --git a/Server/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp b/Server/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp index 7da1c780c8c..235d67432bb 100644 --- a/Server/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp +++ b/Server/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp @@ -19,6 +19,7 @@ CUnoccupiedVehicleSync::CUnoccupiedVehicleSync(CPlayerManager* pPlayerManager, C void CUnoccupiedVehicleSync::DoPulse() { + CPerformanceRecorder::Sample sample("CUnoccupiedVehicleSync::DoPulse"); // Time to check for players that should no longer be syncing a vehicle or vehicles that should be synced? if (m_UpdateTimer.Get() > 500) { diff --git a/Server/mods/deathmatch/logic/lua/CLuaManager.cpp b/Server/mods/deathmatch/logic/lua/CLuaManager.cpp index b5b660ee2c4..b63e54b0620 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaManager.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaManager.cpp @@ -104,12 +104,14 @@ void CLuaManager::OnLuaMainCloseVM(CLuaMain* pLuaMain, lua_State* luaVM) void CLuaManager::DoPulse() { + CPerformanceRecorder::Sample sample("CLuaManager::DoPulse"); list::iterator iter; for (iter = m_virtualMachines.begin(); iter != m_virtualMachines.end(); ++iter) { (*iter)->DoPulse(); } m_pLuaModuleManager->DoPulse(); + sample.SetArg("Virtual machines count", m_virtualMachines.size()); } CLuaMain* CLuaManager::GetVirtualMachine(lua_State* luaVM) diff --git a/Server/mods/deathmatch/logic/lua/CLuaTimerManager.cpp b/Server/mods/deathmatch/logic/lua/CLuaTimerManager.cpp index 4e0aeaacb5a..9416adebce9 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaTimerManager.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaTimerManager.cpp @@ -16,14 +16,18 @@ void CLuaTimerManager::DoPulse(CLuaMain* pLuaMain) assert(m_ProcessQueue.empty()); assert(!m_pPendingDelete); assert(!m_pProcessingTimer); - CTickCount llCurrentTime = CTickCount::Now(); + CPerformanceRecorder::Sample sample("CLuaTimerManager::DoPulse"); + sample.SetArg("Resource name", pLuaMain->GetResource()->GetName()); // Use a separate queue to avoid trouble // What kind of problems are we trying to avoid? Doing a copy each frame isn't quite efficient for (CFastList::const_iterator iter = m_TimerList.begin(); iter != m_TimerList.end(); ++iter) m_ProcessQueue.push_back(*iter); + int executed = 0; + int timersCount = m_ProcessQueue.size(); + while (!m_ProcessQueue.empty()) { m_pProcessingTimer = m_ProcessQueue.front(); @@ -36,8 +40,13 @@ void CLuaTimerManager::DoPulse(CLuaMain* pLuaMain) // Is the time up and is not being deleted if (llCurrentTime >= (llStartTime + llDelay)) { + executed++; + CPerformanceRecorder::Sample timerSample("Timer executed"); // Set our debug info - g_pGame->GetScriptDebugging()->SaveLuaDebugInfo(m_pProcessingTimer->GetLuaDebugInfo()); + const SLuaDebugInfo& debugInfo = m_pProcessingTimer->GetLuaDebugInfo(); + timerSample.SetArg("File",debugInfo.strFile); + timerSample.SetArg("Line",debugInfo.iLine); + g_pGame->GetScriptDebugging()->SaveLuaDebugInfo(debugInfo); m_pProcessingTimer->ExecuteTimer(pLuaMain); // Reset @@ -69,6 +78,8 @@ void CLuaTimerManager::DoPulse(CLuaMain* pLuaMain) else m_pProcessingTimer = NULL; } + sample.SetArg("Timers count", timersCount); + sample.SetArg("Executed timers", executed); } void CLuaTimerManager::RemoveTimer(CLuaTimer* pLuaTimer) diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp index cba5074706f..766f091f991 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp +++ b/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp @@ -1546,6 +1546,7 @@ int CLuaElementDefs::setElementID(lua_State* luaVM) int CLuaElementDefs::setElementData(lua_State* luaVM) { + CPerformanceRecorder::FunctionSample sample("setElementData"); // bool setElementData ( element theElement, string key, var value, [var syncMode = true] ) CElement* pElement; SString strKey; @@ -1579,6 +1580,8 @@ int CLuaElementDefs::setElementData(lua_State* luaVM) strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); } + sample.SetArg("Sync type", EnumToString(syncType)); + sample.SetArg("Key", strKey); if (CStaticFunctionDefinitions::SetElementData(pElement, strKey, value, syncType)) { lua_pushboolean(luaVM, true); diff --git a/Server/mods/deathmatch/logic/net/CNetBuffer.cpp b/Server/mods/deathmatch/logic/net/CNetBuffer.cpp index b2c9f20aad1..ca05fed0cf6 100644 --- a/Server/mods/deathmatch/logic/net/CNetBuffer.cpp +++ b/Server/mods/deathmatch/logic/net/CNetBuffer.cpp @@ -220,6 +220,7 @@ void CNetServerBuffer::StopNetwork() /////////////////////////////////////////////////////////////////////////// void CNetServerBuffer::DoPulse() { + CPerformanceRecorder::Sample sample("CNetServerBuffer::DoPulse"); // Schedule a net pulse SDoPulseArgs* pArgs = new SDoPulseArgs(); AddCommandAndFree(pArgs); diff --git a/Server/mods/deathmatch/premake5.lua b/Server/mods/deathmatch/premake5.lua index 48c3cead82a..5070e3c6a8b 100644 --- a/Server/mods/deathmatch/premake5.lua +++ b/Server/mods/deathmatch/premake5.lua @@ -26,6 +26,7 @@ project "Deathmatch" "../../../Shared/mods/deathmatch/logic", "../../../Shared/animation", "../../../Shared/publicsdk/include", + "../../../Shared/sdk/profiler", "../../../vendor/sparsehash/src/", "logic", "utils", @@ -51,6 +52,7 @@ project "Deathmatch" "../../../Shared/mods/deathmatch/logic/**.h", "../../../Shared/animation/CEasingCurve.cpp", "../../../Shared/animation/CPositionRotationAnimation.cpp", + "../../../Shared/sdk/profiler/CPerformanceRecorder.cpp", -- Todo: Replace these two by using the CryptoPP functions instead "../../../vendor/bochs/bochs_internal/bochs_crc32.cpp", } diff --git a/Server/mods/deathmatch/utils/CFunctionUseLogger.cpp b/Server/mods/deathmatch/utils/CFunctionUseLogger.cpp index 9cfc6fe4213..71420306edf 100644 --- a/Server/mods/deathmatch/utils/CFunctionUseLogger.cpp +++ b/Server/mods/deathmatch/utils/CFunctionUseLogger.cpp @@ -32,6 +32,7 @@ CFunctionUseLogger::~CFunctionUseLogger() // void CFunctionUseLogger::Pulse() { + CPerformanceRecorder::Sample sample("CFunctionUseLogger::Pulse"); if (!m_FuncCallRecordMap.empty()) MaybeFlush(); } diff --git a/Shared/mods/deathmatch/logic/CLatentTransferManager.cpp b/Shared/mods/deathmatch/logic/CLatentTransferManager.cpp index a9bf07ecb7a..966a8f729bc 100644 --- a/Shared/mods/deathmatch/logic/CLatentTransferManager.cpp +++ b/Shared/mods/deathmatch/logic/CLatentTransferManager.cpp @@ -42,6 +42,7 @@ CLatentTransferManager::~CLatentTransferManager() /////////////////////////////////////////////////////////////// void CLatentTransferManager::DoPulse() { + CPerformanceRecorder::Sample sample("CLatentTransferManager::DoPulse"); // Update timing info CTickCount tickCountNow = CTickCount::Now(); diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp b/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp index bc1b90618f9..53d54444b4b 100644 --- a/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp +++ b/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp @@ -9,6 +9,7 @@ *****************************************************************************/ #include "StdInc.h" +#include void CLuaUtilDefs::LoadFunctions() { @@ -46,6 +47,13 @@ void CLuaUtilDefs::LoadFunctions() // Utility functions {"gettok", GetTok}, {"tocolor", tocolor}, + + // Performance recorder + {"startRecordPerformance", ArgumentParser}, + {"stopRecordPerformance", ArgumentParser}, + {"clearPerformanceRecorder", ArgumentParser}, + {"isPerformanceRecording", ArgumentParser}, + {"getRecordedSamples", ArgumentParser}, }; // Add functions @@ -711,3 +719,64 @@ int CLuaUtilDefs::tocolor(lua_State* luaVM) lua_pushnumber(luaVM, static_cast(ulColor)); return 1; } + +bool CLuaUtilDefs::StartRecordPerformance() +{ +#ifdef MTA_CLIENT + if (g_pClientGame->GetPerformanceRecorder()->IsDuringPerformanceRecording()) + return false; + + g_pClientGame->GetPerformanceRecorder()->Start(); +#else + if (g_pGame->GetPerformanceRecorder()->IsDuringPerformanceRecording()) + return false; + + g_pGame->GetPerformanceRecorder()->Start(); +#endif + + return true; +} + +bool CLuaUtilDefs::StopRecordPerformance() +{ +#ifdef MTA_CLIENT + if (!g_pClientGame->GetPerformanceRecorder()->IsDuringPerformanceRecording()) + return false; + + g_pClientGame->GetPerformanceRecorder()->Stop(); +#else + if (!g_pGame->GetPerformanceRecorder()->IsDuringPerformanceRecording()) + return false; + + g_pGame->GetPerformanceRecorder()->Stop(); +#endif + return true; +} + +bool CLuaUtilDefs::ClearPerformanceRecorder() +{ +#ifdef MTA_CLIENT + g_pClientGame->GetPerformanceRecorder()->Clear(); +#else + g_pGame->GetPerformanceRecorder()->Clear(); +#endif + return true; +} + +bool CLuaUtilDefs::IsPerformanceRecording() +{ +#ifdef MTA_CLIENT + return g_pClientGame->GetPerformanceRecorder()->IsDuringPerformanceRecording(); +#else + return g_pGame->GetPerformanceRecorder()->IsDuringPerformanceRecording(); +#endif +} + +std::string CLuaUtilDefs::GetRecordedSamples() +{ +#ifdef MTA_CLIENT + return g_pClientGame->GetPerformanceRecorder()->GetResult(); +#else + return g_pGame->GetPerformanceRecorder()->GetResult(); +#endif +} diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h b/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h index 849011a94d4..fa3a9ad2ae4 100644 --- a/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h +++ b/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h @@ -52,4 +52,10 @@ class CLuaUtilDefs : public CLuaDefs // Utility functions LUA_DECLARE(GetTok); LUA_DECLARE(tocolor); + + static bool StartRecordPerformance(); + static bool StopRecordPerformance(); + static bool ClearPerformanceRecorder(); + static bool IsPerformanceRecording(); + static std::string GetRecordedSamples(); }; diff --git a/Shared/premake5.lua b/Shared/premake5.lua index 76c3c52c84a..309e24907ac 100644 --- a/Shared/premake5.lua +++ b/Shared/premake5.lua @@ -10,6 +10,7 @@ project "Shared" vpaths { ["Headers/*"] = { "**.h", "**.hpp" }, + ["Sources/*"] = "**.cpp", ["*"] = "premake5.lua", } diff --git a/Shared/sdk/profiler/CPerformanceRecorder.cpp b/Shared/sdk/profiler/CPerformanceRecorder.cpp new file mode 100644 index 00000000000..2b6834ffefb --- /dev/null +++ b/Shared/sdk/profiler/CPerformanceRecorder.cpp @@ -0,0 +1,205 @@ +#include "StdInc.h" + +CPerformanceRecorder::CPerformanceRecorder() : m_start(GetTimeUs()), m_samples(json_object_new_array()) +{ +} + +CPerformanceRecorder::~CPerformanceRecorder() +{ + if (m_samples) + { + json_object_put(m_samples); + m_samples = nullptr; + } +} + +void CPerformanceRecorder::Start() +{ + m_bRecordPerformance = true; +} + +void CPerformanceRecorder::Stop() +{ + m_bRecordPerformance = false; +} + +void CPerformanceRecorder::Clear() +{ + m_start = GetTimeUs(); + json_object_put(m_samples); + m_samples = json_object_new_array(); +} + +void CPerformanceRecorder::EnterScope() +{ + m_stackSamples.push(json_object_new_object()); +} + +void CPerformanceRecorder::ExitScope() +{ + assert(m_stackSamples.size() > 0); + json_object_array_add(m_samples, m_stackSamples.top()); + m_stackSamples.pop(); +} + +json_object* CPerformanceRecorder::GetSampleObject() +{ + assert(m_stackSamples.size() > 0); + return m_stackSamples.top(); +} + +std::string CPerformanceRecorder::GetResult() +{ + int flags = JSON_C_TO_STRING_PLAIN; + return json_object_to_json_string_ext(m_samples, flags); +} + +CPerformanceRecorder::FunctionSample::FunctionSample(const char* name) : Sample(name) +{ +} + +CPerformanceRecorder::FunctionSample::~FunctionSample() +{ + if (!m_enabled) + return; + +#ifdef MTA_CLIENT + json_object* object = g_pClientGame->GetPerformanceRecorder()->GetSampleObject(); +#else + json_object* object = g_pGame->GetPerformanceRecorder()->GetSampleObject(); +#endif + json_object_object_add(object, "name", json_object_new_string(("Native function: " + m_name).c_str())); + json_object_object_add(object, "cat", json_object_new_string("function")); +} + +CPerformanceRecorder::EventSample::EventSample(const char* name) : Sample(name) +{ +} + +CPerformanceRecorder::EventSample::~EventSample() +{ + if (!m_enabled) + return; + +#ifdef MTA_CLIENT + json_object* object = g_pClientGame->GetPerformanceRecorder()->GetSampleObject(); +#else + json_object* object = g_pGame->GetPerformanceRecorder()->GetSampleObject(); +#endif + json_object_object_add(object, "name", json_object_new_string(("Event: " + m_name).c_str())); + json_object_object_add(object, "cat", json_object_new_string("event")); +} + +CPerformanceRecorder::Sample::Sample(const char* name) : m_name(name), m_startTime(GetTimeUs()) +{ + +#ifdef MTA_CLIENT + if (!g_pClientGame) + return; + + m_enabled = g_pClientGame->GetPerformanceRecorder()->m_bRecordPerformance; + if (m_enabled) + g_pClientGame->GetPerformanceRecorder()->EnterScope(); +#else + if (!g_pGame) + return; + + m_enabled = g_pGame->GetPerformanceRecorder()->m_bRecordPerformance; + if (m_enabled) + g_pGame->GetPerformanceRecorder()->EnterScope(); +#endif +} + +void CPerformanceRecorder::Sample::SetArg(const char* szKey, const char* value) +{ + if (!m_enabled) + return; + +#ifdef MTA_CLIENT + json_object* object = g_pClientGame->GetPerformanceRecorder()->GetSampleObject(); +#else + json_object* object = g_pGame->GetPerformanceRecorder()->GetSampleObject(); +#endif + json_object* args = json_object_object_get(object, "args"); + if (!args) + { + args = json_object_new_object(); + json_object_object_add(object, "args", args); + } + + json_object_object_add(args, szKey, json_object_new_string(value)); +} + +void CPerformanceRecorder::Sample::SetBool(const char* szKey, bool value) +{ + if (!m_enabled) + return; + +#ifdef MTA_CLIENT + json_object* object = g_pClientGame->GetPerformanceRecorder()->GetSampleObject(); +#else + json_object* object = g_pGame->GetPerformanceRecorder()->GetSampleObject(); +#endif + json_object* args = json_object_object_get(object, "args"); + if (!args) + { + args = json_object_new_object(); + json_object_object_add(object, "args", args); + } + + json_object_object_add(args, szKey, json_object_new_boolean(value)); +} + +void CPerformanceRecorder::Sample::SetArg(const char* szKey, int64_t value) +{ + if (!m_enabled) + return; + +#ifdef MTA_CLIENT + json_object* object = g_pClientGame->GetPerformanceRecorder()->GetSampleObject(); +#else + json_object* object = g_pGame->GetPerformanceRecorder()->GetSampleObject(); +#endif + json_object* args = json_object_object_get(object, "args"); + if (!args) + { + args = json_object_new_object(); + json_object_object_add(object, "args", args); + } + + json_object_object_add(args, szKey, json_object_new_int64(value)); +} + +void CPerformanceRecorder::Sample::Exit() +{ + if (!m_enabled) + return; + +#ifdef MTA_CLIENT + json_object* object = g_pClientGame->GetPerformanceRecorder()->GetSampleObject(); +#else + json_object* object = g_pGame->GetPerformanceRecorder()->GetSampleObject(); +#endif + + if (!json_object_object_get(object, "name")) + json_object_object_add(object, "name", json_object_new_string(m_name.c_str())); + if (!json_object_object_get(object, "cat")) + json_object_object_add(object, "cat", json_object_new_string("default")); + + json_object_object_add(object, "ph", json_object_new_string("X")); + json_object_object_add(object, "ts", json_object_new_int64(m_startTime)); // int64 prevents int32 overflowing in this case. + json_object_object_add(object, "dur", json_object_new_int(GetTimeUs() - m_startTime)); + json_object_object_add(object, "tid", json_object_new_int(getpid())); + +#ifdef MTA_CLIENT + g_pClientGame->GetPerformanceRecorder()->ExitScope(); +#else + g_pGame->GetPerformanceRecorder()->ExitScope(); +#endif + m_enabled = false; +} + +CPerformanceRecorder::Sample::~Sample() +{ + Exit(); +} diff --git a/Shared/sdk/profiler/CPerformanceRecorder.h b/Shared/sdk/profiler/CPerformanceRecorder.h new file mode 100644 index 00000000000..aeb193c05cb --- /dev/null +++ b/Shared/sdk/profiler/CPerformanceRecorder.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +class CPerformanceRecorder +{ + friend class Sample; + +public: + CPerformanceRecorder(); + ~CPerformanceRecorder(); + + void Start(); + void Stop(); + void Clear(); + void EnterScope(); + void ExitScope(); + json_object* GetSampleObject(); + + bool IsDuringPerformanceRecording() const { return m_bRecordPerformance; } + std::string GetResult(); + + class Sample + { + public: + Sample(const char* name); + virtual ~Sample(); + + void SetArg(const char* szKey, const char* value); + void SetArg(const char* szKey, int64_t value); + void SetBool(const char* szKey, bool value); + + void Exit(); + + protected: + std::string m_name; + bool m_enabled = false; + + private: + TIMEUS m_startTime = 0; + }; + + class FunctionSample : public Sample + { + public: + FunctionSample(const char* name); + ~FunctionSample(); + }; + + class EventSample : public Sample + { + public: + EventSample(const char* name); + ~EventSample(); + }; + +private: + bool m_bRecordPerformance = false; + TIMEUS m_start; + + std::stack m_stackSamples; + json_object* m_samples; +};