Browse Source

Improved ScriptInstance performance. Faster call of event handlers. Obtaining information about the attributes of an object is performed only once and is cached. (#2836)

orefkov 4 years ago
parent
commit
d5b763de93

+ 1 - 0
Source/Urho3D/AngelScript/Script.h

@@ -58,6 +58,7 @@ class URHO3D_API Script : public Object
     URHO3D_OBJECT(Script, Object);
 
     friend class ScriptFile;
+    friend class ScriptInstance;
 
 public:
     /// Construct.

+ 1 - 0
Source/Urho3D/AngelScript/ScriptAPI.cpp

@@ -361,6 +361,7 @@ static void RegisterScript(asIScriptEngine* engine)
 static void RegisterScriptObject(asIScriptEngine* engine)
 {
     engine->RegisterInterface("ScriptObject");
+    engine->SetTypeInfoUserDataCleanupCallback(CleanupTypeInfoScriptInstance, eAttrMapUserIdx);
 }
 
 void RegisterScriptInterfaceAPI(asIScriptEngine* engine)

+ 156 - 119
Source/Urho3D/AngelScript/ScriptInstance.cpp

@@ -62,6 +62,26 @@ static const char* methodDeclarations[] = {
     "void TransformChanged()"
 };
 
+void CleanupTypeInfoScriptInstance(asITypeInfo *type)
+{
+    delete static_cast<Vector<AttributeInfo>*>(type->GetUserData(eAttrMapUserIdx));
+}
+
+template<typename Op>
+inline void ScriptInstance::executeScript(asIScriptFunction* method, Op func) const {
+    Script* scriptSystem = GetSubsystem<Script>();
+    asIScriptContext* context = scriptSystem->GetScriptFileContext();
+    if (context->Prepare(method) < 0)
+        return;
+    context->SetObject(scriptObject_);
+    func(context);
+    scriptSystem->IncScriptNestingLevel();
+    context->Execute();
+    context->Unprepare();
+    scriptSystem->DecScriptNestingLevel();
+}
+
+
 ScriptInstance::ScriptInstance(Context* context) :
     Component(context)
 {
@@ -221,7 +241,7 @@ void ScriptInstance::ApplyAttributes()
     idAttributes_.Clear();
 
     if (scriptObject_ && methods_[METHOD_APPLYATTRIBUTES])
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_APPLYATTRIBUTES]);
+        executeScript(methods_[METHOD_APPLYATTRIBUTES], [](asIScriptContext*) {});
 }
 
 void ScriptInstance::OnSetEnabled()
@@ -464,9 +484,9 @@ void ScriptInstance::SetScriptDataAttr(const PODVector<unsigned char>& data)
     if (scriptObject_ && methods_[METHOD_LOAD])
     {
         MemoryBuffer buf(data);
-        VariantVector parameters;
-        parameters.Push(Variant((void*)static_cast<Deserializer*>(&buf)));
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_LOAD], parameters);
+        executeScript(methods_[METHOD_LOAD], [&](asIScriptContext* context) {
+            context->SetArgObject(0, &buf);
+        });
     }
 }
 
@@ -475,9 +495,9 @@ void ScriptInstance::SetScriptNetworkDataAttr(const PODVector<unsigned char>& da
     if (scriptObject_ && methods_[METHOD_READNETWORKUPDATE])
     {
         MemoryBuffer buf(data);
-        VariantVector parameters;
-        parameters.Push(Variant((void*)static_cast<Deserializer*>(&buf)));
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_READNETWORKUPDATE], parameters);
+        executeScript(methods_[METHOD_READNETWORKUPDATE], [&](asIScriptContext* context) {
+            context->SetArgObject(0, &buf);
+        });
     }
 }
 
@@ -508,9 +528,9 @@ PODVector<unsigned char> ScriptInstance::GetScriptDataAttr() const
     else
     {
         VectorBuffer buf;
-        VariantVector parameters;
-        parameters.Push(Variant((void*)static_cast<Serializer*>(&buf)));
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_SAVE], parameters);
+        executeScript(methods_[METHOD_SAVE], [&](asIScriptContext* context) {
+            context->SetArgObject(0, &buf);
+        });
         return buf.GetBuffer();
     }
 }
@@ -522,9 +542,9 @@ PODVector<unsigned char> ScriptInstance::GetScriptNetworkDataAttr() const
     else
     {
         VectorBuffer buf;
-        VariantVector parameters;
-        parameters.Push(Variant((void*)static_cast<Serializer*>(&buf)));
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_WRITENETWORKUPDATE], parameters);
+        executeScript(methods_[METHOD_WRITENETWORKUPDATE], [&](asIScriptContext* context) {
+            context->SetArgObject(0, &buf);
+        });
         return buf.GetBuffer();
     }
 }
