Add Tracy profiler support

This commit is contained in:
Wojtek Figat
2021-05-02 11:24:42 +02:00
parent 1a261597c5
commit 543d1a3c0e
45 changed files with 14752 additions and 108 deletions

View File

@@ -357,7 +357,15 @@ bool Asset::onLoad(LoadAssetTask* task)
Locker.Lock();
// Load asset
const LoadResult result = loadAsset();
LoadResult result;
{
#if TRACY_ENABLE
ZoneScoped;
const StringView name(GetPath());
ZoneName(*name, name.Length());
#endif
result = loadAsset();
}
const bool isLoaded = result == LoadResult::Ok;
const bool failed = !isLoaded;
_loadFailed = failed;

View File

@@ -166,6 +166,7 @@ int32 Engine::Main(const Char* cmdLine)
}
}
#endif
// App paused logic
if (Platform::GetIsPaused())
{
@@ -202,6 +203,7 @@ int32 Engine::Main(const Char* cmdLine)
{
OnDraw();
Time::OnEndDraw();
FrameMark;
canDraw = false;
}

View File

@@ -14,7 +14,7 @@
#include "Engine/Core/Math/Rectangle.h"
#include "Engine/Core/Utilities.h"
#if COMPILE_WITH_PROFILER
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Profiler/ProfilerCPU.h"
#endif
#include "Engine/Threading/Threading.h"
#include "Engine/Engine/CommandLine.h"
@@ -165,6 +165,44 @@ void PlatformBase::Exit()
{
}
#if COMPILE_WITH_PROFILER
void PlatformBase::OnMemoryAlloc(void* ptr, uint64 size)
{
if (!ptr)
return;
#if TRACY_ENABLE
// Track memory allocation in Tracy
//tracy::Profiler::MemAlloc(ptr, size, false);
tracy::Profiler::MemAllocCallstack(ptr, size, 12, false);
#endif
// Register allocation during the current CPU event
auto thread = ProfilerCPU::GetCurrentThread();
if (thread != nullptr && thread->Buffer.GetCount() != 0)
{
auto& activeEvent = thread->Buffer.Last().Event();
if (activeEvent.End < ZeroTolerance)
{
activeEvent.NativeMemoryAllocation += (int32)size;
}
}
}
void PlatformBase::OnMemoryFree(void* ptr)
{
if (!ptr)
return;
#if TRACY_ENABLE
// Track memory allocation in Tracy
tracy::Profiler::MemFree(ptr, false);
#endif
}
#endif
void* PlatformBase::AllocatePages(uint64 numPages, uint64 pageSize)
{
// Fallback to the default memory allocation
@@ -460,15 +498,6 @@ Vector2 PlatformBase::GetVirtualDesktopSize()
return Platform::GetVirtualDesktopBounds().Size;
}
#if COMPILE_WITH_PROFILER
void PlatformBase::TrackAllocation(uint64 size)
{
ProfilerMemory::OnAllocation((uint32)size, false);
}
#endif
void PlatformBase::GetEnvironmentVariables(Dictionary<String, String>& result)
{
// Not supported

View File

@@ -299,7 +299,8 @@ public:
static void Prefetch(void const* ptr) = delete;
#if COMPILE_WITH_PROFILER
static void TrackAllocation(uint64 size);
static void OnMemoryAlloc(void* ptr, uint64 size);
static void OnMemoryFree(void* ptr);
#endif
/// <summary>

View File

@@ -4,6 +4,10 @@
#include "Engine/Threading/IRunnable.h"
#include "Engine/Threading/ThreadRegistry.h"
#include "Engine/Core/Log.h"
#if TRACY_ENABLE
#include "Engine/Core/Math/Math.h"
#include <ThirdParty/tracy/Tracy.h>
#endif
Delegate<Thread*> ThreadBase::ThreadStarting;
Delegate<Thread*, int32> ThreadBase::ThreadExiting;
@@ -70,6 +74,13 @@ int32 ThreadBase::Run()
ASSERT(_runnable);
const auto thread = static_cast<Thread*>(this);
_id = Platform::GetCurrentThreadID();
#if TRACY_ENABLE
char threadName[100];
const int32 threadNameLength = Math::Min<int32>(ARRAY_COUNT(threadName) - 1, _name.Length());
StringUtils::ConvertUTF162ANSI(*_name, threadName, threadNameLength);
threadName[threadNameLength] = 0;
tracy::SetThreadName(threadName);
#endif
ThreadRegistry::Add(thread);
ThreadStarting(thread);
int32 exitCode = 1;

View File

@@ -305,14 +305,18 @@ void Win32Platform::Prefetch(void const* ptr)
void* Win32Platform::Allocate(uint64 size, uint64 alignment)
{
void* ptr = _aligned_malloc((size_t)size, (size_t)alignment);
#if COMPILE_WITH_PROFILER
TrackAllocation(size);
OnMemoryAlloc(ptr, size);
#endif
return _aligned_malloc((size_t)size, (size_t)alignment);
return ptr;
}
void Win32Platform::Free(void* ptr)
{
#if COMPILE_WITH_PROFILER
OnMemoryFree(ptr);
#endif
_aligned_free(ptr);
}

View File

@@ -37,9 +37,30 @@ namespace
int32 SystemDpi = 96;
#if CRASH_LOG_ENABLE
CriticalSection SymLocker;
#if TRACY_ENABLE
bool SymInitialized = true;
#else
bool SymInitialized = false;
bool SymModulesDirty = true;
#endif
Array<String> SymbolsPath;
void OnSymbolsPathModified()
{
if (!SymInitialized)
return;
HANDLE process = GetCurrentProcess();
SymCleanup(process);
String symbolSearchPath;
for (auto& path : SymbolsPath)
{
symbolSearchPath += path;
symbolSearchPath += ";";
}
symbolSearchPath += Platform::GetWorkingDirectory();
SymInitializeW(process, *symbolSearchPath, TRUE);
//SymSetSearchPathW(process, *symbolSearchPath);
//SymRefreshModuleList(process);
}
#endif
}
@@ -378,6 +399,20 @@ void WindowsPlatform::PreInit(void* hInstance)
Error(TEXT("OLE initalization failed!"));
exit(-1);
}
#if CRASH_LOG_ENABLE
TCHAR buffer[MAX_PATH] = { 0 };
SymLocker.Lock();
if (::GetModuleFileNameW(::GetModuleHandleW(nullptr), buffer, MAX_PATH))
SymbolsPath.Add(StringUtils::GetDirectoryName(buffer));
if (::GetEnvironmentVariableW(TEXT("_NT_SYMBOL_PATH"), buffer, MAX_PATH))
SymbolsPath.Add(StringUtils::GetDirectoryName(buffer));
DWORD options = SymGetOptions();
options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS;
SymSetOptions(options);
OnSymbolsPathModified();
SymLocker.Unlock();
#endif
}
bool WindowsPlatform::IsWindows10()
@@ -604,11 +639,13 @@ void WindowsPlatform::Exit()
{
#if CRASH_LOG_ENABLE
SymLocker.Lock();
#if !TRACY_ENABLE
if (SymInitialized)
{
SymInitialized = false;
SymCleanup(GetCurrentProcess());
}
#endif
SymbolsPath.Resize(0);
SymLocker.Unlock();
#endif
@@ -650,25 +687,20 @@ void WindowsPlatform::SetHighDpiAwarenessEnabled(bool enable)
const HMODULE shCoreDll = LoadLibraryW(L"Shcore.dll");
if (!shCoreDll)
return;
typedef enum _PROCESS_DPI_AWARENESS
{
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS;
typedef HRESULT (STDAPICALLTYPE *SetProcessDpiAwarenessProc)(PROCESS_DPI_AWARENESS Value);
const SetProcessDpiAwarenessProc setProcessDpiAwareness = (SetProcessDpiAwarenessProc)GetProcAddress(shCoreDll, "SetProcessDpiAwareness");
if (setProcessDpiAwareness)
{
setProcessDpiAwareness(enable ? PROCESS_PER_MONITOR_DPI_AWARE : PROCESS_DPI_UNAWARE);
}
SystemDpi = CalculateDpi(shCoreDll);
FreeLibrary(shCoreDll);
::FreeLibrary(shCoreDll);
}
BatteryInfo WindowsPlatform::GetBatteryInfo()
@@ -1108,10 +1140,9 @@ void* WindowsPlatform::LoadLibrary(const Char* filename)
SymLocker.Lock();
const auto folder = StringUtils::GetDirectoryName(filename);
if (!SymbolsPath.Contains(folder))
SymbolsPath.Add(folder);
if (SymInitialized)
{
SymModulesDirty = true;
SymbolsPath.Add(folder);
OnSymbolsPathModified();
}
SymLocker.Unlock();
#endif
@@ -1131,46 +1162,16 @@ Array<PlatformBase::StackFrame> WindowsPlatform::GetStackFrames(int32 skipCount,
if (!SymInitialized)
{
SymInitialized = true;
// Build search path
String symbolSearchPath;
TCHAR ModulePath[MAX_PATH] = { 0 };
if (::GetModuleFileName(::GetModuleHandle(nullptr), ModulePath, MAX_PATH))
{
symbolSearchPath += StringUtils::GetDirectoryName(ModulePath);
symbolSearchPath += ";";
}
for (auto& path : SymbolsPath)
{
symbolSearchPath += path;
symbolSearchPath += ";";
}
String _NT_SYMBOL_PATH;
if (!Platform::GetEnvironmentVariable(TEXT("_NT_SYMBOL_PATH"), _NT_SYMBOL_PATH))
{
symbolSearchPath += _NT_SYMBOL_PATH;
symbolSearchPath += ";";
}
symbolSearchPath += Platform::GetWorkingDirectory();
symbolSearchPath += ";";
DWORD options = SymGetOptions();
options |= SYMOPT_LOAD_LINES;
options |= SYMOPT_FAIL_CRITICAL_ERRORS;
options |= SYMOPT_DEFERRED_LOADS;
options |= SYMOPT_EXACT_SYMBOLS;
SymSetOptions(options);
SymInitializeW(process, *symbolSearchPath, TRUE);
}
// Refresh modules if needed
if (SymModulesDirty)
{
SymModulesDirty = false;
SymRefreshModuleList(process);
}
// Capture the context if missing
/*EXCEPTION_POINTERS exceptionPointers;
CONTEXT contextData;

View File

@@ -27,5 +27,13 @@ public class Profiler : EngineModule
options.PrivateDependencies.Clear();
options.PublicDefinitions.Add("COMPILE_WITH_PROFILER");
// Tracy profiling tools
switch (options.Platform.Target)
{
case TargetPlatform.Windows:
options.PublicDependencies.Add("tracy");
break;
}
}
}

View File

@@ -8,6 +8,7 @@
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Scripting/ScriptingType.h"
#include <ThirdParty/tracy/Tracy.h>
#if COMPILE_WITH_PROFILER
@@ -393,12 +394,22 @@ struct TIsPODType<ProfilerCPU::Event>
};
// Shortcut macros for profiling a single code block execution on CPU
#define PROFILE_CPU_NAMED(name) ScopeProfileBlockCPU ProfileBlockCPU(TEXT(name))
// Use ZoneTransient for Tracy for code that can be hot-reloaded (eg. in Editor)
#if USE_EDITOR
#define PROFILE_CPU_NAMED(name) ZoneTransientN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(name))
#else
#define PROFILE_CPU_NAMED(name) ZoneNamedN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(name))
#endif
#if defined(_MSC_VER)
#define PROFILE_CPU() ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__))
#if USE_EDITOR
#define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__))
#else
#define PROFILE_CPU() \
#define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__))
#endif
#else
#define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); \
const char* _functionName = __FUNCTION__; \
const int32 _functionNameLength = ARRAY_COUNT(__FUNCTION__); \
Char _functionNameBuffer[_functionNameLength + 1]; \

View File

@@ -1,25 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if COMPILE_WITH_PROFILER
#include "ProfilerMemory.h"
#include "ProfilerCPU.h"
void ProfilerMemory::OnAllocation(int32 bytes, bool isGC)
{
// Register allocation during the current CPU event
auto thread = ProfilerCPU::GetCurrentThread();
if (thread != nullptr && thread->Buffer.GetCount() != 0)
{
auto& activeEvent = thread->Buffer.Last().Event();
if (activeEvent.End < ZeroTolerance)
{
if (isGC)
activeEvent.ManagedMemoryAllocation += bytes;
else
activeEvent.NativeMemoryAllocation += bytes;
}
}
}
#endif

View File

@@ -1,24 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#if COMPILE_WITH_PROFILER
/// <summary>
/// Provides memory allocations measuring methods.
/// </summary>
class FLAXENGINE_API ProfilerMemory
{
public:
/// <summary>
/// Called on memory allocation.
/// </summary>
/// <param name="bytes">The allocated bytes count.</param>
/// <param name="isGC">True if allocation comes from the Garbage Collector, otherwise false.</param>
static void OnAllocation(int32 bytes, bool isGC);
};
#endif

View File

@@ -15,7 +15,6 @@
#include "Engine/Threading/Threading.h"
#include "Engine/Platform/Thread.h"
#include "Engine/Scripting/MException.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include <ThirdParty/mono-2.0/mono/jit/jit.h>
#include <ThirdParty/mono-2.0/mono/utils/mono-counters.h>
@@ -182,7 +181,16 @@ void OnGCAllocation(MonoProfiler* profiler, MonoObject* obj)
#endif
#if COMPILE_WITH_PROFILER
ProfilerMemory::OnAllocation(size, true);
// Register allocation during the current CPU event
auto thread = ProfilerCPU::GetCurrentThread();
if (thread != nullptr && thread->Buffer.GetCount() != 0)
{
auto& activeEvent = thread->Buffer.Last().Event();
if (activeEvent.End < ZeroTolerance)
{
activeEvent.ManagedMemoryAllocation += size;
}
}
#endif
}