瀏覽代碼

Latest updates

Josh Engebretson 9 年之前
父節點
當前提交
e4e0e8a236

+ 33 - 2
AUTHORS.md

@@ -57,9 +57,12 @@ Urho3D development, contributions and bugfixes by:
 - Lasse Öörni ([email protected], AgentC at GameDev.net)
 - Wei Tjong Yao
 - Aster Jian
+- Vivienne Anthony
 - Colin Barrett
 - Erik Beran
+- Loic Blot
 - Danny Boisvert
+- Sergey Bosko
 - Carlo Carollo
 - Pete Chown
 - Christian Clavet
@@ -68,34 +71,52 @@ Urho3D development, contributions and bugfixes by:
 - Chris Friesen
 - Alex Fuller
 - Mika Heinonen
+- Jukka Jylänki
 - Graham King
 - Jason Kinzer
+- Eugene Kozlov
 - Gunnar Kriik
+- Aliaksandr Kryvashein
+- Artem Kulyk
 - Ali Kämäräinen
 - Pete Leigh
+- Frode 'Modanung' Lindeijer
 - Thorbjørn Lindeijer
+- Nathanial Lydick
+- Xavier Maupeu
 - Jonne Nauha
 - Paul Noome
 - David Palacios
 - Alex Parlett
 - Jordan Patterson
+- Anton Petrov
 - Vladimir Pobedinsky
+- Franck Poulain
+- Pranjal Raihan
 - Nick Royer
-- Jonathan Sandusky
 - Miika Santala
+- Anatoly Sennov
+- Bengt Soderstrom
+- Hualin Song
 - James Thomas
 - Joshua Tippetts
+- Yusuf Umar
 - Daniel Wiberg
 - Steven Zhang
+- Rokas Kupstys
 - AGreatFish
+- BlueMagnificent
 - Enhex
 - Firegorilla
+- Lumak
 - Magic.Lixin
 - Mike3D
-- Modanung
 - MonkeyFirst
+- Newb I the Newbd
 - OvermindDL1
 - Skrylar
+- TheComet93
+- Y-way
 - 1vanK
 - andmar1x
 - amadeus_osa
@@ -103,19 +124,29 @@ Urho3D development, contributions and bugfixes by:
 - att
 - celeron55
 - cosmy1
+- damu
+- dragonCASTjosh
 - feltech
+- fredakilla
 - hdunderscore
+- lvshiling
 - marynate
 - mightyCelu
+- neat3d
 - nemerle
 - ninjastone
+- proller
 - raould
 - rasteron
 - reattiva
 - rifai
+- rikorin
 - skaiware
+- ssinai1
+- svifylabs
 - szamq
 - thebluefish
+- tommy3
 - yushli
 
 Urho3D is greatly inspired by OGRE (http://www.ogre3d.org) and Horde3D

+ 1 - 13
Source/Atomic/Container/Str.h

@@ -538,19 +538,7 @@ public:
 #endif
 
     /// Return length of a C string.
-    static unsigned CStringLength(const char* str)
-    {
-        if (!str)
-            return 0;
-#ifdef _MSC_VER
-        return (unsigned)strlen(str);
-#else
-        const char* ptr = str;
-        while (*ptr)
-            ++ptr;
-        return (unsigned)(ptr - str);
-#endif
-    }
+    static unsigned CStringLength(const char* str) { return str ? (unsigned)strlen(str) : 0; }
 
     /// Append to string using formatting.
     String& AppendWithFormat(const char* formatString, ...);

+ 29 - 1
Source/Atomic/Core/Context.cpp

@@ -23,7 +23,7 @@
 #include "../Precompiled.h"
 
 #include "../Core/Context.h"
-#include "../Core/Thread.h"
+#include "../Core/EventProfiler.h"
 #include "../IO/Log.h"
 
 #include "../DebugNew.h"
@@ -284,4 +284,32 @@ void Context::RemoveEventReceiver(Object* receiver, Object* sender, StringHash e
         group->Erase(receiver);
 }
 
+void Context::BeginSendEvent(Object* sender, StringHash eventType)
+{
+#ifdef URHO3D_PROFILING
+    if (EventProfiler::IsActive())
+    {
+        EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
+        if (eventProfiler)
+            eventProfiler->BeginBlock(eventType);
+    }
+#endif
+
+    eventSenders_.Push(sender);
+}
+
+void Context::EndSendEvent()
+{
+    eventSenders_.Pop();
+
+#ifdef URHO3D_PROFILING
+    if (EventProfiler::IsActive())
+    {
+        EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
+        if (eventProfiler)
+            eventProfiler->EndBlock();
+    }
+#endif
+}
+
 }