@@ -557,7 +577,7 @@ void ScriptInstance::OnMarkedDirty(Node* node)
     }
 
     if (scriptObject_ && methods_[METHOD_TRANSFORMCHANGED])
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_TRANSFORMCHANGED]);
+        executeScript(methods_[METHOD_TRANSFORMCHANGED], [](asIScriptContext*) {});
 }
 
 void ScriptInstance::CreateObject()
@@ -578,7 +598,7 @@ void ScriptInstance::CreateObject()
         UpdateEventSubscription();
 
         if (methods_[METHOD_START])
-            scriptFile_->Execute(scriptObject_, methods_[METHOD_START]);
+            executeScript(methods_[METHOD_START], [](asIScriptContext*) {});
     }
     else
         URHO3D_LOGERROR("Failed to create object of class " + className_ + " from " + scriptFile_->GetName());
@@ -589,7 +609,7 @@ void ScriptInstance::ReleaseObject()
     if (scriptObject_)
     {
         if (methods_[METHOD_STOP])
-            scriptFile_->Execute(scriptObject_, methods_[METHOD_STOP]);
+            executeScript(methods_[METHOD_STOP], [](asIScriptContext*) {});
 
         PODVector<StringHash> exceptions;
         exceptions.Push(E_RELOADSTARTED);
@@ -631,97 +651,120 @@ void ScriptInstance::GetScriptMethods()
 
 void ScriptInstance::GetScriptAttributes()
 {
-    asIScriptEngine* engine = GetSubsystem<Script>()->GetScriptEngine();
     attributeInfos_ = *context_->GetAttributes(GetTypeStatic());
+    asITypeInfo* pTypeInfo = scriptObject_->GetObjectType();
+    Vector<AttributeInfo>* attrTemplate = reinterpret_cast<Vector<AttributeInfo>*>(pTypeInfo->GetUserData(eAttrMapUserIdx));
 
-    unsigned numProperties = scriptObject_->GetPropertyCount();
-    for (unsigned i = 0; i < numProperties; ++i)
+    if (!attrTemplate)
     {
-        const char* name = nullptr;
-        int typeId = 0; // AngelScript void typeid
-        bool isPrivate=false, isProtected=false, isHandle=false, isEnum=false;
+        attrTemplate = new Vector<AttributeInfo>;
+        pTypeInfo->SetUserData(attrTemplate, eAttrMapUserIdx);
 
-        scriptObject_->GetObjectType()->GetProperty(i, &name, &typeId, &isPrivate, &isProtected);
+        asIScriptEngine* engine = pTypeInfo->GetEngine();
+        unsigned numProperties = pTypeInfo->GetPropertyCount();
+        for (unsigned i = 0; i < numProperties; ++i)
+        {
+            const char* name = nullptr;
+            int typeId = 0, offset; // AngelScript void typeid
+            bool isPrivate = false, isProtected = false, isHandle = false, isEnum = false;
 
-        // Hide private variables or ones that begin with an underscore
-        if (isPrivate || isProtected || name[0] == '_')
-            continue;
+            pTypeInfo->GetProperty(i, &name, &typeId, &isPrivate, &isProtected, &offset);
 
-        String typeName = engine->GetTypeDeclaration(typeId);
-        isHandle = typeName.EndsWith("@");
-        if (isHandle)
-            typeName = typeName.Substring(0, typeName.Length() - 1);
+            // Hide private variables or ones that begin with an underscore
+            if (isPrivate || isProtected || name[0] == '_')
+                continue;
 
-        if (engine->GetTypeInfoById(typeId))
-            isEnum = engine->GetTypeInfoById(typeId)->GetFlags() & asOBJ_ENUM;
+            isHandle = typeId & asTYPEID_OBJHANDLE;
+            String typeName = engine->GetTypeDeclaration(typeId & ~asTYPEID_OBJHANDLE);
+            isEnum = (typeId & asTYPEID_MASK_OBJECT) == 0 && typeId > asTYPEID_DOUBLE;
 
-        AttributeInfo info;
-        info.mode_ = AM_FILE;
-        info.name_ = name;
-        info.ptr_ = scriptObject_->GetAddressOfProperty(i);
+            AttributeInfo info;
 
-        if (isEnum)
-        {
-            info.type_ = VAR_INT;
-            info.enumNames_ = GetSubsystem<Script>()->GetEnumValues(typeId);
-        }
-        else if (!isHandle)
-        {
-            switch (typeId)
+            if (isEnum)
             {
-            case asTYPEID_BOOL:
-                info.type_ = VAR_BOOL;
-                break;
-
-            case asTYPEID_INT32:
-            case asTYPEID_UINT32:
                 info.type_ = VAR_INT;
-                break;
-
-            case asTYPEID_FLOAT:
-                info.type_ = VAR_FLOAT;
-                break;
-
-            default:
-                if (typeName == "Variant[]")
-                    info.type_ = VAR_VARIANTVECTOR;
-                else if (typeName == "String[]")
-                    info.type_ = VAR_STRINGVECTOR;
-                else
-                    info.type_ = Variant::GetTypeFromName(typeName);
-                break;
+                info.enumNames_ = GetSubsystem<Script>()->GetEnumValues(typeId);
             }
-        }
-        else
-        {
-            // For a handle type, check if it's an Object subclass with a registered factory
-            StringHash typeHash(typeName);
-            const HashMap<StringHash, SharedPtr<ObjectFactory> >& factories = context_->GetObjectFactories();
-            HashMap<StringHash, SharedPtr<ObjectFactory> >::ConstIterator j = factories.Find(typeHash);
-            if (j != factories.End())
+            else if (!isHandle)
             {
-                // Check base class type. Node & Component are supported as ID attributes, Resource as a resource reference
-                const TypeInfo* typeInfo = j->second_->GetTypeInfo();
-                if (typeInfo->IsTypeOf<Node>())
+                switch (typeId)
                 {
-                    info.mode_ |= AM_NODEID;
-                    info.type_ = VAR_INT;
-                }
-                else if (typeInfo->IsTypeOf<Component>())
-                {
-                    info.mode_ |= AM_COMPONENTID;
+                case asTYPEID_BOOL:
+                    info.type_ = VAR_BOOL;
+                    break;
+
+                case asTYPEID_INT32:
+                case asTYPEID_UINT32:
                     info.type_ = VAR_INT;
+                    break;
+
+                case asTYPEID_FLOAT:
+                    info.type_ = VAR_FLOAT;
+                    break;
+                case asTYPEID_DOUBLE:
+                    info.type_ = VAR_DOUBLE;
+                    break;
+                
+                case asTYPEID_INT64:
+                case asTYPEID_UINT64:
+                    info.type_ = VAR_INT64;
+                    break;
+
+                default:
+                    if (typeName == "Variant[]")
+                        info.type_ = VAR_VARIANTVECTOR;
+                    else if (typeName == "String[]")
+                        info.type_ = VAR_STRINGVECTOR;
+                    else
+                        info.type_ = Variant::GetTypeFromName(typeName);
+                    break;
                 }
-                else if (typeInfo->IsTypeOf<Resource>())
+            }
+            else
+            {
+                // For a handle type, check if it's an Object subclass with a registered factory
+                StringHash typeHash(typeName);
+                const HashMap<StringHash, SharedPtr<ObjectFactory> >& factories = context_->GetObjectFactories();
+                HashMap<StringHash, SharedPtr<ObjectFactory> >::ConstIterator j = factories.Find(typeHash);
+                if (j != factories.End())
                 {
-                    info.type_ = VAR_RESOURCEREF;
-                    info.defaultValue_ = ResourceRef(typeHash);
+                    // Check base class type. Node & Component are supported as ID attributes, Resource as a resource reference
+                    const TypeInfo* typeInfo = j->second_->GetTypeInfo();
+                    if (typeInfo->IsTypeOf<Node>())
+                    {
+                        info.mode_ = AM_NODEID;
+                        info.type_ = VAR_INT;
+                    }
+                    else if (typeInfo->IsTypeOf<Component>())
+                    {
+                        info.mode_ = AM_COMPONENTID;
+                        info.type_ = VAR_INT;
+                    }
+                    else if (typeInfo->IsTypeOf<Resource>())
+                    {
+                        info.type_ = VAR_RESOURCEREF;
+                        info.defaultValue_ = ResourceRef(typeHash);
+                    }
                 }
             }
+
+            if (info.type_ != VAR_NONE)
+            {
+                info.mode_ |= AM_FILE;
+                info.name_ = name;
+                info.ptr_ = (void*)i;
+                attrTemplate->Push(info);
+            }
         }
 
-        if (info.type_ != VAR_NONE)
-            attributeInfos_.Push(info);
+    }
+    attributeInfos_.Reserve(attributeInfos_.Size() + attrTemplate->Size());
+    for (unsigned i = 0; i < attrTemplate->Size(); ++i)
+    {
+        attributeInfos_.Push(attrTemplate->At(i));
+        AttributeInfo& back = attributeInfos_.Back();
+        back.ptr_ = scriptObject_->GetAddressOfProperty((size_t)back.ptr_);
+        int t = 1;
     }
 }
 
@@ -870,28 +913,25 @@ void ScriptInstance::HandleSceneUpdate(StringHash eventType, VariantMap& eventDa
     // Execute delayed start before first update
     if (methods_[METHOD_DELAYEDSTART])
     {
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_DELAYEDSTART]);
+        executeScript(methods_[METHOD_DELAYEDSTART], [](asIScriptContext*) {});
         methods_[METHOD_DELAYEDSTART] = nullptr;  // Only execute once
     }
 
     if (methods_[METHOD_UPDATE])
-    {
-        VariantVector parameters;
-        parameters.Push(timeStep);
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_UPDATE], parameters);
-    }
+        executeScript(methods_[METHOD_UPDATE], [&](asIScriptContext* context) {
+            context->SetArgFloat(0, timeStep);
+        });
 }
 
 void ScriptInstance::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
 {
-    if (!scriptObject_)
+    if (!scriptObject_ || !methods_[METHOD_POSTUPDATE])
         return;
 
     using namespace ScenePostUpdate;
-
-    VariantVector parameters;
-    parameters.Push(eventData[P_TIMESTEP]);
-    scriptFile_->Execute(scriptObject_, methods_[METHOD_POSTUPDATE], parameters);
+    executeScript(methods_[METHOD_POSTUPDATE], [&](asIScriptContext* context) {
+        context->SetArgFloat(0, eventData[P_TIMESTEP].GetFloat());
+    });
 }
 
 #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
