浏览代码

easy_profiler integration

Rokas Kupstys 8 年之前
父节点
当前提交
cef1940b0b

+ 8 - 2
Build/CMake/Modules/AtomicCommon.cmake

@@ -185,10 +185,16 @@ endmacro()
 macro(setup_executable)
     cmake_parse_arguments(ARG "PRIVATE;TOOL;NODEPS" "" "" ${ARGN})
     check_source_files()
-
     add_executable(${TARGET_NAME} ${ARG_UNPARSED_ARGUMENTS} ${SOURCE_FILES})
-
     setup_target()
+    if (ARG_TOOL)
+        if (DEFINED ATOMIC_TOOL_DIR)
+            set (TOOL_DIR ${ATOMIC_TOOL_DIR})
+        else ()
+            set (TOOL_DIR ${ATOMIC_SOURCE_DIR}/Artifacts/Build/${TARGET_NAME})
+        endif ()
+        set_property(TARGET ${TARGET_NAME} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${TOOL_DIR})
+    endif ()
 endmacro()
 
 # Macro for replacing substrings in every variable specified in the list.

+ 8 - 0
CMakeLists.txt

@@ -10,6 +10,14 @@ include(AtomicGit)
 include(AtomicUtils)
 include(AtomicCommon)
 
+if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
+    set (ATOMIC_RELEASE_OFF OFF)
+    set (ATOMIC_RELEASE_ON ON)
+else ()
+    set (ATOMIC_RELEASE_OFF ON)
+    set (ATOMIC_RELEASE_ON OFF)
+endif ()
+
 add_definitions(-DATOMIC_ROOT_SOURCE_DIR="${ATOMIC_SOURCE_DIR}" -DATOMIC_ROOT_BUILD_DIR="${CMAKE_BINARY_DIR}")
 
 if (NOT DEFINED ATOMIC_DEV_BUILD)

+ 28 - 0
Script/AtomicNET/AtomicNET/Core/Profiler.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+    public partial class Profiler : AObject
+    {
+        public static void Block(string name, Action block, uint color = 0xffffecb3,
+                                 ProfilerBlockStatus status = ProfilerBlockStatus.ON,
+                                 [CallerFilePath] string file = "",
+                                 [CallerLineNumber] int line = 0)
+        {
+#if ATOMIC_PROFILING
+            var profiler = AtomicNET.Context.GetProfiler();
+            if (profiler != null)
+                csi_Atomic_Profiler_BeginBlock(profiler, name, file, line, color, (byte)status);
+#endif
+            block();
+#if ATOMIC_PROFILING
+            profiler?.EndBlock();
+#endif
+        }
+
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern void csi_Atomic_Profiler_BeginBlock(IntPtr self, string name, string file, int line, uint argb, byte status);
+    }
+}

+ 4 - 3
Script/AtomicNET/AtomicNETProject.json

@@ -23,6 +23,7 @@
             "name": "AtomicNET",
             "atomicNET" : true,
             "outputType" : "Library",
+            "defineConstants" : ["ATOMIC_PROFILING"],
             "rootNamespace" : "AtomicEngine",
             "assemblyName" : "AtomicNET",
             "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\Portable\\",
@@ -44,7 +45,7 @@
             "assemblyDocFile" : true,
             "platforms" : ["desktop"],
             "outputType" : "Library",
-            "defineConstants" : ["ATOMIC_DESKTOP"],
+            "defineConstants" : ["ATOMIC_DESKTOP", "ATOMIC_PROFILING"],
             "rootNamespace" : "AtomicGameEngine",
             "assemblyName" : "AtomicNET",
             "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\Desktop\\",
@@ -87,7 +88,7 @@
             "atomicNET" : true,
             "platforms" : ["android"],
             "outputType" : "Library",
-            "defineConstants" : ["ATOMIC_ANDROID"],
+            "defineConstants" : ["ATOMIC_ANDROID", "ATOMIC_PROFILING"],
             "rootNamespace" : "AtomicGameEngine",
             "assemblyName" : "AtomicNET",
             "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\Android\\",