+ 6 - 8
Source/Atomic/Core/Context.h

@@ -22,9 +22,9 @@
 
 #pragma once
 
+#include "../Container/HashSet.h"
 #include "../Core/Attribute.h"
 #include "../Core/Object.h"
-#include "../Container/HashSet.h"
 
 namespace Atomic
 {
@@ -101,7 +101,7 @@ public:
 
     /// Return subsystem by type.
     Object* GetSubsystem(StringHash type) const;
-    
+
     /// Return global variable based on key
     const Variant& GetGlobalVar(StringHash key) const ;
 
@@ -197,16 +197,14 @@ private:
     void RemoveEventReceiver(Object* receiver, Object* sender, StringHash eventType);
     /// Remove event receiver from non-specific events.
     void RemoveEventReceiver(Object* receiver, StringHash eventType);
+    /// Begin event send.
+    void BeginSendEvent(Object* sender, StringHash eventType);
+    /// End event send. Clean up event receivers removed in the meanwhile.
+    void EndSendEvent();
 
     /// Set current event handler. Called by Object.
     void SetEventHandler(EventHandler* handler) { eventHandler_ = handler; }
 
-    /// Begin event send.
-    void BeginSendEvent(Object* sender) { eventSenders_.Push(sender); }
-
-    /// End event send. Clean up event receivers removed in the meanwhile.
-    void EndSendEvent() { eventSenders_.Pop(); }
-
     /// Object factories.
     HashMap<StringHash, SharedPtr<ObjectFactory> > factories_;
     /// Subsystems.

+ 7 - 139
Source/Atomic/Core/EventProfiler.cpp

@@ -22,156 +22,24 @@
 
 #include "../Precompiled.h"
 
-#include "../Core/CoreEvents.h"
 #include "../Core/EventProfiler.h"
 
-#include <cstdio>
-
 #include "../DebugNew.h"
 
 namespace Atomic
 {
 
-static const int LINE_MAX_LENGTH = 256;
-static const int NAME_MAX_LENGTH = 30;
-
 bool EventProfiler::active = false;
 
-EventProfiler::EventProfiler(Context* context) : 
-    Object(context),
-    current_(0),
-    root_(0),
-    intervalFrames_(0),
-    totalFrames_(0)
-{
-    root_ = new EventProfilerBlock(0, "Root");
-    current_ = root_;
-    current_->name_ = "Root";
-}
-
-EventProfiler::~EventProfiler()
-{
-    delete root_;
-    root_ = 0;
-}
-
-void EventProfiler::SetActive(bool newActive)
-{
-    active = newActive;
-}
-
-void EventProfiler::BeginFrame()
-{
-    // End the previous frame if any
-    EndFrame();
-
-    BeginBlock("RunFrame");
-    current_->name_ = "RunFrame";
-}
-
-void EventProfiler::EndFrame()
-{
-    if (current_ != root_)
-    {
-        EndBlock();
-        ++intervalFrames_;
-        ++totalFrames_;
-        if (!totalFrames_)
-            ++totalFrames_;
-        root_->EndFrame();
-        current_ = root_;
-    }
-}
-
-void EventProfiler::BeginInterval()
-{
-
-    root_->BeginInterval();
-    intervalFrames_ = 0;
-}
-
-void EventProfiler::Clear()
+EventProfiler::EventProfiler(Context* context) :
+    Profiler(context)
 {
+    // FIXME: Is there a cleaner way?
     delete root_;
-    root_ = new EventProfilerBlock(0, "Root");
-    current_ = root_;
-    current_->name_ = "Root";
-}
-
-Atomic::String EventProfiler::PrintData(bool showUnused /*= false*/, bool showTotal /*= false*/, unsigned maxDepth /*= M_MAX_UNSIGNED*/) const
-{
-    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 EventProfiler::PrintData(EventProfilerBlock* block, String& output, unsigned depth, unsigned maxDepth, bool showUnused, bool showTotal) const
-{
-    char line[LINE_MAX_LENGTH];
-    char indentedName[LINE_MAX_LENGTH];
-
-    unsigned intervalFrames = Max(intervalFrames_, 1U);
-
-    if (depth >= maxDepth)
-        return;
-
-    // Do not print the root block as it does not collect any actual data
-    if (block != root_)
-    {
-        if (showUnused || block->intervalCount_ || (showTotal && block->totalCount_))
-        {
-            memset(indentedName, ' ', NAME_MAX_LENGTH);
-            indentedName[depth] = 0;
-
-            strcat(indentedName, block->name_.Empty() ? block->eventID_.ToString().CString() : block->name_.CString());
-            indentedName[strlen(indentedName)] = ' ';
-            indentedName[NAME_MAX_LENGTH] = 0;
-
-            if (!showTotal)
-            {
-                float avg = (block->intervalCount_ ? block->intervalTime_ / block->intervalCount_ : 0.0f) / 1000.0f;
-                float max = block->intervalMaxTime_ / 1000.0f;
-                float frame = block->intervalTime_ / intervalFrames / 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->totalCount_ ? block->totalTime_ / block->totalCount_ : 0.0f) / 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);
-        }
-
-        ++depth;
-    }
-
-    for (HashMap<StringHash, EventProfilerBlock*>::ConstIterator i = block->children_.Begin(); i != block->children_.End(); ++i)
-        PrintData(i->second_, output, depth, maxDepth, showUnused, showTotal);
+    current_ = root_ = new EventProfilerBlock(0, "RunFrame");
+    delete [] root_->name_;
+    root_->name_ = new char[sizeof("RunFrame")];
+    memcpy(root_->name_, "RunFrame", sizeof("RunFrame"));
 }
 
 }

+ 19 - 163
Source/Atomic/Core/EventProfiler.h

@@ -22,212 +22,68 @@
 
 #pragma once
 
-#include "../Container/Str.h"
-#include "../Core/Thread.h"
-#include "../Core/Timer.h"
-
 #include "../Core/Profiler.h"
 
-
 namespace Atomic
 {
+
 /// Event profiling data for one block in the event profiling tree.
-class ATOMIC_API EventProfilerBlock
+class ATOMIC_API EventProfilerBlock : public ProfilerBlock
 {
 public:
-    /// Construct with the specified parent block and event id.
+    /// Construct with the specified parent block and event ID.
     EventProfilerBlock(EventProfilerBlock* parent, StringHash eventID) :
-        eventID_(eventID),
-        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)
+        ProfilerBlock(parent, EventNameRegistrar::GetEventName(eventID).CString()),
+        eventID_(eventID)
     {
-        name_ = EventNameRegistrar::GetEventName(eventID);
     }
 
-    /// Destruct. Free the child blocks.
-    ~EventProfilerBlock()
+    /// Return child block with the specified event ID.
+    EventProfilerBlock* GetChild(StringHash eventID)
     {
-        for (HashMap<StringHash, EventProfilerBlock* >::Iterator i = children_.Begin(); i != children_.End(); ++i)
+        for (PODVector<ProfilerBlock*>::Iterator i = children_.Begin(); i != children_.End(); ++i)
         {
-            delete i->second_;
-            i->second_ = 0;
+            EventProfilerBlock* eventProfilerBlock = static_cast<EventProfilerBlock*>(*i);
+            if (eventProfilerBlock->eventID_ == eventID)
+                return eventProfilerBlock;
         }
-    }
-
-    /// 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 (HashMap<StringHash, EventProfilerBlock* >::Iterator i = children_.Begin(); i != children_.End(); ++i)
-            i->second_->EndFrame();
-    }
-
-    /// Begin new profiling interval.
-    void BeginInterval()
-    {
-        intervalTime_ = 0;
-        intervalMaxTime_ = 0;
-        intervalCount_ = 0;
-
-        for (HashMap<StringHash, EventProfilerBlock*>::Iterator i = children_.Begin(); i != children_.End(); ++i)
-            i->second_->BeginInterval();
-    }
-
-    /// Return child block with the specified name.
-    EventProfilerBlock* GetChild(StringHash eventID)
-    {
-        HashMap<StringHash, EventProfilerBlock*>::Iterator it = children_.Find(eventID);
-        if (it != children_.End())
-            return it->second_;
 
         EventProfilerBlock* newBlock = new EventProfilerBlock(this, eventID);
-        children_[eventID] = newBlock;
+        children_.Push(newBlock);
 
         return newBlock;
     }
+
     /// Event ID.
     StringHash eventID_;
-    /// Block name.
-    String 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.
-    EventProfilerBlock* parent_;
-    /// Child blocks.
-    HashMap<StringHash,EventProfilerBlock*> 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 event profiler subsystem.
-class ATOMIC_API EventProfiler : public Object
+class ATOMIC_API EventProfiler : public Profiler
 {
-    ATOMIC_OBJECT(EventProfiler, Object);
+    ATOMIC_OBJECT(EventProfiler, Profiler);
 
 public:
     /// Construct.
     EventProfiler(Context* context);
-    /// Destruct.
-    virtual ~EventProfiler();
 
     /// Activate the event profiler to collect information. This incurs slight performance hit on each SendEvent. By default inactive.
-    static void SetActive(bool active);
+    static void SetActive(bool newActive) { active = newActive; }
     /// Return true if active.
     static bool IsActive() { return active; }
 
-    /// Begin timing a profiling block.
+    /// 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_ = current_->GetChild(eventID);
-        current_->Begin();
-    }
-
-    /// End timing the current profiling block.
-    void EndBlock()
-    {
         if (!Thread::IsMainThread())
             return;
 
-        if (current_ != root_)
-        {
-            current_->End();
-            current_ = current_->parent_;
-        }
+        current_ = static_cast<EventProfilerBlock*>(current_)->GetChild(eventID);
+        current_->Begin();
     }
 
-    /// Begin the profiling frame. Called by Engine::RunFrame().
-    void BeginFrame();
-    /// End the profiling frame. Called by Engine::RunFrame().
-    void EndFrame();
-    /// Begin a new interval.
-    void BeginInterval();
-    /// Delete all blocks and recreate root block.
-    void Clear();
-    /// Return profiling data as text output.
-    String PrintData(bool showUnused = false, bool showTotal = false, unsigned maxDepth = M_MAX_UNSIGNED) const;
-    /// Return the current profiling block.
-    const EventProfilerBlock* GetCurrentBlock() { return current_; }
-    /// Return the root profiling block.
-    const EventProfilerBlock* GetRootBlock() { return root_; }
 private:
-    /// Return profiling data as text output for a specified profiling block.
-    void PrintData(EventProfilerBlock* block, String& output, unsigned depth, unsigned maxDepth, bool showUnused, bool showTotal) const;
-    /// Current profiling block.
-    EventProfilerBlock* current_;
-    /// Root profiling block.
-    EventProfilerBlock* root_;
-    /// Frames in the current interval.
-    unsigned intervalFrames_;
-    /// Total frames.
-    unsigned totalFrames_;
     /// Profiler active. Default false.
     static bool active;
 };

