Add support for ANSI name and variable name for named profiler events
This commit is contained in:
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user