@@ -113,7 +114,7 @@
             "platforms" : ["ios"],
             "outputType" : "Library",
             "projectTypeGuids" :[ "8FFB629D-F513-41CE-95D2-7ECE97B6EEEC", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC" ],
-            "defineConstants" : ["ATOMIC_IOS"],
+            "defineConstants" : ["ATOMIC_IOS", "ATOMIC_PROFILING"],
             "rootNamespace" : "AtomicGameEngine",
             "assemblyName" : "AtomicNET",
             "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\iOS\\",

+ 3 - 3
Script/AtomicNET/AtomicProject.json

@@ -35,7 +35,7 @@
             "name": "$ATOMIC_PROJECT_NAME$.Desktop",
             "platforms" : ["desktop"],
             "outputType" : "Exe",
-            "defineConstants" : ["ATOMIC_DESKTOP"],
+            "defineConstants" : ["ATOMIC_DESKTOP", "ATOMIC_PROFILING"],
             "rootNamespace" : "",
             "assemblyName" : "$ATOMIC_PROJECT_NAME$",
             "assemblyOutputPath" : "$ATOMIC_PROJECT_ROOT$\\AtomicNET\\$ATOMIC_CONFIG$\\Bin\\Desktop",
@@ -60,7 +60,7 @@
             "name": "$ATOMIC_PROJECT_NAME$.Android",
             "platforms" : ["android"],
             "outputType" : "Library",
-            "defineConstants" : ["ATOMIC_ANDROID"],
+            "defineConstants" : ["ATOMIC_ANDROID", "ATOMIC_PROFILING"],
             "rootNamespace" : "",
             "assemblyName" : "$ATOMIC_PROJECT_NAME$",
             "projectTypeGuids" : ["EFBA0AD7-5A72-4C68-AF49-83D382785DCF", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"],
@@ -89,7 +89,7 @@
             "name": "$ATOMIC_PROJECT_NAME$.iOS",
             "platforms" : ["ios"],
             "outputType" : "Exe",
-            "defineConstants" : ["ATOMIC_IOS"],
+            "defineConstants" : ["ATOMIC_IOS", "ATOMIC_PROFILING"],
             "rootNamespace" : "",
             "assemblyName" : "$ATOMIC_PROJECT_NAME$",
             "projectGuid" : "071BD84E-7518-11E6-C78E-005056C00008",

+ 1 - 1
Script/Packages/Atomic/Core.json

@@ -12,7 +12,7 @@
 		"<Atomic/Graphics/Renderer.h>",
 		"<Atomic/Metrics/Metrics.h>"
 	],
-	"classes" : ["Context", "Object", "AtomicBuildInfo", "Time"],
+	"classes" : ["Context", "Object", "AtomicBuildInfo", "Time", "Profiler"],
 	"classes_rename" : {
 		"Object" : "AObject"
 	},

+ 1 - 1
Source/Atomic/CMakeLists.txt

@@ -177,9 +177,9 @@ if (NOT WEB)
     target_link_libraries (Atomic libcurl Civetweb kNet)
 endif()
 
-option (ATOMIC_PROFILING "Enable profiler" ON)
 if (ATOMIC_PROFILING)
     target_compile_definitions (Atomic PUBLIC -DATOMIC_PROFILING=1)
+    target_link_libraries (Atomic easy_profiler)
 endif ()
 
 option (ATOMIC_LOGGING "Enable logging" ON)

+ 3 - 19
Source/Atomic/Core/Context.cpp

@@ -23,7 +23,9 @@
 #include "../Precompiled.h"
 
 #include "../Core/Context.h"
-#include "../Core/EventProfiler.h"
+// ATOMIC BEGIN
+#include "../Core/Profiler.h"
+// ATOMIC END
 #include "../IO/Log.h"
 
 #ifndef MINI_URHO
@@ -446,30 +448,12 @@ void Context::RemoveEventReceiver(Object* receiver, Object* sender, StringHash e
 
 void Context::BeginSendEvent(Object* sender, StringHash eventType)
 {
-#ifdef ATOMIC_PROFILING
-    if (EventProfiler::IsActive())
-    {
-        EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
-        if (eventProfiler)
-            eventProfiler->BeginBlock(eventType);
-    }
-#endif
-
     eventSenders_.Push(sender);
 }
 
 void Context::EndSendEvent()
 {
     eventSenders_.Pop();
-
-#ifdef ATOMIC_PROFILING
-    if (EventProfiler::IsActive())
-    {
-        EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
-        if (eventProfiler)
-            eventProfiler->EndBlock();
-    }
-#endif
 }
 
 }

+ 31 - 2
Source/Atomic/Core/Object.cpp

@@ -25,6 +25,9 @@
 #include "../Core/Context.h"
 #include "../Core/Thread.h"
 #include "../IO/Log.h"
+// ATOMIC BEGIN
+#include "../Core/Profiler.h"
+// ATOMIC END
 
 #include "../DebugNew.h"
 
@@ -302,8 +305,34 @@ void Object::SendEvent(StringHash eventType)
 
     SendEvent(eventType, noEventData);
 }
-
+// ATOMIC BEGIN
 void Object::SendEvent(StringHash eventType, VariantMap& eventData)