+ 6 - 34
Source/Atomic/Core/Object.cpp

@@ -25,8 +25,6 @@
 #include "../Core/Context.h"
 #include "../Core/Thread.h"
 #include "../IO/Log.h"
-#include "../Core/EventProfiler.h"
-#include "../Container/HashMap.h"
 
 #include "../DebugNew.h"
 
@@ -305,16 +303,6 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
         return;
     }
 
-#ifdef ATOMIC_PROFILING
-    EventProfiler* eventProfiler = 0;
-    if (EventProfiler::IsActive())
-    {
-        eventProfiler = GetSubsystem<EventProfiler>();
-        if (eventProfiler)
-            eventProfiler->BeginBlock(eventType);
-    }
-#endif
-
     // Make a weak pointer to self to check for destruction during event handling
     WeakPtr<Object> self(this);
     Context* context = context_;
@@ -324,7 +312,7 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
     context->GlobalBeginSendEvent(this, eventType, eventData);
 // ATOMIC END
 
-    context->BeginSendEvent(this);
+    context->BeginSendEvent(this, eventType);
 
     // Check first the specific event receivers
     const HashSet<Object*>* group = context->GetEventReceivers(this, eventType);
@@ -345,10 +333,6 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
             if (self.Expired())
             {
                 context->EndSendEvent();
-#ifdef ATOMIC_PROFILING
-                if (eventProfiler)
-                    eventProfiler->EndBlock();
-#endif
                 return;
             }
 
