Browse Source

Use a VariantMap cache when sending JS registered events

Josh Engebretson 10 years ago
parent
commit
fba93453ae

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

@@ -54,7 +54,8 @@ void RemoveNamedAttribute(HashMap<StringHash, Vector<AttributeInfo> >& attribute
 
 
 Context::Context() :
 Context::Context() :
     eventHandler_(0),
     eventHandler_(0),
-    editorContext_(false)
+    editorContext_(false),
+    globalEventListener_(0)
 {
 {
 #ifdef ANDROID
 #ifdef ANDROID
     // Always reset the random seed on Android, as the Urho3D library might not be unloaded between runs
     // Always reset the random seed on Android, as the Urho3D library might not be unloaded between runs

+ 20 - 2
Source/Atomic/Core/Context.h

@@ -30,6 +30,17 @@
 namespace Atomic
 namespace Atomic
 {
 {
 
 
+// ATOMIC BEGIN
+
+class GlobalEventListener
+{
+public:
+    virtual void BeginSendEvent(Context* context, Object* sender, StringHash eventType, VariantMap& eventData) = 0;
+    virtual void EndSendEvent(Context* context, Object* sender, StringHash eventType, VariantMap& eventData) = 0;
+};
+
+// ATOMIC END
+
 /// Atomic execution context. Provides access to subsystems, object factories and attributes, and event receivers.
 /// Atomic execution context. Provides access to subsystems, object factories and attributes, and event receivers.
 class ATOMIC_API Context : public RefCounted
 class ATOMIC_API Context : public RefCounted
 {
 {
@@ -144,6 +155,10 @@ public:
     }
     }
 
 
     // ATOMIC BEGIN
     // ATOMIC BEGIN
+
+    // hook for listening into events
+    void SetGlobalEventListener(GlobalEventListener* listener) { globalEventListener_ = listener; }
+
     /// Get whether an Editor Context
     /// Get whether an Editor Context
     void SetEditorContent(bool editor) { editorContext_ = editor; }
     void SetEditorContent(bool editor) { editorContext_ = editor; }
     // ATOMIC END
     // ATOMIC END
@@ -164,10 +179,10 @@ private:
     void SetEventHandler(EventHandler* handler) { eventHandler_ = handler; }
     void SetEventHandler(EventHandler* handler) { eventHandler_ = handler; }
 
 
     /// Begin event send.
     /// Begin event send.
-    void BeginSendEvent(Object* sender) { eventSenders_.Push(sender); }
+    void BeginSendEvent(Object* sender, StringHash eventType, VariantMap& eventData) { if (globalEventListener_) globalEventListener_->BeginSendEvent(this, sender, eventType, eventData); eventSenders_.Push(sender); }
 
 
     /// End event send. Clean up event receivers removed in the meanwhile.
     /// End event send. Clean up event receivers removed in the meanwhile.
-    void EndSendEvent() { eventSenders_.Pop(); }
+    void EndSendEvent(Object* sender, StringHash eventType, VariantMap& eventData) { if (globalEventListener_) globalEventListener_->EndSendEvent(this, sender, eventType, eventData); eventSenders_.Pop(); }
 
 
     /// Object factories.
     /// Object factories.
     HashMap<StringHash, SharedPtr<ObjectFactory> > factories_;
     HashMap<StringHash, SharedPtr<ObjectFactory> > factories_;
@@ -190,7 +205,10 @@ private:
     /// Object categories.
     /// Object categories.
     HashMap<String, Vector<StringHash> > objectCategories_;
     HashMap<String, Vector<StringHash> > objectCategories_;
 
 
+    // ATOMIC BEGIN
+    GlobalEventListener* globalEventListener_;
     bool editorContext_;
     bool editorContext_;
+    // ATOMIC END
 };
 };
 
 
 template <class T> void Context::RegisterFactory() { RegisterFactory(new ObjectFactoryImpl<T>(this)); }
 template <class T> void Context::RegisterFactory() { RegisterFactory(new ObjectFactoryImpl<T>(this)); }

+ 5 - 5
Source/Atomic/Core/Object.cpp

@@ -236,7 +236,7 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
     Context* context = context_;
     Context* context = context_;
     HashSet<Object*> processed;
     HashSet<Object*> processed;
 
 
-    context->BeginSendEvent(this);
+    context->BeginSendEvent(this, eventType, eventData);
 
 
     // Check first the specific event receivers
     // Check first the specific event receivers
     const HashSet<Object*>* group = context->GetEventReceivers(this, eventType);
     const HashSet<Object*>* group = context->GetEventReceivers(this, eventType);
@@ -256,7 +256,7 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
             // If self has been destroyed as a result of event handling, exit
             // If self has been destroyed as a result of event handling, exit
             if (self.Expired())
             if (self.Expired())
             {
             {
-                context->EndSendEvent();
+                context->EndSendEvent(this, eventType, eventData);
                 return;
                 return;
             }
             }
 
 
@@ -288,7 +288,7 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
 
 
                 if (self.Expired())
                 if (self.Expired())
                 {
                 {
-                    context->EndSendEvent();
+                    context->EndSendEvent(this,eventType,  eventData);
                     return;
                     return;
                 }
                 }
 
 
@@ -314,7 +314,7 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
 
 
                     if (self.Expired())
                     if (self.Expired())
                     {
                     {
-                        context->EndSendEvent();
+                        context->EndSendEvent(this, eventType, eventData);
                         return;
                         return;
                     }
                     }
 
 
@@ -325,7 +325,7 @@ void Object::SendEvent(StringHash eventType, VariantMap& eventData)
         }
         }
     }
     }
 
 