+{
+#if ATOMIC_PROFILING
+    bool eventProfilingEnabled = false;
+    if (Profiler* profiler = GetSubsystem<Profiler>())
+        eventProfilingEnabled = profiler->GetEventProfilingEnabled();
+
+    if (eventProfilingEnabled)
+        SendEventProfiled(eventType, eventData);
+    else
+#endif
+        SendEventNonProfiled(eventType, eventData);
+}
+
+void Object::SendEventProfiled(StringHash eventType, VariantMap& eventData)
+{
+#if ATOMIC_PROFILING
+    String eventName;
+    if (!StringHash::GetSignificantString(eventType, eventName))
+        eventName = eventType.ToString();
+    ATOMIC_PROFILE_SCOPED(eventName.CString(), PROFILER_COLOR_EVENTS);
+#endif
+    SendEventNonProfiled(eventType, eventData);
+}
+
+void Object::SendEventNonProfiled(StringHash eventType, VariantMap& eventData)
+// ATOMIC END
 {
     if (!Thread::IsMainThread())
     {
@@ -402,7 +431,7 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
     context->EndSendEvent();
 
 // ATOMIC BEGIN
-    context->GlobalEndSendEvent(this,eventType, eventData);
+    context->GlobalEndSendEvent(this, eventType, eventData);
 // ATOMIC END
 
 }

+ 3 - 0
Source/Atomic/Core/Object.h

@@ -222,6 +222,9 @@ protected:
     /// Execution context.
     Context* context_;
 
+    void SendEventProfiled(StringHash eventType, VariantMap& eventData);
+    void SendEventNonProfiled(StringHash eventType, VariantMap& eventData);
+
 private:
     /// Find the first event handler with no specific sender.
     EventHandler* FindEventHandler(StringHash eventType, EventHandler** previous = 0) const;

+ 160 - 0
Source/Atomic/Core/Profiler.cpp

@@ -0,0 +1,160 @@
+//
+// Copyright (c) 2017 the Atomic project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../Precompiled.h"
+#include "../Core/Profiler.h"
+#include "../Core/StringUtils.h"
+
+namespace Atomic
+{
+
+Profiler::Profiler(Context* context)
+    : Object(context)
+{
+    SetEnabled(true);
+#if !ATOMIC_PROFILING
+    enableEventProfiling_ = false;
+#endif
+}
+
+Profiler::~Profiler()
+{
+}
+
+void Profiler::SetEnabled(bool enabled)
+{
+#if ATOMIC_PROFILING
+    ::profiler::setEnabled(enabled);
+#endif
+}
+
+bool Profiler::GetEnabled() const
+{
+#if ATOMIC_PROFILING
+    return ::profiler::isEnabled();
+#else
+    return false;
+#endif
+}
+
+void Profiler::StartListen(unsigned short port)
+{
+#if ATOMIC_PROFILING
+    ::profiler::startListen(port);
+#endif
+}
+
+void Profiler::StopListen()
+{
+#if ATOMIC_PROFILING
+    ::profiler::stopListen();
+#endif
+}
+
+bool Profiler::GetListening() const
+{
+#if ATOMIC_PROFILING
+    return ::profiler::isListening();
+#else
+    return false;
+#endif
+}
+
+void Profiler::SetEventTracingEnabled(bool enable)
+{
+#if ATOMIC_PROFILING
+    ::profiler::setEventTracingEnabled(enable);
+#endif
+}
+
+bool Profiler::GetEventTracingEnabled()
+{
+#if ATOMIC_PROFILING
+    return ::profiler::isEventTracingEnabled();
+#else
+    return false;
+#endif
+}
+
+void Profiler::SetLowPriorityEventTracing(bool isLowPriority)
+{
+#if ATOMIC_PROFILING
+    ::profiler::setLowPriorityEventTracing(isLowPriority);
+#endif
+}
+
+bool Profiler::GetLowPriorityEventTracing()
+{
+#if ATOMIC_PROFILING
+    return ::profiler::isLowPriorityEventTracing();
+#else
+    return false;
+#endif
+}
+
+void Profiler::SaveProfilerData(const String& filePath)
+{
+#if ATOMIC_PROFILING
+    ::profiler::dumpBlocksToFile(filePath.CString());
+#endif
+}
+
+void Profiler::SetEventProfilingEnabled(bool enabled)
+{
+#if ATOMIC_PROFILING
+    enableEventProfiling_ = enabled;
+#endif
+}
+
+bool Profiler::GetEventProfilingEnabled() const
+{
+    return enableEventProfiling_;
+}
+
+void Profiler::BeginBlock(const char* name, const char* file, int line, unsigned int color, unsigned char status)
+{
+#if ATOMIC_PROFILING
+    // Line used as starting hash value for efficiency.
+    // This is likely to not play well with hot code reload.
+    unsigned hash = StringHash::Calculate(file, (unsigned)line);
+    HashMap<unsigned, ::profiler::BaseBlockDescriptor*>::Iterator it = blockDescriptorCache_.Find(hash);
+    const ::profiler::BaseBlockDescriptor* desc = 0;
+    if (it == blockDescriptorCache_.End())
+    {
+        String uniqueName = ToString("%s:%d", file, line);
+        desc = ::profiler::registerDescription((::profiler::EasyBlockStatus)status, uniqueName.CString(), name, file,
+                                               line, ::profiler::BLOCK_TYPE_BLOCK, color, true);
+    }
+    else
+        desc = it->second_;
+    ::profiler::beginNonScopedBlock(desc, name);
+#endif
+}
+
+void Profiler::EndBlock()
+{
+#if ATOMIC_PROFILING
+    ::profiler::endBlock();
+#endif
+}
+
+}

+ 117 - 0
Source/Atomic/Core/Profiler.h

@@ -0,0 +1,117 @@
+//
+// Copyright (c) 2017 the Atomic project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Container/Str.h"
+#include "../Core/Thread.h"
+#include "../Core/Timer.h"
+
+#if ATOMIC_PROFILING
+#   include <easy/profiler.h>
+#else
+namespace profiler { class BaseBlockDescriptor {}; };
+#endif
+
+namespace Atomic
+{
+
+static const int PROFILER_DEFAULT_PORT = 28077;
+static const uint32_t PROFILER_COLOR_DEFAULT = 0xffffecb3;
+static const uint32_t PROFILER_COLOR_EVENTS = 0xffff9800;
+static const uint32_t PROFILER_COLOR_RESOURCES = 0xff00bcd4;
+
+// Copied from easy_profiler
+enum ProfilerBlockStatus
+{
+    OFF = 0,
+    ON = 1,
+    FORCE_ON = ON | 2,
+    OFF_RECURSIVE = 4,
+    ON_WITHOUT_CHILDREN = ON | OFF_RECURSIVE,
+    FORCE_ON_WITHOUT_CHILDREN = FORCE_ON | OFF_RECURSIVE,
+};
+
+
+/// Hierarchical performance profiler subsystem.
+class ATOMIC_API Profiler : public Object
+{
+    ATOMIC_OBJECT(Profiler, Object);
+
+public:
+    /// Construct.
+    Profiler(Context* context);
+    /// Destruct.
+    virtual ~Profiler();
+
+    /// Enables or disables profiler.
+    void SetEnabled(bool enabled);
+    /// Returns true if profiler is enabled, false otherwise.
+    bool GetEnabled() const;
+    /// Enables or disables event profiling.
+    void SetEventProfilingEnabled(bool enabled);
+    /// Returns true if event profiling is enabled, false otherwise.
+    bool GetEventProfilingEnabled() const;
+    /// Starts listening for incoming profiler tool connections.
+    void StartListen(unsigned short port=PROFILER_DEFAULT_PORT);
+    /// Stops listening for incoming profiler tool connections.
+    void StopListen();
+    /// Returns true if profiler is currently listening for incoming connections.
+    bool GetListening() const;
+    /// Enables or disables event tracing. This is windows-specific, does nothing on other OS.
+    void SetEventTracingEnabled(bool enable);
+    /// Returns true if event tracing is enabled, false otherwise.
+    bool GetEventTracingEnabled();
+    /// Enables or disables low priority event tracing. This is windows-specific, does nothing on other OS.
+    void SetLowPriorityEventTracing(bool isLowPriority);
+    /// Returns true if low priority event tracing is enabled, false otherwise.
+    bool GetLowPriorityEventTracing();
+    /// Save profiler data to a file.
+    void SaveProfilerData(const String& filePath);
+    /// Begin non-scoped profiled block. Block has to be terminated with call to EndBlock(). This is slow and is for
+    /// integration with scripting lnaguages. Use ATOMIC_PROFILE* macros when writing c++ code instead.
+    void BeginBlock(const char* name, const char* file, int line, unsigned int argb=PROFILER_COLOR_DEFAULT,
+                    unsigned char status=ProfilerBlockStatus::ON);
+    /// End block started with BeginBlock().
+    void EndBlock();
+
+private:
+
+    bool enableEventProfiling_ = true;
+    HashMap<unsigned, ::profiler::BaseBlockDescriptor*> blockDescriptorCache_;
+};
+
+#if ATOMIC_PROFILING
+#   define ATOMIC_PROFILE(name, ...) EASY_BLOCK(#name, __VA_ARGS__)
+#   define ATOMIC_PROFILE_SCOPED(name, ...) EASY_BLOCK(name, __VA_ARGS__)
+#   define ATOMIC_PROFILE_NONSCOPED(name, ...) EASY_NONSCOPED_BLOCK(name, __VA_ARGS__)
+#   define ATOMIC_PROFILE_END(...) EASY_END_BLOCK
+#   define ATOMIC_PROFILE_THREAD(name) EASY_THREAD(name)
+#else
+#   define ATOMIC_PROFILE(name, ...)
+#   define ATOMIC_PROFILE_NONSCOPED(name, ...)
+#   define ATOMIC_PROFILE_SCOPED(name, ...)
+#   define ATOMIC_PROFILE_END(...)
+#   define ATOMIC_PROFILE_THREAD(name)
+#endif
+
+}

+ 12 - 24
Source/Atomic/Core/Timer.cpp

@@ -110,36 +110,24 @@ void Time::BeginFrame(float timeStep)
 
     timeStep_ = timeStep;
 
-    Profiler* profiler = GetSubsystem<Profiler>();
-    if (profiler)
-        profiler->BeginFrame();
+// ATOMIC BEGIN
+    ATOMIC_PROFILE(BeginFrame);
+    // Frame begin event
+    using namespace BeginFrame;
 
-    {
-        ATOMIC_PROFILE(BeginFrame);
-
-        // Frame begin event
-        using namespace BeginFrame;
-
-        VariantMap& eventData = GetEventDataMap();
-        eventData[P_FRAMENUMBER] = frameNumber_;
-        eventData[P_TIMESTEP] = timeStep_;
-        SendEvent(E_BEGINFRAME, eventData);
-    }
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_FRAMENUMBER] = frameNumber_;
+    eventData[P_TIMESTEP] = timeStep_;
+    SendEvent(E_BEGINFRAME, eventData);
 }
 
 void Time::EndFrame()
 {
-    {
-        ATOMIC_PROFILE(EndFrame);
-
-        // Frame end event
-        SendEvent(E_ENDFRAME);
-    }
-
-    Profiler* profiler = GetSubsystem<Profiler>();
-    if (profiler)
-        profiler->EndFrame();
+    ATOMIC_PROFILE(EndFrame);
+    // Frame end event
+    SendEvent(E_ENDFRAME);
 }