@@ -381,10 +365,6 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
                 if (self.Expired())
                 {
                     context->EndSendEvent();
-#ifdef ATOMIC_PROFILING
-                    if (eventProfiler)
-                         eventProfiler->EndBlock();
-#endif
                     return;
                 }
 
@@ -411,10 +391,6 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
                     if (self.Expired())
                     {
                         context->EndSendEvent();
-#ifdef ATOMIC_PROFILING
-                        if (eventProfiler)
-                            eventProfiler->EndBlock();
-#endif
                         return;
                     }
 
@@ -431,10 +407,6 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
     context->GlobalEndSendEvent(this,eventType, eventData);
 // ATOMIC END
 
-#ifdef ATOMIC_PROFILING
-    if (eventProfiler)
-        eventProfiler->EndBlock();
-#endif
 }
 
 VariantMap& Object::GetEventDataMap() const
@@ -447,9 +419,9 @@ const Variant& Object::GetGlobalVar(StringHash key) const
     return context_->GetGlobalVar(key);
 }
 
-const VariantMap& Object::GetGlobalVars() const 
-{ 
-    return context_->GetGlobalVars(); 
+const VariantMap& Object::GetGlobalVars() const
+{
+    return context_->GetGlobalVars();
 }
 
 void Object::SetGlobalVar(StringHash key, const Variant& value)
@@ -574,13 +546,13 @@ void Object::RemoveEventSender(Object* sender)
 
 
 Atomic::StringHash EventNameRegistrar::RegisterEventName(const char* eventName)
-{  
+{
     StringHash id(eventName);
     GetEventNameMap()[id] = eventName;
     return id;
 }
 
-const String& EventNameRegistrar::GetEventName(StringHash eventID) 
+const String& EventNameRegistrar::GetEventName(StringHash eventID)
 {
     HashMap<StringHash, String>::ConstIterator it = GetEventNameMap().Find(eventID);
     return  it != GetEventNameMap().End() ? it->second_ : String::EMPTY ;

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

@@ -390,6 +390,7 @@ private:
 /// Register event names.
 struct ATOMIC_API EventNameRegistrar
 {
+    /// Register an event name for hash reverse mapping.
     static StringHash RegisterEventName(const char* eventName);
     /// Return Event name or empty string if not found.
     static const String& GetEventName(StringHash eventID);

+ 46 - 61
Source/Atomic/Core/Profiler.cpp

@@ -22,7 +22,6 @@
 
 #include "../Precompiled.h"
 
-#include "../Core/CoreEvents.h"
 #include "../Core/Profiler.h"
 
 #include <cstdio>
@@ -32,18 +31,13 @@
 namespace Atomic
 {
 
-static const int LINE_MAX_LENGTH = 256;
-static const int NAME_MAX_LENGTH = 30;
-
 Profiler::Profiler(Context* context) :
     Object(context),
     current_(0),
     root_(0),
-    intervalFrames_(0),
-    totalFrames_(0)
+    intervalFrames_(0)
 {
-    root_ = new ProfilerBlock(0, "Root");
-    current_ = root_;
+    current_ = root_ = new ProfilerBlock(0, "RunFrame");
 }
 
 Profiler::~Profiler()
@@ -55,23 +49,18 @@ Profiler::~Profiler()
 void Profiler::BeginFrame()
 {
     // End the previous frame if any
-    EndFrame();
+    if (root_->count_)
+        EndFrame();
 
-    BeginBlock("RunFrame");
+    root_->Begin();
 }
 
 void Profiler::EndFrame()
 {
-    if (current_ != root_)
-    {
-        EndBlock();
-        ++intervalFrames_;
-        ++totalFrames_;
-        if (!totalFrames_)
-            ++totalFrames_;
-        root_->EndFrame();
-        current_ = root_;
-    }
+    EndBlock();
+    ++intervalFrames_;
+    root_->EndFrame();
+    current_ = root_;
 }
 
 void Profiler::BeginInterval()
@@ -80,15 +69,15 @@ void Profiler::BeginInterval()
     intervalFrames_ = 0;
 }
 
-String Profiler::PrintData(bool showUnused, bool showTotal, unsigned maxDepth) const
+const String& Profiler::PrintData(bool showUnused, bool showTotal, unsigned maxDepth) const
 {
-    String output;
+    static String output;
 
     if (!showTotal)
-        output += "Block                            Cnt     Avg      Max     Frame     Total\n\n";
+        output  = "Block                            Cnt     Avg      Max     Frame     Total\n\n";
     else
     {
-        output += "Block                                       Last frame                       Whole execution time\n\n";
+        output  = "Block                                       Last frame                       Whole execution time\n\n";
         output += "                                 Cnt     Avg      Max      Total      Cnt      Avg       Max        Total\n\n";
     }
 
@@ -103,53 +92,49 @@ String Profiler::PrintData(bool showUnused, bool showTotal, unsigned maxDepth) c
 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];
 
-    unsigned intervalFrames = Max(intervalFrames_, 1U);
-
     if (depth >= maxDepth)
         return;
 
-    // Do not print the root block as it does not collect any actual data
-    if (block != root_)
+    // Do not print any block that does not collect critical data
+    if (showUnused || block->intervalCount_ || (showTotal && block->totalCount_))
     {
-        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
         {
-            memset(indentedName, ' ', NAME_MAX_LENGTH);
-            indentedName[depth] = 0;
-            strcat(indentedName, block->name_);
-            indentedName[strlen(indentedName)] = ' ';
-            indentedName[NAME_MAX_LENGTH] = 0;
-
-            if (!showTotal)
-            {
-                float avg = (block->intervalCount_ ? block->intervalTime_ / block->intervalCount_ : 0.0f) / 1000.0f;
-                float max = block->intervalMaxTime_ / 1000.0f;
-                float frame = block->intervalTime_ / intervalFrames / 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->totalCount_ ? block->totalTime_ / block->totalCount_ : 0.0f) / 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);
+            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);
         }
 
-        ++depth;
+        output += String(line);
     }
 
     for (PODVector<ProfilerBlock*>::ConstIterator i = block->children_.Begin(); i != block->children_.End(); ++i)

+ 29 - 33
Source/Atomic/Core/Profiler.h

@@ -57,26 +57,26 @@ public:
             memcpy(name_, name, nameLength + 1);
         }
     }