@@ -904,27 +944,26 @@ void ScriptInstance::HandlePhysicsPreStep(StringHash eventType, VariantMap& even
     // Execute delayed start before first fixed update if not called yet
     if (methods_[METHOD_DELAYEDSTART])
     {
-        scriptFile_->Execute(scriptObject_, methods_[METHOD_DELAYEDSTART]);
+        executeScript(methods_[METHOD_DELAYEDSTART], [](asIScriptContext*) {});
         methods_[METHOD_DELAYEDSTART] = nullptr;  // Only execute once
     }
-
-    using namespace PhysicsPreStep;
-
-    VariantVector parameters;
-    parameters.Push(eventData[P_TIMESTEP]);
-    scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDUPDATE], parameters);
+    if (methods_[METHOD_FIXEDUPDATE]) {
+        using namespace PhysicsPreStep;
+        executeScript(methods_[METHOD_FIXEDUPDATE], [&](asIScriptContext* context) {
+            context->SetArgFloat(0, eventData[P_TIMESTEP].GetFloat());
+            });
+    }
 }
 
 void ScriptInstance::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
 {
-    if (!scriptObject_)
+    if (!scriptObject_ || !methods_[METHOD_FIXEDPOSTUPDATE])
         return;
 
     using namespace PhysicsPostStep;
-
-    VariantVector parameters;
-    parameters.Push(eventData[P_TIMESTEP]);
-    scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDPOSTUPDATE], parameters);
+    executeScript(methods_[METHOD_FIXEDPOSTUPDATE], [&](asIScriptContext* context) {
+        context->SetArgFloat(0, eventData[P_TIMESTEP].GetFloat());
+    });
 }
 
 #endif
