diff --git a/AUTHORS.md b/AUTHORS.md index 540ff98432..2400cb1763 100755 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -57,6 +57,8 @@ - Ken Paulson (https://github.com/MuffinManKen) +- Rokas Kupstys (https://github.com/rokups) + ### Contribution Copyright and Licensing Atomic Game Engine contribution copyrights are held by their authors. Each author retains the copyright to their contribution and agrees to irrevocably license the contribution under the Atomic Game Engine Contribution License `CONTRIBUTION_LICENSE.md`. Please see `CONTRIBUTING.md` for more details. diff --git a/Build/CMake/Modules/AtomicCommon.cmake b/Build/CMake/Modules/AtomicCommon.cmake index 80d6693b62..f469087237 100644 --- a/Build/CMake/Modules/AtomicCommon.cmake +++ b/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. diff --git a/CMakeLists.txt b/CMakeLists.txt index 653d15dfef..50f144f5a8 100644 --- a/CMakeLists.txt +++ b/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) diff --git a/Script/AtomicNET/AtomicNET/Core/Profiler.cs b/Script/AtomicNET/AtomicNET/Core/Profiler.cs new file mode 100644 index 0000000000..b6b5641c65 --- /dev/null +++ b/Script/AtomicNET/AtomicNET/Core/Profiler.cs @@ -0,0 +1,49 @@ +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 + if (profiler != null) + csi_Atomic_Profiler_EndBlock(profiler); +#endif + } + + public static void BeginBlock(string name, uint color = 0xffffecb3, + ProfilerBlockStatus status = ProfilerBlockStatus.ON, + [CallerFilePath] string file = "", + [CallerLineNumber] int line = 0) + { + var profiler = AtomicNET.Context.GetProfiler(); + if (profiler != null) + csi_Atomic_Profiler_BeginBlock(profiler, name, file, line, color, (byte)status); + } + + public static void EndBlock() + { + var profiler = AtomicNET.Context.GetProfiler(); + if (profiler != null) + csi_Atomic_Profiler_EndBlock(profiler); + } + + [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); + + [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern void csi_Atomic_Profiler_EndBlock(IntPtr self); + } +} diff --git a/Script/AtomicNET/AtomicNETProject.json b/Script/AtomicNET/AtomicNETProject.json index 4a6c5174c2..63d0051709 100644 --- a/Script/AtomicNET/AtomicNETProject.json +++ b/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\\", diff --git a/Script/AtomicNET/AtomicProject.json b/Script/AtomicNET/AtomicProject.json index 34e67592e1..1f9e607333 100644 --- a/Script/AtomicNET/AtomicProject.json +++ b/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", diff --git a/Script/Packages/Atomic/Core.json b/Script/Packages/Atomic/Core.json index ab3c5b0212..50f53099aa 100755 --- a/Script/Packages/Atomic/Core.json +++ b/Script/Packages/Atomic/Core.json @@ -12,7 +12,7 @@ "", "" ], - "classes" : ["Context", "Object", "AtomicBuildInfo", "Time"], + "classes" : ["Context", "Object", "AtomicBuildInfo", "Time", "Profiler"], "classes_rename" : { "Object" : "AObject" }, @@ -28,6 +28,9 @@ "CSharp" : { "Object" : { "UnsubscribeFromAllEvents" : [] + }, + "Profiler": { + "EndBlock" : [] } } }, @@ -45,6 +48,9 @@ "subscribeToEvent(sender:AObject, eventType:string, callback:(data: any) => void);", "subscribeToEvent(eventMetaData:Atomic.EventMetaData);", "subscribeToEvent(sender:AObject, eventMetaData:Atomic.EventMetaData);" + ], + "Profiler" : [ + "beginBlock(name:string, filename:string, line:number, argb?:number, status?:Atomic.ProfilerBlockStatus );" ] }, "haxe_decl" : { diff --git a/Source/Atomic/CMakeLists.txt b/Source/Atomic/CMakeLists.txt index 02d1b0e08c..dc8f9b0726 100644 --- a/Source/Atomic/CMakeLists.txt +++ b/Source/Atomic/CMakeLists.txt @@ -32,6 +32,8 @@ elseif (ATOMIC_DATABASE_ODBC) endif () if (WIN32) + option (ATOMIC_D3D11 "Use DirectX 11" OFF) + option (ATOMIC_OPENGL "Use OpenGL" OFF) if (ATOMIC_D3D11) # DirectX 11 file (GLOB GRAPHICS_IMPL_SOURCE Graphics/Direct3D11/*.cpp Graphics/Direct3D11/*.h) elseif (ATOMIC_OPENGL) # OpenGL @@ -125,8 +127,6 @@ elseif (LINUX) target_link_libraries (Atomic pthread GLEW GL dl) target_compile_definitions(Atomic PUBLIC -DATOMIC_PLATFORM_LINUX=1) elseif (WIN32) - option (ATOMIC_D3D11 "Use DirectX 11" ON) - option (ATOMIC_OPENGL "Use OpenGL" OFF) target_compile_definitions (Atomic PUBLIC -DATOMIC_PLATFORM_WINDOWS=1) target_link_libraries (Atomic user32 gdi32 winmm imm32 ole32 oleaut32 version uuid Ws2_32) if (ATOMIC_D3D11) # DirectX 11 @@ -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) @@ -241,10 +241,7 @@ if ($ENV{ATOMIC_BUILD_DIST}) endif () if (MSVC) - target_compile_options(Atomic PUBLIC "$<$:${ATOMIC_MSVC_RUNTIME}d>") - target_compile_options(Atomic PUBLIC "$<$:${ATOMIC_MSVC_RUNTIME}>") - target_compile_options(Atomic PUBLIC "$<$:${ATOMIC_MSVC_RUNTIME}>") - target_compile_options(Atomic PUBLIC "$<$:${ATOMIC_MSVC_RUNTIME}>") + target_compile_options(Atomic PUBLIC $<$:${ATOMIC_MSVC_RUNTIME}d> $<$>:${ATOMIC_MSVC_RUNTIME}>) endif () if (UNIX OR MINGW) diff --git a/Source/Atomic/Core/Context.cpp b/Source/Atomic/Core/Context.cpp index dfcb755e4c..1a39a8030a 100644 --- a/Source/Atomic/Core/Context.cpp +++ b/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(); - if (eventProfiler) - eventProfiler->BeginBlock(eventType); - } -#endif - eventSenders_.Push(sender); } void Context::EndSendEvent() { eventSenders_.Pop(); - -#ifdef ATOMIC_PROFILING - if (EventProfiler::IsActive()) - { - EventProfiler* eventProfiler = GetSubsystem(); - if (eventProfiler) - eventProfiler->EndBlock(); - } -#endif } } diff --git a/Source/Atomic/Core/EventProfiler.cpp b/Source/Atomic/Core/EventProfiler.cpp deleted file mode 100644 index 7b1ac26b5a..0000000000 --- a/Source/Atomic/Core/EventProfiler.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright (c) 2008-2017 the Urho3D 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/EventProfiler.h" - -#include "../DebugNew.h" - -namespace Atomic -{ - -bool EventProfiler::active = false; - -EventProfiler::EventProfiler(Context* context) : - Profiler(context) -{ - // FIXME: Is there a cleaner way? - delete root_; - current_ = root_ = new EventProfilerBlock(0, "RunFrame"); - delete [] root_->name_; - root_->name_ = new char[sizeof("RunFrame")]; - memcpy(root_->name_, "RunFrame", sizeof("RunFrame")); -} - -} diff --git a/Source/Atomic/Core/EventProfiler.h b/Source/Atomic/Core/EventProfiler.h deleted file mode 100644 index 670256c530..0000000000 --- a/Source/Atomic/Core/EventProfiler.h +++ /dev/null @@ -1,91 +0,0 @@ -// -// Copyright (c) 2008-2017 the Urho3D 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 "../Core/Profiler.h" - -namespace Atomic -{ - -/// Event profiling data for one block in the event profiling tree. -class ATOMIC_API EventProfilerBlock : public ProfilerBlock -{ -public: - /// Construct with the specified parent block and event ID. - EventProfilerBlock(EventProfilerBlock* parent, StringHash eventID) : - ProfilerBlock(parent, EventNameRegistrar::GetEventName(eventID).CString()), - eventID_(eventID) - { - } - - /// Return child block with the specified event ID. - EventProfilerBlock* GetChild(StringHash eventID) - { - for (PODVector::Iterator i = children_.Begin(); i != children_.End(); ++i) - { - EventProfilerBlock* eventProfilerBlock = static_cast(*i); - if (eventProfilerBlock->eventID_ == eventID) - return eventProfilerBlock; - } - - EventProfilerBlock* newBlock = new EventProfilerBlock(this, eventID); - children_.Push(newBlock); - - return newBlock; - } - - /// Event ID. - StringHash eventID_; -}; - -/// Hierarchical performance event profiler subsystem. -class ATOMIC_API EventProfiler : public Profiler -{ - ATOMIC_OBJECT(EventProfiler, Profiler); - -public: - /// Construct. - EventProfiler(Context* context); - - /// Activate the event profiler to collect information. This incurs slight performance hit on each SendEvent. By default inactive. - static void SetActive(bool newActive) { active = newActive; } - /// Return true if active. - static bool IsActive() { return active; } - - /// Begin timing a profiling block based on an event ID. - void BeginBlock(StringHash eventID) - { - // Profiler supports only the main thread currently - if (!Thread::IsMainThread()) - return; - - current_ = static_cast(current_)->GetChild(eventID); - current_->Begin(); - } - -private: - /// Profiler active. Default false. - static bool active; -}; - -} diff --git a/Source/Atomic/Core/Object.cpp b/Source/Atomic/Core/Object.cpp index 7eddf80018..a74b8bf89f 100644 --- a/Source/Atomic/Core/Object.cpp +++ b/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()) + 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 } diff --git a/Source/Atomic/Core/Object.h b/Source/Atomic/Core/Object.h index 96ffe3948a..5cd494c56c 100644 --- a/Source/Atomic/Core/Object.h +++ b/Source/Atomic/Core/Object.h @@ -222,6 +222,9 @@ class ATOMIC_API Object : public RefCounted /// 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; diff --git a/Source/Atomic/Core/Profiler.cpp b/Source/Atomic/Core/Profiler.cpp index 711edd917f..e2ee55cb82 100644 --- a/Source/Atomic/Core/Profiler.cpp +++ b/Source/Atomic/Core/Profiler.cpp @@ -1,144 +1,160 @@ -// -// Copyright (c) 2008-2017 the Urho3D 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 - -#include "../DebugNew.h" - -namespace Atomic -{ - -Profiler::Profiler(Context* context) : - Object(context), - current_(0), - root_(0), - intervalFrames_(0) -{ - current_ = root_ = new ProfilerBlock(0, "RunFrame"); -} - -Profiler::~Profiler() -{ - delete root_; - root_ = 0; -} - -void Profiler::BeginFrame() -{ - // End the previous frame if any - if (root_->count_) - EndFrame(); - - root_->Begin(); -} - -void Profiler::EndFrame() -{ - EndBlock(); - ++intervalFrames_; - root_->EndFrame(); - current_ = root_; -} - -void Profiler::BeginInterval() -{ - root_->BeginInterval(); - intervalFrames_ = 0; -} - -const String& Profiler::PrintData(bool showUnused, bool showTotal, unsigned maxDepth) const -{ - static String output; - - if (!showTotal) - output = "Block Cnt Avg Max Frame Total\n\n"; - else - { - output = "Block Last frame Whole execution time\n\n"; - output += " Cnt Avg Max Total Cnt Avg Max Total\n\n"; - } - - if (!maxDepth) - maxDepth = 1; - - PrintData(root_, output, 0, maxDepth, showUnused, showTotal); - - return output; -} - -void Profiler::PrintData(ProfilerBlock* block, String& output, unsigned depth, unsigned maxDepth, bool showUnused, - bool showTotal) const -{ - static const int LINE_MAX_LENGTH = 256; - static const int NAME_MAX_LENGTH = 30; - - char line[LINE_MAX_LENGTH]; - char indentedName[LINE_MAX_LENGTH]; - - if (depth >= maxDepth) - return; - - // Do not print any block that does not collect critical data - if (showUnused || block->intervalCount_ || (showTotal && block->totalCount_)) - { - memset(indentedName, ' ', NAME_MAX_LENGTH); - indentedName[depth++] = 0; - strncat(indentedName, block->name_, NAME_MAX_LENGTH - depth); - indentedName[strlen(indentedName)] = ' '; - indentedName[NAME_MAX_LENGTH] = 0; - - if (!showTotal) - { - float avg = block->intervalTime_ / block->intervalCount_ / 1000.0f; - float max = block->intervalMaxTime_ / 1000.0f; - float frame = block->intervalTime_ / (intervalFrames_ ? intervalFrames_ : 1) / 1000.0f; - float all = block->intervalTime_ / 1000.0f; - - sprintf(line, "%s %5u %8.3f %8.3f %8.3f %9.3f\n", indentedName, Min(block->intervalCount_, 99999U), - avg, max, frame, all); - } - else - { - float avg = (block->frameCount_ ? block->frameTime_ / block->frameCount_ : 0.0f) / 1000.0f; - float max = block->frameMaxTime_ / 1000.0f; - float all = block->frameTime_ / 1000.0f; - - float totalAvg = block->totalTime_ / block->totalCount_ / 1000.0f; - float totalMax = block->totalMaxTime_ / 1000.0f; - float totalAll = block->totalTime_ / 1000.0f; - - sprintf(line, "%s %5u %8.3f %8.3f %9.3f %7u %9.3f %9.3f %11.3f\n", indentedName, Min(block->frameCount_, 99999U), - avg, max, all, Min(block->totalCount_, 99999U), totalAvg, totalMax, totalAll); - } - - output += String(line); - } - - for (PODVector::ConstIterator i = block->children_.Begin(); i != block->children_.End(); ++i) - PrintData(*i, output, depth, maxDepth, showUnused, showTotal); -} - -} +// +// 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 argb, 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::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, argb, true); + } + else + desc = it->second_; + ::profiler::beginNonScopedBlock(desc, name); +#endif +} + +void Profiler::EndBlock() +{ +#if ATOMIC_PROFILING + ::profiler::endBlock(); +#endif +} + +} diff --git a/Source/Atomic/Core/Profiler.h b/Source/Atomic/Core/Profiler.h index 8c61e847b7..f465b71c3c 100644 --- a/Source/Atomic/Core/Profiler.h +++ b/Source/Atomic/Core/Profiler.h @@ -1,260 +1,117 @@ -// -// Copyright (c) 2008-2017 the Urho3D 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" - -namespace Atomic -{ - -/// Profiling data for one block in the profiling tree. -class ATOMIC_API ProfilerBlock -{ -public: - /// Construct with the specified parent block and name. - ProfilerBlock(ProfilerBlock* parent, const char* name) : - name_(0), - time_(0), - maxTime_(0), - count_(0), - parent_(parent), - frameTime_(0), - frameMaxTime_(0), - frameCount_(0), - intervalTime_(0), - intervalMaxTime_(0), - intervalCount_(0), - totalTime_(0), - totalMaxTime_(0), - totalCount_(0) - { - if (name) - { - unsigned nameLength = String::CStringLength(name); - name_ = new char[nameLength + 1]; - memcpy(name_, name, nameLength + 1); - } - } - - /// Destruct. Free the child blocks. - virtual ~ProfilerBlock() - { - for (PODVector::Iterator i = children_.Begin(); i != children_.End(); ++i) - { - delete *i; - *i = 0; - } - - delete [] name_; - } - - /// Begin timing. - void Begin() - { - timer_.Reset(); - ++count_; - } - - /// End timing. - void End() - { - long long time = timer_.GetUSec(false); - if (time > maxTime_) - maxTime_ = time; - time_ += time; - } - - /// End profiling frame and update interval and total values. - void EndFrame() - { - frameTime_ = time_; - frameMaxTime_ = maxTime_; - frameCount_ = count_; - intervalTime_ += time_; - if (maxTime_ > intervalMaxTime_) - intervalMaxTime_ = maxTime_; - intervalCount_ += count_; - totalTime_ += time_; - if (maxTime_ > totalMaxTime_) - totalMaxTime_ = maxTime_; - totalCount_ += count_; - time_ = 0; - maxTime_ = 0; - count_ = 0; - - for (PODVector::Iterator i = children_.Begin(); i != children_.End(); ++i) - (*i)->EndFrame(); - } - - /// Begin new profiling interval. - void BeginInterval() - { - intervalTime_ = 0; - intervalMaxTime_ = 0; - intervalCount_ = 0; - - for (PODVector::Iterator i = children_.Begin(); i != children_.End(); ++i) - (*i)->BeginInterval(); - } - - /// Return child block with the specified name. - ProfilerBlock* GetChild(const char* name) - { - for (PODVector::Iterator i = children_.Begin(); i != children_.End(); ++i) - { - if (!String::Compare((*i)->name_, name, true)) - return *i; - } - - ProfilerBlock* newBlock = new ProfilerBlock(this, name); - children_.Push(newBlock); - - return newBlock; - } - - /// Block name. - char* name_; - /// High-resolution timer for measuring the block duration. - HiresTimer timer_; - /// Time on current frame. - long long time_; - /// Maximum time on current frame. - long long maxTime_; - /// Calls on current frame. - unsigned count_; - /// Parent block. - ProfilerBlock* parent_; - /// Child blocks. - PODVector children_; - /// Time on the previous frame. - long long frameTime_; - /// Maximum time on the previous frame. - long long frameMaxTime_; - /// Calls on the previous frame. - unsigned frameCount_; - /// Time during current profiler interval. - long long intervalTime_; - /// Maximum time during current profiler interval. - long long intervalMaxTime_; - /// Calls during current profiler interval. - unsigned intervalCount_; - /// Total accumulated time. - long long totalTime_; - /// All-time maximum time. - long long totalMaxTime_; - /// Total accumulated calls. - unsigned totalCount_; -}; - -/// Hierarchical performance profiler subsystem. -class ATOMIC_API Profiler : public Object -{ - ATOMIC_OBJECT(Profiler, Object); - -public: - /// Construct. - Profiler(Context* context); - /// Destruct. - virtual ~Profiler(); - - /// Begin timing a profiling block. - void BeginBlock(const char* name) - { - // Profiler supports only the main thread currently - if (!Thread::IsMainThread()) - return; - - current_ = current_->GetChild(name); - current_->Begin(); - } - - /// End timing the current profiling block. - void EndBlock() - { - if (!Thread::IsMainThread()) - return; - - current_->End(); - if (current_->parent_) - current_ = current_->parent_; - } - - /// Begin the profiling frame. Called by HandleBeginFrame(). - void BeginFrame(); - /// End the profiling frame. Called by HandleEndFrame(). - void EndFrame(); - /// Begin a new interval. - void BeginInterval(); - - /// Return profiling data as text output. This method is not thread-safe. - const String& PrintData(bool showUnused = false, bool showTotal = false, unsigned maxDepth = M_MAX_UNSIGNED) const; - /// Return the current profiling block. - const ProfilerBlock* GetCurrentBlock() { return current_; } - /// Return the root profiling block. - const ProfilerBlock* GetRootBlock() { return root_; } - -protected: - /// Return profiling data as text output for a specified profiling block. - void PrintData(ProfilerBlock* block, String& output, unsigned depth, unsigned maxDepth, bool showUnused, bool showTotal) const; - - /// Current profiling block. - ProfilerBlock* current_; - /// Root profiling block. - ProfilerBlock* root_; - /// Frames in the current interval. - unsigned intervalFrames_; -}; - -/// Helper class for automatically beginning and ending a profiling block -class ATOMIC_API AutoProfileBlock -{ -public: - /// Construct. Begin a profiling block with the specified name and optional call count. - AutoProfileBlock(Profiler* profiler, const char* name) : - profiler_(profiler) - { - if (profiler_) - profiler_->BeginBlock(name); - } - - /// Destruct. End the profiling block. - ~AutoProfileBlock() - { - if (profiler_) - profiler_->EndBlock(); - } - -private: - /// Profiler. - Profiler* profiler_; -}; - -#ifdef ATOMIC_PROFILING -#define ATOMIC_PROFILE(name) Atomic::AutoProfileBlock profile_ ## name (GetSubsystem(), #name) -#else -#define ATOMIC_PROFILE(name) -#endif - -} +// +// 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 +#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 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 + +} diff --git a/Source/Atomic/Core/Timer.cpp b/Source/Atomic/Core/Timer.cpp index a9bb1220c9..cc88fc3a21 100644 --- a/Source/Atomic/Core/Timer.cpp +++ b/Source/Atomic/Core/Timer.cpp @@ -110,36 +110,24 @@ void Time::BeginFrame(float timeStep) timeStep_ = timeStep; - Profiler* profiler = GetSubsystem(); - 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(); - if (profiler) - profiler->EndFrame(); + ATOMIC_PROFILE(EndFrame); + // Frame end event + SendEvent(E_ENDFRAME); } +// ATOMIC END void Time::SetTimerPeriod(unsigned mSec) { diff --git a/Source/Atomic/Engine/Application.cpp b/Source/Atomic/Engine/Application.cpp index 0291265b05..a4de5c03fa 100644 --- a/Source/Atomic/Engine/Application.cpp +++ b/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 { diff --git a/Source/Atomic/Engine/Engine.cpp b/Source/Atomic/Engine/Engine.cpp index 55d043000d..abd8e4cda4 100644 --- a/Source/Atomic/Engine/Engine.cpp +++ b/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()) { - 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(); Audio* audio = GetSubsystem