-    
+
     /// Destruct. Free the child blocks.
-    ~ProfilerBlock()
+    virtual ~ProfilerBlock()
     {
         for (PODVector<ProfilerBlock*>::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()
     {
@@ -85,7 +85,7 @@ public:
             maxTime_ = time;
         time_ += time;
     }
-    
+
     /// End profiling frame and update interval and total values.
     void EndFrame()
     {
@@ -103,22 +103,22 @@ public:
         time_ = 0;
         maxTime_ = 0;
         count_ = 0;
-        
+
         for (PODVector<ProfilerBlock*>::Iterator i = children_.Begin(); i != children_.End(); ++i)
             (*i)->EndFrame();
     }
-    
+
     /// Begin new profiling interval.
     void BeginInterval()
     {
         intervalTime_ = 0;
         intervalMaxTime_ = 0;
         intervalCount_ = 0;
-        
+
         for (PODVector<ProfilerBlock*>::Iterator i = children_.Begin(); i != children_.End(); ++i)
             (*i)->BeginInterval();
     }
-    
+
     /// Return child block with the specified name.
     ProfilerBlock* GetChild(const char* name)
     {
@@ -127,13 +127,13 @@ public:
             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.
@@ -172,63 +172,59 @@ public:
 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;
-        
-        if (current_ != root_)
-        {
-            current_->End();
+
+        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.
-    String PrintData(bool showUnused = false, bool showTotal = false, unsigned maxDepth = M_MAX_UNSIGNED) const;
+
+    /// 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_; }
-    
-private:
+
+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_;
-    /// Total frames.
-    unsigned totalFrames_;
 };
 
 /// Helper class for automatically beginning and ending a profiling block
@@ -242,14 +238,14 @@ public:
         if (profiler_)
             profiler_->BeginBlock(name);
     }
-    
+
     /// Destruct. End the profiling block.
     ~AutoProfileBlock()
     {
         if (profiler_)
             profiler_->EndBlock();
     }
-    
+
 private:
     /// Profiler.
     Profiler* profiler_;

+ 15 - 9
Source/Atomic/Core/StringUtils.cpp

@@ -103,31 +103,37 @@ bool ToBool(const char* source)
     return false;
 }
 
-int ToInt(const String& source)
+int ToInt(const String& source, int base)
 {
-    return ToInt(source.CString());
+    return ToInt(source.CString(), base);
 }
 
-int ToInt(const char* source)
+int ToInt(const char* source, int base)
 {
     if (!source)
         return 0;
 
-    // Explicitly ask for base 10 to prevent source starts with '0' or '0x' from being converted to base 8 or base 16, respectively
-    return (int)strtol(source, 0, 10);
+    // Shield against runtime library assert by converting illegal base values to 0 (autodetect)
+    if (base < 2 || base > 36)
+        base = 0;
+
+    return (int)strtol(source, 0, base);
 }
 
-unsigned ToUInt(const String& source)
+unsigned ToUInt(const String& source, int base)
 {
-    return ToUInt(source.CString());
+    return ToUInt(source.CString(), base);
 }
 
-unsigned ToUInt(const char* source)
+unsigned ToUInt(const char* source, int base)
 {
     if (!source)
         return 0;
 
-    return (unsigned)strtoul(source, 0, 10);
+    if (base < 2 || base > 36)
+        base = 0;
+
+    return (unsigned)strtoul(source, 0, base);
 }
 
 float ToFloat(const String& source)

+ 8 - 8
Source/Atomic/Core/StringUtils.h

@@ -39,14 +39,14 @@ ATOMIC_API float ToFloat(const char* source);
 ATOMIC_API double ToDouble(const String& source);
 /// Parse a double from a C string.
 ATOMIC_API double ToDouble(const char* source);
-/// Parse an integer from a string.
-ATOMIC_API int ToInt(const String& source);
-/// Parse an integer from a C string.
-ATOMIC_API int ToInt(const char* source);
-/// Parse an unsigned integer from a string.
-ATOMIC_API unsigned ToUInt(const String& source);
-/// Parse an unsigned integer from a C string.
-ATOMIC_API unsigned ToUInt(const char* source);
+/// Parse an integer from a string. Assumed to be decimal by default (base 10). Use base 0 to autodetect from string.
+ATOMIC_API int ToInt(const String& source, int base = 10);
+/// Parse an integer from a C string. Assumed to be decimal by default (base 10). Use base 0 to autodetect from string.
+ATOMIC_API int ToInt(const char* source, int base = 10);
+/// Parse an unsigned integer from a string. Assumed to be decimal by default (base 10). Use base 0 to autodetect from string.
+ATOMIC_API unsigned ToUInt(const String& source, int base = 10);
+/// Parse an unsigned integer from a C string. Assumed to be decimal by default (base 10). Use base 0 to autodetect from string.
+ATOMIC_API unsigned ToUInt(const char* source, int base = 10);
 /// Parse a Color from a string.
 ATOMIC_API Color ToColor(const String& source);
 /// Parse a Color from a C string.

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

@@ -25,15 +25,14 @@
 #include "../Audio/Audio.h"
 #include "../Core/Context.h"
 #include "../Core/CoreEvents.h"
-#include "../Core/ProcessUtils.h"
-#include "../Core/Profiler.h"
 #include "../Core/EventProfiler.h"
+#include "../Core/ProcessUtils.h"
 #include "../Core/WorkQueue.h"
 #include "../Engine/Engine.h"
 #include "../Graphics/Graphics.h"
 #include "../Graphics/Renderer.h"
-#include "../IO/FileSystem.h"
 #include "../Input/Input.h"
+#include "../IO/FileSystem.h"
 #include "../IO/Log.h"
 #include "../IO/PackageFile.h"
 
@@ -290,6 +289,7 @@ bool Engine::Initialize(const VariantMap& parameters)
             ATOMIC_LOGERRORF(
                 "Failed to add resource package '%s', check the documentation on how to set the 'resource prefix path'",
                 resourcePackages[i].CString());
+            return false;
         }
     }
 