+// ATOMIC END
 
 void Time::SetTimerPeriod(unsigned mSec)
 {

+ 7 - 0
Source/Atomic/Engine/Application.cpp

@@ -25,6 +25,9 @@
 #include "../Engine/Application.h"
 #include "../IO/IOEvents.h"
 #include "../IO/Log.h"
+// ATOMIC BEGIN
+#include "../Core/Profiler.h"
+// ATOMIC END
 
 #ifdef IOS
 #include "../Graphics/Graphics.h"
@@ -87,6 +90,10 @@ Application::Application(Context* context) :
 
 int Application::Run()
 {
+    // ATOMIC BEGIN
+    // Profiler requires main thread to be named "Main" as fps calculations depend on it.
+    ATOMIC_PROFILE_THREAD("Main");
+    // ATOMIC END
 #if !defined(__GNUC__) || __EXCEPTIONS
     try
     {

+ 15 - 29
Source/Atomic/Engine/Engine.cpp

@@ -25,7 +25,10 @@
 #include "../Audio/Audio.h"
 #include "../Core/Context.h"
 #include "../Core/CoreEvents.h"
-#include "../Core/EventProfiler.h"
+// ATOMIC BEGIN
+#include "../Core/Profiler.h"
+#include "../Engine/EngineDefs.h"
+// ATOMIC END
 #include "../Core/ProcessUtils.h"
 #include "../Core/WorkQueue.h"
 #include "../Engine/Engine.h"
@@ -370,11 +373,14 @@ bool Engine::Initialize(const VariantMap& parameters)
 #endif
 
 #ifdef ATOMIC_PROFILING
-    if (GetParameter(parameters, EP_EVENT_PROFILER, true).GetBool())
+    // ATOMIC BEGIN
+    if (Profiler* profiler = GetSubsystem<Profiler>())
     {
-        context_->RegisterSubsystem(new EventProfiler(context_));
-        EventProfiler::SetActive(true);
+        if (GetParameter(parameters, EP_PROFILER_LISTEN, false).GetBool())
+            profiler->StartListen((unsigned short)GetParameter(parameters, EP_PROFILER_PORT, PROFILER_DEFAULT_PORT).GetInt());
+        profiler->SetEventProfilingEnabled(GetParameter(parameters, EP_EVENT_PROFILER, true).GetBool());
     }
+    // ATOMIC END
 #endif
 
     // ATOMIC BEGIN
@@ -553,8 +559,10 @@ bool Engine::InitializeResourceCache(const VariantMap& parameters, bool removeOl
     return true;
 }
 
+// ATOMIC BEGIN
 void Engine::RunFrame()
 {
+    ATOMIC_PROFILE(RunFrame);
     assert(initialized_);
 
     // If not headless, and the graphics subsystem no longer has a window open, assume we should exit
@@ -570,18 +578,8 @@ void Engine::RunFrame()
     Input* input = GetSubsystem<Input>();
     Audio* audio = GetSubsystem<Audio>();
 
-#ifdef ATOMIC_PROFILING
-    if (EventProfiler::IsActive())
-    {
-        EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
-        if (eventProfiler)
-            eventProfiler->BeginFrame();
-    }
-#endif
-
+    ATOMIC_PROFILE(DoFrame);
     time->BeginFrame(timeStep_);
-
-    // ATOMIC BEGIN
     // check for exit again that comes in thru an event handler
     if ( exiting_ ) // needed to prevent scripts running the 
         return;     // current frame update with null objects
@@ -620,13 +618,13 @@ void Engine::RunFrame()
         fpsFramesSinceUpdate_ = 0;
         fpsTimeSinceUpdate_ = 0;
     }
-    // ATOMIC END
-
     Render();
+    ATOMIC_PROFILE_END();
     ApplyFrameLimit();
 
     time->EndFrame();
 }
+// ATOMIC END
 
 Console* Engine::CreateConsole()
 {
@@ -690,18 +688,6 @@ void Engine::Exit()
 #endif
 }
 
-void Engine::DumpProfiler()
-{
-#ifdef ATOMIC_LOGGING
-    if (!Thread::IsMainThread())
-        return;
-
-    Profiler* profiler = GetSubsystem<Profiler>();
-    if (profiler)
-        ATOMIC_LOGRAW(profiler->PrintData(true, true) + "\n");
-#endif
-}
-
 void Engine::DumpResources(bool dumpFileName)
 {
 #ifdef ATOMIC_LOGGING

+ 0 - 2
Source/Atomic/Engine/Engine.h

@@ -68,8 +68,6 @@ public:
     void SetNextTimeStep(float seconds);
     /// Close the graphics window and set the exit flag. No-op on iOS, as an iOS application can not legally exit.
     void Exit();
-    /// Dump profiling information to the log.
-    void DumpProfiler();
     /// Dump information of all resources to the log.
     void DumpResources(bool dumpFileName = false);
     /// Dump information of all memory allocations to the log. Supported in MSVC debug mode only.

+ 2 - 0
Source/Atomic/Engine/EngineDefs.h

@@ -76,5 +76,7 @@ static const String EP_WORKER_THREADS = "WorkerThreads";
 // ATOMIC BEGIN
 static const String EP_WINDOW_MAXIMIZED = "WindowMaximized";
 static const String EP_AUTO_METRICS = "AutoMetrics";
+static const String EP_PROFILER_LISTEN = "ProfilerListen";
+static const String EP_PROFILER_PORT = "ProfilerPort";
 // ATOMIC END
 }

+ 5 - 0
Source/Atomic/IK/IKConverters.h

@@ -28,6 +28,11 @@
 #include <ik/quat.h>
 #include <ik/vec3.h>
 
+// ATOMIC BEGIN
+#undef ON
+#undef OFF
+// ATOMIC END
+
 namespace Atomic {
 
 /// Converts from an Atomic Vector3 to an IK vec3_t

+ 31 - 16
Source/Atomic/Math/StringHash.cpp

@@ -43,20 +43,24 @@ const StringHash StringHash::ZERO;
 StringHash::StringHash(const char* str) :
     value_(Calculate(str))
 {
+#if ATOMIC_PROFILING
+    RegisterSignificantString(str, *this);
+#endif
 }
 
 StringHash::StringHash(const String& str) :
     value_(Calculate(str.CString()))
 {
+#if ATOMIC_PROFILING
+    RegisterSignificantString(str, *this);
+#endif
 }
-
-unsigned StringHash::Calculate(const char* str)
+// ATOMIC BEGIN
+unsigned StringHash::Calculate(const char* str, unsigned hash)
 {
-    unsigned hash = 0;
-
     if (!str)
         return hash;
-
+// ATOMIC END
     while (*str)
     {
         // Perform the actual hashing as case-insensitive
@@ -78,36 +82,47 @@ String StringHash::ToString() const
 // ATOMIC BEGIN
 
 // Lookup for significant strings, not a member of StringHash so don't need to drag hashmap into header
-static HashMap<StringHash, String> gSignificantLookup;
+static HashMap<StringHash, String>* gSignificantLookup = 0;
 
-StringHash StringHash::RegisterSignificantString(const char* str)
+StringHash StringHash::RegisterSignificantString(const String& str)
 {
-    StringHash hash(str);
+    StringHash hash(str.CString());
+    RegisterSignificantString(str.CString(), hash);
+    return hash;
+}
 
-    if (gSignificantLookup.Contains(hash))
-        return StringHash(hash);
+void StringHash::RegisterSignificantString(const char* str, StringHash hash)
+{
+    if (!gSignificantLookup)
+        gSignificantLookup = new HashMap<StringHash, String>();
 
-    gSignificantLookup[hash] = String(str);
+    if (gSignificantLookup->Contains(hash))
+        return;
 
-    return hash;
+    (*gSignificantLookup)[hash] = str;
+}
 
+StringHash StringHash::RegisterSignificantString(const char* str)
+{
+    StringHash hash(str);
+    RegisterSignificantString(str, hash);
+    return hash;
 }
 
-StringHash StringHash::RegisterSignificantString(const String& str)
+void StringHash::RegisterSignificantString(const String& str, StringHash hash)
 {
-    return RegisterSignificantString(str.CString());
+    RegisterSignificantString(str.CString(), hash);
 }
 
 bool StringHash::GetSignificantString(StringHash hash, String& strOut)
 {
-    if (!gSignificantLookup.TryGetValue(hash, strOut))
+    if (!gSignificantLookup || !gSignificantLookup->TryGetValue(hash, strOut))
     {
         strOut.Clear();
         return false;
     }
 
     return true;
-
 }
 
 // ATOMIC END

+ 8 - 6
Source/Atomic/Math/StringHash.h

@@ -100,19 +100,21 @@ public:
     /// Return hash value for HashSet & HashMap.
     unsigned ToHash() const { return value_; }
 
-    /// Calculate hash value case-insensitively from a C string.
-    static unsigned Calculate(const char* str);
-
     /// Zero hash.
     static const StringHash ZERO;
 
     // ATOMIC BEGIN
 
-    /// Register significant C string, which can be looked up via hash, note that the lookup is case insensitive
-    static StringHash RegisterSignificantString(const char* str);
-
+    /// Calculate hash value case-insensitively from a C string.
+    static unsigned Calculate(const char* str, unsigned hash = 0);
     /// Register significant string, which can be looked up via hash, note that the lookup is case insensitive
     static StringHash RegisterSignificantString(const String& str);
+    /// Register significant string, which can be looked up via hash, note that the lookup is case insensitive
+    static void RegisterSignificantString(const String& str, StringHash hash);
+    /// Register significant C string, which can be looked up via hash, note that the lookup is case insensitive
+    static StringHash RegisterSignificantString(const char* str);
+    /// Register significant C string, which can be looked up via hash, note that the lookup is case insensitive
+    static void RegisterSignificantString(const char* str, StringHash hash);
 
     /// Get a significant string from a case insensitive hash value
     static bool GetSignificantString(StringHash hash, String& strOut);

+ 6 - 10
Source/Atomic/Physics/PhysicsWorld.cpp

@@ -806,21 +806,17 @@ void PhysicsWorld::PreStep(float timeStep)
     eventData[P_TIMESTEP] = timeStep;
     SendEvent(E_PHYSICSPRESTEP, eventData);
 
+    // ATOMIC BEGIN
     // Start profiling block for the actual simulation step
-#ifdef ATOMIC_PROFILING
-    Profiler* profiler = GetSubsystem<Profiler>();
-    if (profiler)
-        profiler->BeginBlock("StepSimulation");
-#endif
+    ATOMIC_PROFILE_NONSCOPED("PhysicsStepSimulation");
+    // ATOMIC END
 }
 
 void PhysicsWorld::PostStep(float timeStep)
 {
-#ifdef ATOMIC_PROFILING
-    Profiler* profiler = GetSubsystem<Profiler>();
-    if (profiler)
-        profiler->EndBlock();
-#endif
+    // ATOMIC BEGIN
+    ATOMIC_PROFILE_END();
+    // ATOMIC END
 
     SendCollisionEvents();
 

+ 2 - 10
Source/Atomic/Resource/BackgroundLoader.cpp

@@ -264,20 +264,12 @@ void BackgroundLoader::FinishBackgroundLoading(BackgroundLoadItem& item)
     // If BeginLoad() phase was successful, call EndLoad() and get the final success/failure result
     if (success)
     {
-#ifdef ATOMIC_PROFILING
+#if ATOMIC_PROFILING
         String profileBlockName("Finish" + resource->GetTypeName());
-
-        Profiler* profiler = owner_->GetSubsystem<Profiler>();
-        if (profiler)
-            profiler->BeginBlock(profileBlockName.CString());
+        ATOMIC_PROFILE_SCOPED(profileBlockName.CString(), PROFILER_COLOR_RESOURCES);
 #endif
         ATOMIC_LOGDEBUG("Finishing background loaded resource " + resource->GetName());
         success = resource->EndLoad();
-
-#ifdef ATOMIC_PROFILING
-        if (profiler)
-            profiler->EndBlock();
-#endif
     }
     resource->SetAsyncLoadState(ASYNC_DONE);
 

+ 2 - 10
Source/Atomic/Resource/Resource.cpp

@@ -41,12 +41,9 @@ bool Resource::Load(Deserializer& source)
 {
     // Because BeginLoad() / EndLoad() can be called from worker threads, where profiling would be a no-op,
     // create a type name -based profile block here
-#ifdef ATOMIC_PROFILING
+#if ATOMIC_PROFILING
     String profileBlockName("Load" + GetTypeName());
-
-    Profiler* profiler = GetSubsystem<Profiler>();
-    if (profiler)
-        profiler->BeginBlock(profileBlockName.CString());
+    ATOMIC_PROFILE_SCOPED(profileBlockName.CString(), PROFILER_COLOR_RESOURCES);
 #endif
 
     // If we are loading synchronously in a non-main thread, behave as if async loading (for example use
@@ -57,11 +54,6 @@ bool Resource::Load(Deserializer& source)
         success &= EndLoad();
     SetAsyncLoadState(ASYNC_DONE);
 
-#ifdef ATOMIC_PROFILING
-    if (profiler)
-        profiler->EndBlock();
-#endif
-
     return success;
 }
 

+ 3 - 7
Source/Atomic/UI/SystemUI/DebugHud.cpp

@@ -297,15 +297,11 @@ void DebugHud::RenderUi(StringHash eventType, VariantMap& eventData)
         {
             if (profilerTimer_.GetMSec(false) >= profilerInterval_)
             {
+                profilerTimer_.Reset();
                 if (profilerMode_ == DEBUG_HUD_PROFILE_PERFORMANCE)
                 {
-                    profilerTimer_.Reset();
-                    Profiler* profiler = GetSubsystem<Profiler>();
-                    if (profiler)
-                    {
-                        profilerOutput_ = profiler->PrintData(false, false, profilerMaxDepth_);
-                        profiler->BeginInterval();
-                    }
+                    // Maybe implement some on-screen performance later.
+                    profilerOutput_ = "Performance metrics may be inspected using Profiler tool.";
                 }
                 else
                 {

+ 2 - 0
Source/AtomicEditor/Application/AEEditorApp.cpp

@@ -24,6 +24,7 @@
 #include <Atomic/UI/UI.h>
 #include <AtomicJS/Javascript/Javascript.h>
 #include <Atomic/IPC/IPC.h>
+#include <Atomic/Engine/EngineDefs.h>
 
 // This can be removed once bone hack is fixed
 #include <Atomic/Graphics/AnimatedModel.h>
@@ -124,6 +125,7 @@ namespace AtomicEditor
         engineParameters_["ResourcePaths"] = "CoreData;EditorData";
 
 #endif // ATOMIC_DEV_BUILD
+        engineParameters_[EP_PROFILER_LISTEN] = false;
 
         GetSubsystem<AEEditorPrefs>()->ReadPreferences(engineParameters_);
 

+ 17 - 0
Source/AtomicNET/NETNative/NETCInterop.cpp

@@ -28,6 +28,8 @@
 #include <Atomic/Input/Controls.h>
 
 #include <Atomic/Atomic2D/PhysicsWorld2D.h>
+#include <Atomic/Core/Profiler.h>
+#include <Atomic/IO/Log.h>
 
 #include "NETCore.h"
 
@@ -750,7 +752,22 @@ namespace Atomic
         }
 #endif
 
+        ATOMIC_EXPORT_API void csi_Atomic_Profiler_BeginBlock(Profiler* profiler, const char* name, const char* file, int line, unsigned int argb, unsigned char status)
+        {
+#if ATOMIC_PROFILING
+            if (!profiler)
+                return;
 
+            profiler->BeginBlock(name, file, line, argb, status);
+#else
+            static bool warned = false;
+            if (!warned)
+            {
+                warned = true;
+                ATOMIC_LOGWARNING("Engine is built without profiler support.");
+            }
+#endif
+        }
     }
 }
 

+ 1 - 0
Source/CMakeLists.txt

@@ -8,6 +8,7 @@ option(ATOMIC_EDITOR "Build Editor" ON)
 option(ATOMIC_DATABASE_SQLITE "Enable SQLite database subsystem" OFF)
 option(ATOMIC_DATABASE_ODBC "Enable ODBC database subsystem" OFF)
 option(ATOMIC_IK "Enable inverse kinematics subsystem" OFF)
+option(ATOMIC_PROFILING "Enable profiler" ${ATOMIC_RELEASE_OFF})
 
 add_subdirectory(ThirdParty)
 add_subdirectory(Atomic)

+ 4 - 0
Source/ThirdParty/CMakeLists.txt

@@ -96,3 +96,7 @@ if (ATOMIC_IK)
 endif ()
 
 add_subdirectory(imgui)
+
+if (ATOMIC_PROFILING)
+    add_subdirectory(easy_profiler)
+endif ()