-    context->EndSendEvent();
+    context->EndSendEvent(this,eventType, eventData);
 }
 }
 
 
 VariantMap& Object::GetEventDataMap() const
 VariantMap& Object::GetEventDataMap() const

+ 1 - 0
Source/AtomicJS/Javascript/JSAPI.h

@@ -12,6 +12,7 @@ typedef void* JS_HEAP_PTR;
 
 
 #define JS_GLOBALSTASH_INDEX_COMPONENTS 0
 #define JS_GLOBALSTASH_INDEX_COMPONENTS 0
 #define JS_GLOBALSTASH_INDEX_NODE_REGISTRY 1
 #define JS_GLOBALSTASH_INDEX_NODE_REGISTRY 1
+#define JS_GLOBALSTASH_VARIANTMAP_CACHE 2
 
 
 // indexers for instance objects
 // indexers for instance objects
 #define JS_INSTANCE_INDEX_FINALIZED 0
 #define JS_INSTANCE_INDEX_FINALIZED 0

+ 2 - 0
Source/AtomicJS/Javascript/JSAtomic.cpp

@@ -310,6 +310,8 @@ void jsapi_init_atomic(JSVM* vm)
     duk_push_global_stash(ctx);
     duk_push_global_stash(ctx);
     duk_push_object(ctx);
     duk_push_object(ctx);
     duk_put_prop_index(ctx, -2, JS_GLOBALSTASH_INDEX_NODE_REGISTRY);
     duk_put_prop_index(ctx, -2, JS_GLOBALSTASH_INDEX_NODE_REGISTRY);
+    duk_push_object(ctx);
+    duk_put_prop_index(ctx, -2, JS_GLOBALSTASH_VARIANTMAP_CACHE);
     duk_pop(ctx);
     duk_pop(ctx);
 
 
     duk_push_c_function(ctx, js_openConsoleWindow, 0);
     duk_push_c_function(ctx, js_openConsoleWindow, 0);

+ 75 - 4
Source/AtomicJS/Javascript/JSEventHelper.cpp

@@ -7,9 +7,67 @@
 namespace Atomic
 namespace Atomic
 {
 {
 
 
+
+JSEventDispatcher::JSEventDispatcher(Context* context) :
+    Object(context)
+{
+
+}
+
+JSEventDispatcher::~JSEventDispatcher()
+{
+}
+
+void JSEventDispatcher::BeginSendEvent(Context* context, Object* sender, StringHash eventType, VariantMap& eventData)
+{
+    JSVM* vm = JSVM::GetJSVM(NULL);
+
+    if (!vm)
+        return;
+
+    if (!jsEvents_.Contains(eventType))
+        return;
+
+    duk_context* ctx = vm->GetJSContext();
+
+    duk_push_global_stash(ctx);
+    duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_VARIANTMAP_CACHE);
+
+    duk_push_pointer(ctx, (void*) &eventData);
+    js_push_variantmap(ctx, eventData);
+    duk_put_prop(ctx, -3);
+
+    duk_pop_2(ctx);
+
+}
+
+void JSEventDispatcher::EndSendEvent(Context* context, Object* sender, StringHash eventType, VariantMap& eventData)
+{
+    JSVM* vm = JSVM::GetJSVM(NULL);
+
+    if (!vm)
+        return;
+
+    if (!jsEvents_.Contains(eventType))
+        return;
+
+    duk_context* ctx = vm->GetJSContext();
+
+    duk_push_global_stash(ctx);
+    duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_VARIANTMAP_CACHE);
+
+    duk_push_pointer(ctx, (void*) &eventData);
+    duk_push_undefined(ctx);
+    duk_put_prop(ctx, -3);
+
+    duk_pop_2(ctx);
+
+}
+
 JSEventHelper::JSEventHelper(Context* context) :
 JSEventHelper::JSEventHelper(Context* context) :
     Object(context)
     Object(context)
 {
 {
+
 }
 }
 
 
 JSEventHelper::~JSEventHelper()
 JSEventHelper::~JSEventHelper()