@@ -357,7 +357,7 @@ bool Engine::Initialize(const VariantMap& parameters)
 
         if (HasParameter(parameters, "ExternalWindow"))
             graphics->SetExternalWindow(GetParameter(parameters, "ExternalWindow").GetVoidPtr());
-        graphics->SetWindowTitle(GetParameter(parameters, "WindowTitle", "Urho3D").GetString());
+        graphics->SetWindowTitle(GetParameter(parameters, "WindowTitle", "Atomic").GetString());
         graphics->SetWindowIcon(cache->GetResource<Image>(GetParameter(parameters, "WindowIcon", String::EMPTY).GetString()));
         graphics->SetFlushGPU(GetParameter(parameters, "FlushGPU", false).GetBool());
         graphics->SetOrientations(GetParameter(parameters, "Orientations", "LandscapeLeft LandscapeRight").GetString());
@@ -585,35 +585,39 @@ void Engine::Exit()
 
 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
+    if (!Thread::IsMainThread())
+        return;
+
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     const HashMap<StringHash, ResourceGroup>& resourceGroups = cache->GetAllResources();
-    ATOMIC_LOGRAW("\n");
-
     if (dumpFileName)
     {
         ATOMIC_LOGRAW("Used resources:\n");
-        for (HashMap<StringHash, ResourceGroup>::ConstIterator i = resourceGroups.Begin();
-            i != resourceGroups.End(); ++i)
+        for (HashMap<StringHash, ResourceGroup>::ConstIterator i = resourceGroups.Begin(); i != resourceGroups.End(); ++i)
         {
             const HashMap<StringHash, SharedPtr<Resource> >& resources = i->second_.resources_;
             if (dumpFileName)
             {
-                for (HashMap<StringHash, SharedPtr<Resource> >::ConstIterator j = resources.Begin();
-                    j != resources.End(); ++j)
+                for (HashMap<StringHash, SharedPtr<Resource> >::ConstIterator j = resources.Begin(); j != resources.End(); ++j)
                     ATOMIC_LOGRAW(j->second_->GetName() + "\n");
             }
         }
     }
     else
