Add support for ANSI name and variable name for named profiler events

This commit is contained in:
Wojtek Figat
2021-05-04 00:17:01 +02:00
parent aa7c467679
commit 17e3625e5d
3 changed files with 126 additions and 73 deletions

View File

@@ -5,6 +5,29 @@ using FlaxEditor.GUI;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
namespace FlaxEngine
{
partial class ProfilerCPU
{
partial struct Event
{
/// <summary>
/// Gets the event name.
/// </summary>
public unsafe string Name
{
get
{
fixed (char* name = &Name0)
{
return new string(name);
}
}
}
}
}
}
namespace FlaxEditor.Windows.Profiler namespace FlaxEditor.Windows.Profiler
{ {
/// <summary> /// <summary>
@@ -204,7 +227,7 @@ namespace FlaxEditor.Windows.Profiler
{ {
var e = events[i]; var e = events[i];
if (e.Depth == 0 && new string(e.Name) == "Update") if (e.Depth == 0 && e.Name == "Update")
{ {
return new ViewRange(ref e); return new ViewRange(ref e);
} }
@@ -225,7 +248,7 @@ namespace FlaxEditor.Windows.Profiler
double scale = 100.0; double scale = 100.0;
float x = (float)((e.Start - startTime) * scale); float x = (float)((e.Start - startTime) * scale);
float width = (float)(length * scale); float width = (float)(length * scale);
string name = new string(e.Name).Replace("::", "."); string name = e.Name.Replace("::", ".");
var control = new Timeline.Event(x + xOffset, e.Depth + depthOffset, width) var control = new Timeline.Event(x + xOffset, e.Depth + depthOffset, width)
{ {
@@ -399,7 +422,7 @@ namespace FlaxEditor.Windows.Profiler
subEventsMemoryTotal += sub.ManagedMemoryAllocation + e.NativeMemoryAllocation; subEventsMemoryTotal += sub.ManagedMemoryAllocation + e.NativeMemoryAllocation;
} }
string name = new string(e.Name).Replace("::", "."); string name = e.Name.Replace("::", ".");
var row = new Row var row = new Row
{ {

View File

@@ -10,6 +10,20 @@ THREADLOCAL ProfilerCPU::Thread* ProfilerCPU::Thread::Current = nullptr;
Array<ProfilerCPU::Thread*, InlinedAllocation<64>> ProfilerCPU::Threads; Array<ProfilerCPU::Thread*, InlinedAllocation<64>> ProfilerCPU::Threads;
bool ProfilerCPU::Enabled = false; bool ProfilerCPU::Enabled = false;
ProfilerCPU::EventBuffer::EventBuffer()
{
_capacity = Math::RoundUpToPowerOf2(10 * 1000);
_capacityMask = _capacity - 1;
_data = NewArray<Event>(_capacity);
_head = 0;
_count = 0;
}
ProfilerCPU::EventBuffer::~EventBuffer()
{
DeleteArray(_data, _capacity);
}
void ProfilerCPU::EventBuffer::Extract(Array<Event>& data, bool withRemove) void ProfilerCPU::EventBuffer::Extract(Array<Event>& data, bool withRemove)
{ {
data.Clear(); data.Clear();
@@ -87,28 +101,23 @@ void ProfilerCPU::EventBuffer::Extract(Array<Event>& data, bool withRemove)
Platform::MemoryCopy(data.Get() + spaceLeftCount, &_data[0], overflow * sizeof(Event)); Platform::MemoryCopy(data.Get() + spaceLeftCount, &_data[0], overflow * sizeof(Event));
} }
int32 ProfilerCPU::Thread::BeginEvent(const Char* name) int32 ProfilerCPU::Thread::BeginEvent()
{ {
const double time = Platform::GetTimeSeconds() * 1000.0; const double time = Platform::GetTimeSeconds() * 1000.0;
const auto index = Buffer.Add(); const auto index = Buffer.Add();
Event& e = Buffer.Get(index); Event& e = Buffer.Get(index);
e.Start = time; e.Start = time;
e.End = 0; e.End = 0;
e.Depth = _depth++; e.Depth = _depth++;
e.NativeMemoryAllocation = 0; e.NativeMemoryAllocation = 0;
e.ManagedMemoryAllocation = 0; e.ManagedMemoryAllocation = 0;
e.Name = name;
return index; return index;
} }
void ProfilerCPU::Thread::EndEvent(int32 index) void ProfilerCPU::Thread::EndEvent(int32 index)
{ {
const double time = Platform::GetTimeSeconds() * 1000.0; const double time = Platform::GetTimeSeconds() * 1000.0;
_depth--; _depth--;
Event& e = Buffer.Get(index); Event& e = Buffer.Get(index);
e.End = time; e.End = time;
} }
@@ -123,7 +132,7 @@ ProfilerCPU::Thread* ProfilerCPU::GetCurrentThread()
return Enabled ? Thread::Current : nullptr; return Enabled ? Thread::Current : nullptr;
} }
int32 ProfilerCPU::BeginEvent(const Char* name) int32 ProfilerCPU::BeginEvent()
{ {
if (!Enabled) if (!Enabled)
return -1; return -1;
@@ -142,8 +151,45 @@ int32 ProfilerCPU::BeginEvent(const Char* name)
Thread::Current = thread; Thread::Current = thread;
Threads.Add(thread); Threads.Add(thread);
} }
return thread->BeginEvent();
}
return thread->BeginEvent(name); int32 ProfilerCPU::BeginEvent(const Char* name, bool transient)
{
if (!Enabled)
return -1;
const auto index = BeginEvent();
const auto thread = Thread::Current;
auto& e = thread->Buffer.Get(index);
auto dst = e.Name;
auto src = name;
if (src)
{
auto end = dst + ARRAY_COUNT(e.Name) - 1;
while (*src && dst != end)
*dst++ = *src++;
}
*dst = 0;
return index;
}
int32 ProfilerCPU::BeginEvent(const char* name, bool transient)
{
if (!Enabled)
return -1;
const auto index = BeginEvent();
const auto thread = Thread::Current;
auto& e = thread->Buffer.Get(index);
auto dst = e.Name;
auto src = name;
if (src)
{
auto end = dst + ARRAY_COUNT(e.Name) - 1;
while (*src && dst != end)
*dst++ = *src++;
}
*dst = 0;
return index;
} }
void ProfilerCPU::EndEvent(int32 index) void ProfilerCPU::EndEvent(int32 index)

View File

@@ -5,17 +5,13 @@
#include "Engine/Platform/Platform.h" #include "Engine/Platform/Platform.h"
#include "Engine/Core/NonCopyable.h" #include "Engine/Core/NonCopyable.h"
#include "Engine/Core/Types/String.h" #include "Engine/Core/Types/String.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Math/Math.h" #include "Engine/Core/Math/Math.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Scripting/ScriptingType.h" #include "Engine/Scripting/ScriptingType.h"
#include <ThirdParty/tracy/Tracy.h> #include <ThirdParty/tracy/Tracy.h>
#if COMPILE_WITH_PROFILER #if COMPILE_WITH_PROFILER
// Profiler events buffers capacity (tweaked manually)
#define PROFILER_CPU_EVENTS_FRAMES 10
#define PROFILER_CPU_EVENTS_PER_FRAME 1000
/// <summary> /// <summary>
/// Provides CPU performance measuring methods. /// Provides CPU performance measuring methods.
/// </summary> /// </summary>
@@ -56,10 +52,7 @@ public:
/// </summary> /// </summary>
API_FIELD() int32 ManagedMemoryAllocation; API_FIELD() int32 ManagedMemoryAllocation;
/// <summary> API_FIELD(Private, NoArray) Char Name[100];
/// The name of the event.
/// </summary>
API_FIELD() const Char* Name;
}; };
/// <summary> /// <summary>
@@ -77,26 +70,14 @@ public:
public: public:
EventBuffer() EventBuffer();
{ ~EventBuffer();
_capacity = Math::RoundUpToPowerOf2(PROFILER_CPU_EVENTS_FRAMES * PROFILER_CPU_EVENTS_PER_FRAME);
_capacityMask = _capacity - 1;
_data = NewArray<Event>(_capacity);
_head = 0;
_count = 0;
}
~EventBuffer()
{
DeleteArray(_data, _capacity);
}
public: public:
/// <summary> /// <summary>
/// Gets the amount of the events in the buffer. /// Gets the amount of the events in the buffer.
/// </summary> /// </summary>
/// <returns>The events count.</returns>
FORCE_INLINE int32 GetCount() const FORCE_INLINE int32 GetCount() const
{ {
return _count; return _count;
@@ -170,9 +151,8 @@ public:
public: public:
/// <summary> /// <summary>
/// Checks if iterator is in the end of the collection /// Checks if iterator is in the end of the collection.
/// </summary> /// </summary>
/// <returns>True if is in the end, otherwise false</returns>
bool IsEnd() const bool IsEnd() const
{ {
ASSERT(_buffer); ASSERT(_buffer);
@@ -180,9 +160,8 @@ public:
} }
/// <summary> /// <summary>
/// Checks if iterator is not in the end of the collection /// Checks if iterator is not in the end of the collection.
/// </summary> /// </summary>
/// <returns>True if is not in the end, otherwise false</returns>
bool IsNotEnd() const bool IsNotEnd() const
{ {
ASSERT(_buffer); ASSERT(_buffer);
@@ -287,7 +266,6 @@ public:
/// <summary> /// <summary>
/// Gets the name. /// Gets the name.
/// </summary> /// </summary>
/// <returns>The name.</returns>
FORCE_INLINE const String& GetName() const FORCE_INLINE const String& GetName() const
{ {
return _name; return _name;
@@ -303,9 +281,8 @@ public:
/// <summary> /// <summary>
/// Begins the event running on a this thread. Call EndEvent with index parameter equal to the returned value by BeginEvent function. /// Begins the event running on a this thread. Call EndEvent with index parameter equal to the returned value by BeginEvent function.
/// </summary> /// </summary>
/// <param name="name">The event name.</param>
/// <returns>The event token.</returns> /// <returns>The event token.</returns>
int32 BeginEvent(const Char* name); int32 BeginEvent();
/// <summary> /// <summary>
/// Ends the event running on a this thread. /// Ends the event running on a this thread.
@@ -331,21 +308,34 @@ public:
/// <summary> /// <summary>
/// Determines whether the current (calling) thread is being profiled by the service (it may has no active profile block but is registered). /// Determines whether the current (calling) thread is being profiled by the service (it may has no active profile block but is registered).
/// </summary> /// </summary>
/// <returns><c>true</c> if service is profiling the current thread; otherwise, <c>false</c>.</returns>
static bool IsProfilingCurrentThread(); static bool IsProfilingCurrentThread();
/// <summary> /// <summary>
/// Gets the current thread (profiler service shadow object). /// Gets the current thread (profiler service shadow object).
/// </summary> /// </summary>
/// <returns>The current thread object or null if not profiled yet.</returns>
static Thread* GetCurrentThread(); static Thread* GetCurrentThread();
/// <summary> /// <summary>
/// Begins the event. Call EndEvent with index parameter equal to the returned value by BeginEvent function. /// Begins the event. Call EndEvent with index parameter equal to the returned value by BeginEvent function.
/// </summary> /// </summary>
/// <param name="name">The event name.</param>
/// <returns>The event token.</returns> /// <returns>The event token.</returns>
static int32 BeginEvent(const Char* name); static int32 BeginEvent();
/// <summary>
/// Begins the event. Call EndEvent with index parameter equal to the returned value by BeginEvent function.
/// </summary>
/// <param name="name">The event name.</param>
/// <param name="transient">True if name is transient and should be cached by allocator (not static).</param>
/// <returns>The event token.</returns>
static int32 BeginEvent(const Char* name, bool transient = false);
/// <summary>
/// Begins the event. Call EndEvent with index parameter equal to the returned value by BeginEvent function.
/// </summary>
/// <param name="name">The event name.</param>
/// <param name="transient">True if name is transient and should be cached by allocator (not static).</param>
/// <returns>The event token.</returns>
static int32 BeginEvent(const char* name, bool transient = false);
/// <summary> /// <summary>
/// Ends the event. /// Ends the event.
@@ -354,7 +344,7 @@ public:
static void EndEvent(int32 index); static void EndEvent(int32 index);
/// <summary> /// <summary>
/// Releases resources. Calls to the profiling API after Dispose are not valid /// Releases resources. Calls to the profiling API after Dispose are not valid.
/// </summary> /// </summary>
static void Dispose(); static void Dispose();
}; };
@@ -364,24 +354,19 @@ public:
/// </summary> /// </summary>
struct ScopeProfileBlockCPU struct ScopeProfileBlockCPU
{ {
/// <summary>
/// The event token index.
/// </summary>
int32 Index; int32 Index;
/// <summary> FORCE_INLINE ScopeProfileBlockCPU(const Char* name, bool transient = false)
/// Initializes a new instance of the <see cref="ScopeProfileBlockCPU"/> struct.
/// </summary>
/// <param name="name">The event name.</param>
ScopeProfileBlockCPU(const Char* name)
{ {
Index = ProfilerCPU::BeginEvent(name); Index = ProfilerCPU::BeginEvent(name, transient);
} }
/// <summary> FORCE_INLINE ScopeProfileBlockCPU(const char* name, bool transient = false)
/// Finalizes an instance of the <see cref="ScopeProfileBlockCPU"/> class. {
/// </summary> Index = ProfilerCPU::BeginEvent(name, transient);
~ScopeProfileBlockCPU() }
FORCE_INLINE ~ScopeProfileBlockCPU()
{ {
ProfilerCPU::EndEvent(Index); ProfilerCPU::EndEvent(Index);
} }
@@ -394,28 +379,27 @@ struct TIsPODType<ProfilerCPU::Event>
}; };
// Shortcut macros for profiling a single code block execution on CPU // Shortcut macros for profiling a single code block execution on CPU
// Use ZoneTransient for Tracy for code that can be hot-reloaded (eg. in Editor) // Use ZoneTransient for Tracy for code that can be hot-reloaded (eg. in Editor) or if name can be a variable
#if USE_EDITOR #define PROFILE_CPU_NAMED(name) ZoneTransientN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(name, true)
#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) #if defined(_MSC_VER)
#if USE_EDITOR #if USE_EDITOR
#define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__)) #define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__), false)
#else #else
#define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__)) #define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__))
#endif #endif
#else #else
#define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); \
const char* _functionName = __FUNCTION__; \ #if USE_EDITOR
const int32 _functionNameLength = ARRAY_COUNT(__FUNCTION__); \ #define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(__FUNCTION__)
Char _functionNameBuffer[_functionNameLength + 1]; \ #else
StringUtils::ConvertANSI2UTF16(_functionName, _functionNameBuffer, _functionNameLength); \ #define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(_functionNameBuffer)
_functionNameBuffer[_functionNameLength] = 0; \ #endif
ScopeProfileBlockCPU ProfileBlockCPU(_functionNameBuffer)
#endif #endif
#else #else