@@ -933,17 +972,15 @@ void ScriptInstance::HandleScriptEvent(StringHash eventType, VariantMap& eventDa
 {
     if (!IsEnabledEffective() || !scriptFile_ || !scriptObject_)
         return;
-
-    auto* method = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
-
-    VariantVector parameters;
-    if (method->GetParamCount() > 0)
-    {
-        parameters.Push(Variant((void*)&eventType));
-        parameters.Push(Variant((void*)&eventData));
+    asIScriptFunction* method = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
+    if (method->GetParamCount()) {
+        executeScript(method, [&](asIScriptContext* context) {
+            context->SetArgObject(0, &eventType);
+            context->SetArgObject(1, &eventData);
+        });
+    } else {
+        executeScript(method, [](asIScriptContext* context) {});
     }
-
-    scriptFile_->Execute(scriptObject_, method, parameters);
 }
 
 void ScriptInstance::HandleScriptFileReload(StringHash eventType, VariantMap& eventData)

+ 5 - 0
Source/Urho3D/AngelScript/ScriptInstance.h

@@ -36,6 +36,8 @@ namespace Urho3D
 
 class Script;
 class ScriptFile;
+enum { eAttrMapUserIdx = 0x1df4};
+void CleanupTypeInfoScriptInstance(asITypeInfo *type);
 
 /// Inbuilt scripted component methods.
 enum ScriptInstanceMethod
@@ -191,6 +193,9 @@ private:
     /// Handle script file reload finished.
     void HandleScriptFileReloadFinished(StringHash eventType, VariantMap& eventData);
 
+    template<typename Op>
+    void executeScript(asIScriptFunction* method, Op func) const;
+
     /// Script file.
     WeakPtr<ScriptFile> scriptFile_;
     /// Script object.