-        ATOMIC_LOGRAW(cache->PrintMemoryUsage());
+        ATOMIC_LOGRAW(cache->PrintMemoryUsage() + "\n");
 #endif
 }
 

+ 3 - 1
Source/Atomic/Graphics/Camera.h

@@ -153,7 +153,9 @@ public:
     Ray GetScreenRay(float x, float y) const;
     /// Convert a world space point to normalized screen coordinates (0.0 - 1.0).
     Vector2 WorldToScreenPoint(const Vector3& worldPos) const;
-    /// Convert normalized screen coordinates (0.0 - 1.0) and distance (in Z coordinate) to a world space point. The distance can not be closer than the near clip plane.
+    /// Convert normalized screen coordinates (0.0 - 1.0) and distance along view Z axis (in Z coordinate) to a world space point. The distance can not be closer than the near clip plane.
+    /** Note that a HitDistance() from the camera screen ray is not the same as distance along the view Z axis, as under a perspective projection the ray is likely to not be Z-aligned.
+     */
     Vector3 ScreenToWorldPoint(const Vector3& screenPos) const;
 
     /// Return projection offset.

+ 1 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Graphics.cpp

@@ -219,6 +219,7 @@ Graphics::Graphics(Context* context) :
     dxtTextureSupport_(false),
     etcTextureSupport_(false),
     pvrtcTextureSupport_(false),