@@ -19,11 +77,15 @@ JSEventHelper::~JSEventHelper()
 
 
 void JSEventHelper::AddEventHandler(StringHash eventType)
 void JSEventHelper::AddEventHandler(StringHash eventType)
 {
 {
+    GetSubsystem<JSEventDispatcher>()->RegisterJSEvent(eventType);
+
     SubscribeToEvent(eventType, HANDLER(JSEventHelper, HandleEvent));
     SubscribeToEvent(eventType, HANDLER(JSEventHelper, HandleEvent));
 }
 }
 
 
 void JSEventHelper::AddEventHandler(Object* sender, StringHash eventType)
 void JSEventHelper::AddEventHandler(Object* sender, StringHash eventType)
 {
 {
+    GetSubsystem<JSEventDispatcher>()->RegisterJSEvent(eventType);
+
     SubscribeToEvent(sender, eventType, HANDLER(JSEventHelper, HandleEvent));
     SubscribeToEvent(sender, eventType, HANDLER(JSEventHelper, HandleEvent));
 }
 }
 
 
@@ -47,10 +109,19 @@ void JSEventHelper::HandleEvent(StringHash eventType, VariantMap& eventData)
     {
     {
         assert(duk_is_object(ctx, -1));
         assert(duk_is_object(ctx, -1));
 
 
-        //TODO: this is expensive, we should be caching these in VM
-        // and reusing, instead of creating new variant map object
-        // per event handler
-        js_push_variantmap(ctx, eventData);
+        duk_push_global_stash(ctx);
+        duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_VARIANTMAP_CACHE);
+        duk_push_pointer(ctx, (void*) &eventData);
+        duk_get_prop(ctx, -2);
+
+        duk_remove(ctx, -2); // vmap cache
+        duk_remove(ctx, -2); // global stash
+
+        if (!duk_is_object(ctx, -1))
+        {
+            duk_set_top(ctx, top);
+            return;
+        }
 
 
         if (duk_pcall(ctx, 1) != 0)
         if (duk_pcall(ctx, 1) != 0)
         {
         {

+ 22 - 0
Source/AtomicJS/Javascript/JSEventHelper.h

@@ -2,10 +2,32 @@
 #pragma once
 #pragma once
 
 
 #include <Atomic/Core/Object.h>
 #include <Atomic/Core/Object.h>
+#include <Atomic/Core/Context.h>
 
 
 namespace Atomic
 namespace Atomic
 {
 {
 
 
+class ATOMIC_API JSEventDispatcher : public Object, public GlobalEventListener
+{
+    OBJECT(JSEventDispatcher);
+
+public:
+    /// Construct.
+    JSEventDispatcher(Context* context);
+    /// Destruct.
+    virtual ~JSEventDispatcher();
+
+    void RegisterJSEvent(StringHash hash) { jsEvents_[hash] = true; }
+
+private:
+
+    void BeginSendEvent(Context* context, Object* sender, StringHash eventType, VariantMap& eventData);
+    void EndSendEvent(Context* context, Object* sender, StringHash eventType, VariantMap& eventData);
+
+    HashMap<StringHash, bool> jsEvents_;
+
+};
+
 class ATOMIC_API JSEventHelper : public Object
 class ATOMIC_API JSEventHelper : public Object
 {
 {
     OBJECT(JSEventHelper);
     OBJECT(JSEventHelper);

+ 8 - 0
Source/AtomicJS/Javascript/JSVM.cpp

@@ -21,6 +21,7 @@
 #include "JSAtomic.h"
 #include "JSAtomic.h"
 #include "JSUI.h"
 #include "JSUI.h"
 #include "JSMetrics.h"
 #include "JSMetrics.h"
+#include "JSEventHelper.h"
 
 
 namespace Atomic
 namespace Atomic
 {
 {
@@ -37,10 +38,17 @@ JSVM::JSVM(Context* context) :
     instance_ = this;
     instance_ = this;
 
 
     metrics_ = new JSMetrics(context, this);
     metrics_ = new JSMetrics(context, this);
+
+    context_->RegisterSubsystem(new JSEventDispatcher(context_));
+    context_->SetGlobalEventListener(context_->GetSubsystem<JSEventDispatcher>());
+
 }
 }
 
 
 JSVM::~JSVM()
 JSVM::~JSVM()
 {
 {
+    context_->SetGlobalEventListener(0);
+    context_->RemoveSubsystem(JSEventDispatcher::GetTypeStatic());
+
     duk_destroy_heap(ctx_);
     duk_destroy_heap(ctx_);
     instance_ = NULL;
     instance_ = NULL;
 }
 }