+    hardwareShadowSupport_(false),
     lightPrepassSupport_(false),
     deferredSupport_(false),
     instancingSupport_(false),

+ 2 - 2
Source/Atomic/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -262,6 +262,7 @@ Graphics::Graphics(Context* context) :
     dxtTextureSupport_(false),
     etcTextureSupport_(false),
     pvrtcTextureSupport_(false),
+    hardwareShadowSupport_(false),
     lightPrepassSupport_(false),
     deferredSupport_(false),
     instancingSupport_(false),
@@ -2363,7 +2364,7 @@ void Graphics::CheckFeatureSupport()
 {
     anisotropySupport_ = true;
     dxtTextureSupport_ = true;
-    
+
     // Reset features first
     lightPrepassSupport_ = false;
     deferredSupport_ = false;
@@ -2646,4 +2647,3 @@ void Graphics::SetUBO(unsigned object)
 // ATOMIC END
 
 }
-

+ 5 - 0
Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp

@@ -245,6 +245,7 @@ Graphics::Graphics(Context* context_) :
     dxtTextureSupport_(false),
     etcTextureSupport_(false),
     pvrtcTextureSupport_(false),
+    hardwareShadowSupport_(false),
     sRGBSupport_(false),
     sRGBWriteSupport_(false),
     numPrimitives_(0),
@@ -2691,6 +2692,10 @@ void Graphics::CheckFeatureSupport()
 #endif
     }
 #endif
+
+// Consider OpenGL shadows always hardware sampled, if supported at all
+hardwareShadowSupport_ = shadowMapFormat_ != 0;
+
 }
 
 void Graphics::PrepareDraw()

+ 16 - 0
Source/Atomic/Input/Input.cpp

@@ -1867,6 +1867,19 @@ void Input::HandleSDLEvent(void* sdlEvent)
             return;
     }
 
+    // Possibility for custom handling or suppression of default handling for the SDL event
+    {
+        using namespace SDLRawInput;
+
+        VariantMap eventData = GetEventDataMap();
+        eventData[P_SDLEVENT] = &evt;
+        eventData[P_CONSUMED] = false;
+        SendEvent(E_SDLRAWINPUT, eventData);
+
+        if (eventData[P_CONSUMED].GetBool())
+            return;
+    }
+
     switch (evt.type)
     {
         case SDL_KEYDOWN:
@@ -2401,7 +2414,10 @@ void Input::HandleScreenMode(StringHash eventType, VariantMap& eventData)
 void Input::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
 {
     // Update input right at the beginning of the frame
+    SendEvent(E_INPUTBEGIN);
     Update();
+    SendEvent(E_INPUTEND);
+
 }
 
 #ifdef __EMSCRIPTEN__

+ 17 - 0
Source/Atomic/Input/InputEvents.h

@@ -223,6 +223,23 @@ ATOMIC_EVENT(E_EXITREQUESTED, ExitRequested)
 {
 }
 
+/// Raw SDL input event.
+ATOMIC_EVENT(E_SDLRAWINPUT, SDLRawInput)
+{
+    ATOMIC_PARAM(P_SDLEVENT, SDLEvent);           // SDL_Event*
+    ATOMIC_PARAM(P_CONSUMED, Consumed);            // bool
+}
+
+/// Input handling begins.
+ATOMIC_EVENT(E_INPUTBEGIN, InputBegin)
+{
+}
+
+/// Input handling ends.
+ATOMIC_EVENT(E_INPUTEND, InputEnd)
+{
+}
+
 // ATOMIC BEGIN
 
 /// Application pause requested.