Reduce update proxy performance 39/300139/5
authorEunki, Hong <eunkiki.hong@samsung.com>
Tue, 17 Oct 2023 08:50:53 +0000 (17:50 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Wed, 18 Oct 2023 04:28:07 +0000 (13:28 +0900)
Since we try to travel whole scene tree whenever
we fail to found node id, It might make some performance drop.

To avoid these kind of performance issue, let we cache
whole traversal scene tree only one times until scene tree
updated.

Change-Id: Iab4379c9421a18e5fa9ebcb373c7ef1de91022a5
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
dali/internal/file.list
dali/internal/update/manager/frame-callback-processor.cpp
dali/internal/update/manager/frame-callback-processor.h
dali/internal/update/manager/scene-graph-frame-callback.cpp
dali/internal/update/manager/scene-graph-frame-callback.h
dali/internal/update/manager/scene-graph-traveler.cpp [new file with mode: 0644]
dali/internal/update/manager/scene-graph-traveler.h [new file with mode: 0644]
dali/internal/update/manager/update-proxy-impl.cpp
dali/internal/update/manager/update-proxy-impl.h

index ece9186..2ec62c0 100644 (file)
@@ -153,6 +153,7 @@ SET( internal_src_files
   ${internal_src_dir}/update/manager/render-instruction-processor.cpp
   ${internal_src_dir}/update/manager/render-task-processor.cpp
   ${internal_src_dir}/update/manager/scene-graph-frame-callback.cpp
+  ${internal_src_dir}/update/manager/scene-graph-traveler.cpp
   ${internal_src_dir}/update/manager/transform-manager.cpp
   ${internal_src_dir}/update/manager/update-algorithms.cpp
   ${internal_src_dir}/update/manager/update-manager.cpp
index 6e17904..afa480e 100644 (file)
@@ -24,6 +24,7 @@
 // INTERNAL INCLUDES
 #include <dali/devel-api/update/frame-callback-interface.h>
 #include <dali/devel-api/update/update-proxy.h>
+#include <dali/integration-api/debug.h>
 #include <dali/integration-api/trace.h>
 
 namespace
@@ -41,6 +42,7 @@ FrameCallbackProcessor::FrameCallbackProcessor(UpdateManager& updateManager, Tra
 : mFrameCallbacks(),
   mUpdateManager(updateManager),
   mTransformManager(transformManager),
+  mTravelerMap{},
   mNodeHierarchyChanged(true)
 {
 }
@@ -51,7 +53,9 @@ void FrameCallbackProcessor::AddFrameCallback(OwnerPointer<FrameCallback>& frame
 {
   Node& node = const_cast<Node&>(*rootNode); // Was sent as const from event thread, we need to be able to use non-const version here.
 
-  frameCallback->ConnectToSceneGraph(mUpdateManager, mTransformManager, node);
+  SceneGraphTravelerPtr traveler = GetSceneGraphTraveler(&node);
+
+  frameCallback->ConnectToSceneGraph(mUpdateManager, mTransformManager, node, traveler);
 
   mFrameCallbacks.emplace_back(frameCallback);
 }
@@ -77,6 +81,25 @@ bool FrameCallbackProcessor::Update(BufferIndex bufferIndex, float elapsedSecond
 {
   bool keepRendering = false;
 
+  if(mNodeHierarchyChanged)
+  {
+    DALI_LOG_DEBUG_INFO("Node hierarchy changed. Update traveler map\n");
+    // Clear node traveler
+    for(auto iter = mTravelerMap.begin(); iter != mTravelerMap.end();)
+    {
+      // We don't need to erase invalidated traveler always. Just erase now.
+      // Note : ReferenceCount == 1 mean, no frame callbacks use this traveler now. We can erase it.
+      if(iter->second->IsInvalidated() || iter->second->ReferenceCount() == 1u)
+      {
+        iter = mTravelerMap.erase(iter);
+      }
+      else
+      {
+        (iter++)->second->NodeHierarchyChanged();
+      }
+    }
+  }
+
   if(!mFrameCallbacks.empty())
   {
     DALI_TRACE_SCOPE(gTraceFilter, "DALI_FRAME_CALLBACK_UPDATE");
@@ -96,6 +119,27 @@ bool FrameCallbackProcessor::Update(BufferIndex bufferIndex, float elapsedSecond
   return keepRendering;
 }
 
+SceneGraphTravelerPtr FrameCallbackProcessor::GetSceneGraphTraveler(Node* rootNode)
+{
+  auto iter = mTravelerMap.find(rootNode);
+
+  if(iter != mTravelerMap.end())
+  {
+    // Check wheter traveler is invalidated or not
+    if(!iter->second->IsInvalidated())
+    {
+      return iter->second;
+    }
+    else
+    {
+      mTravelerMap.erase(iter);
+    }
+  }
+
+  // Create new traveler and keep it.
+  return (mTravelerMap.insert({rootNode, new SceneGraphTraveler(*rootNode)})).first->second;
+}
+
 } // namespace SceneGraph
 
 } // namespace Internal
index d4aa2c4..1f92b65 100644 (file)
 
 // EXTERNAL INCLUDES
 #include <memory>
+#include <unordered_map>
 
 // INTERNAL INCLUDES
 #include <dali/internal/common/buffer-index.h>
 #include <dali/internal/common/owner-pointer.h>
 #include <dali/internal/update/manager/scene-graph-frame-callback.h>
+#include <dali/internal/update/manager/scene-graph-traveler.h>
 #include <dali/internal/update/manager/update-proxy-impl.h>
 #include <dali/public-api/common/vector-wrapper.h>
 
@@ -102,12 +104,18 @@ public:
   }
 
 private:
+  SceneGraphTravelerPtr GetSceneGraphTraveler(Node* rootNode);
+
+private:
   std::vector<OwnerPointer<FrameCallback> > mFrameCallbacks; ///< A container of all the frame-callbacks & accompanying update-proxies.
 
   UpdateManager& mUpdateManager;
 
   TransformManager& mTransformManager;
 
+  using TravelerContainer = std::unordered_map<const Node*, SceneGraphTravelerPtr>;
+  TravelerContainer mTravelerMap;
+
   bool mNodeHierarchyChanged; ///< Set to true if the node hierarchy changes
 };
 
index 8be731c..30024b6 100644 (file)
@@ -46,9 +46,9 @@ FrameCallback::~FrameCallback()
   Invalidate();
 }
 
-void FrameCallback::ConnectToSceneGraph(UpdateManager& updateManager, TransformManager& transformManager, Node& rootNode)
+void FrameCallback::ConnectToSceneGraph(UpdateManager& updateManager, TransformManager& transformManager, Node& rootNode, SceneGraphTravelerPtr traveler)
 {
-  mUpdateProxy = std::unique_ptr<UpdateProxy>(new UpdateProxy(updateManager, transformManager, rootNode));
+  mUpdateProxy = std::unique_ptr<UpdateProxy>(new UpdateProxy(updateManager, transformManager, rootNode, traveler));
   rootNode.AddObserver(*this);
 }
 
index cb8feaf..9eaf71d 100644 (file)
@@ -26,6 +26,7 @@
 #include <dali/devel-api/update/frame-callback-interface.h>
 #include <dali/internal/common/owner-pointer.h>
 #include <dali/internal/update/common/property-owner.h>
+#include <dali/internal/update/manager/scene-graph-traveler.h>
 #include <dali/internal/update/manager/update-proxy-impl.h>
 #include <dali/public-api/common/list-wrapper.h>
 
@@ -72,8 +73,9 @@ public:
    * @param[in]  updateManager     The Update Manager
    * @param[in]  transformManager  The Transform Manager
    * @param[in]  rootNode          The rootNode of this frame-callback
+   * @param[in]  traveler          The cache of traversal for given rootNode
    */
-  void ConnectToSceneGraph(UpdateManager& updateManager, TransformManager& transformManager, Node& rootNode);
+  void ConnectToSceneGraph(UpdateManager& updateManager, TransformManager& transformManager, Node& rootNode, SceneGraphTravelerPtr traveler);
 
   // Movable but not copyable
 
diff --git a/dali/internal/update/manager/scene-graph-traveler.cpp b/dali/internal/update/manager/scene-graph-traveler.cpp
new file mode 100644 (file)
index 0000000..30bf0f3
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/update/manager/scene-graph-traveler.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/update/nodes/node.h>
+
+namespace Dali
+{
+namespace Internal
+{
+SceneGraphTraveler::SceneGraphTraveler(SceneGraph::Node& rootNode)
+: mRootNode(rootNode),
+  mNodeStack{},
+  mInvalidated{false}
+{
+  mRootNode.AddObserver(*this);
+  Clear();
+}
+
+SceneGraphTraveler::~SceneGraphTraveler()
+{
+  if(!mInvalidated)
+  {
+    mRootNode.RemoveObserver(*this);
+  }
+}
+
+SceneGraph::Node* SceneGraphTraveler::FindNode(uint32_t id)
+{
+  SceneGraph::Node* node = nullptr;
+
+  // Find node in cached map
+  auto iter = mTravledNodeMap.find(id);
+  if(iter != mTravledNodeMap.end())
+  {
+    node = iter->second;
+  }
+  else
+  {
+    while(!FullSearched())
+    {
+      SceneGraph::Node& currentNode = GetCurrentNode();
+      IterateNextNode();
+
+      // Cache traveled node and id pair.
+      mTravledNodeMap.insert({currentNode.mId, &currentNode});
+
+      if(currentNode.mId == id)
+      {
+        node = &currentNode;
+        break;
+      }
+    }
+  }
+
+  return node;
+}
+
+void SceneGraphTraveler::Clear()
+{
+  mTravledNodeMap.clear();
+  mTravledNodeMap.rehash(0u); ///< Note : We have to reduce capacity of hash map. Without this line, clear() API will be slow downed.
+  mNodeStack.clear();
+  if(!mInvalidated)
+  {
+    mNodeStack.emplace_back(&mRootNode, 0u);
+  }
+}
+
+bool SceneGraphTraveler::FullSearched() const
+{
+  return mNodeStack.empty();
+}
+
+SceneGraph::Node& SceneGraphTraveler::GetCurrentNode()
+{
+  DALI_ASSERT_DEBUG(!FullSearched());
+
+  return *(mNodeStack.back().first);
+}
+
+void SceneGraphTraveler::IterateNextNode()
+{
+  while(!mNodeStack.empty())
+  {
+    auto&    currentNode       = *(mNodeStack.back().first);
+    uint32_t currentChildIndex = mNodeStack.back().second;
+
+    if(currentNode.GetChildren().Count() <= currentChildIndex)
+    {
+      mNodeStack.pop_back();
+      continue;
+    }
+    else
+    {
+      // Stack current child, and increase index
+      ++mNodeStack.back().second;
+      mNodeStack.emplace_back(currentNode.GetChildren()[currentChildIndex], 0u);
+      break;
+    }
+  }
+}
+
+} // namespace Internal
+
+} // namespace Dali
diff --git a/dali/internal/update/manager/scene-graph-traveler.h b/dali/internal/update/manager/scene-graph-traveler.h
new file mode 100644 (file)
index 0000000..a14b539
--- /dev/null
@@ -0,0 +1,115 @@
+#ifndef DALI_INTERNAL_SCENE_GRAPH_TRAVELER_H
+#define DALI_INTERNAL_SCENE_GRAPH_TRAVELER_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <unordered_map>
+
+// INTERNAL INCLUDES
+#include <dali/internal/update/common/property-owner.h>
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/object/ref-object.h>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace SceneGraph
+{
+class Node;
+} // namespace SceneGraph
+
+class SceneGraphTraveler;
+using SceneGraphTravelerPtr = IntrusivePtr<SceneGraphTraveler>;
+
+/**
+ * @brief Helper class to travel scene graph incrementally.
+ */
+class SceneGraphTraveler : public Dali::RefObject, public SceneGraph::PropertyOwner::Observer
+{
+public:
+  SceneGraphTraveler(SceneGraph::Node& rootNode);
+
+  ~SceneGraphTraveler();
+
+public:
+  void NodeHierarchyChanged()
+  {
+    Clear();
+  }
+
+  bool IsInvalidated() const
+  {
+    return mInvalidated;
+  }
+
+  SceneGraph::Node* FindNode(uint32_t id);
+
+private:
+  // From SceneGraph::PropertyOwner::Observer
+
+  /**
+   * @copydoc SceneGraph::PropertyOwner::Observer::PropertyOwnerConnected()
+   */
+  void PropertyOwnerConnected(SceneGraph::PropertyOwner& owner) override
+  { /* Nothing to do */
+  }
+
+  /**
+   * @copydoc SceneGraph::PropertyOwner::Observer::PropertyOwnerDisconnected()
+   */
+  void PropertyOwnerDisconnected(BufferIndex updateBufferIndex, SceneGraph::PropertyOwner& owner) override
+  { /* Nothing to do */
+  }
+
+  /**
+   * @copydoc SceneGraph::PropertyOwner::Observer::PropertyOwnerDisconnected()
+   */
+  void PropertyOwnerDestroyed(SceneGraph::PropertyOwner& owner) override
+  {
+    // Invalidate this traveler
+    mInvalidated = true;
+    Clear();
+  }
+
+private:
+  void Clear();
+
+  bool FullSearched() const;
+
+  SceneGraph::Node& GetCurrentNode();
+
+  void IterateNextNode();
+
+private:
+  SceneGraph::Node&                                   mRootNode;
+  std::vector<std::pair<SceneGraph::Node*, uint32_t>> mNodeStack; ///< Depth first search stack. Pair of node, and it's index of children that we need to iterate next.
+
+  std::unordered_map<uint32_t, SceneGraph::Node*> mTravledNodeMap; ///< Used to store cached pointers to already searched for Nodes.
+
+  bool mInvalidated : 1; ///< Troe if root node was destroyed.
+};
+
+} // namespace Internal
+
+} // namespace Dali
+
+#endif // DALI_INTERNAL_UPDATE_PROXY_SCENE_GRAPH_TRAVELER_H
index f6ffa20..04006ff 100644 (file)
@@ -25,41 +25,14 @@ namespace Dali
 {
 namespace Internal
 {
-namespace
-{
-SceneGraph::Node* FindNodeInSceneGraph(uint32_t id, SceneGraph::Node& node)
-{
-  SceneGraph::Node* matchingNode = nullptr;
-
-  if(node.mId == id)
-  {
-    matchingNode = &node;
-  }
-  else
-  {
-    for(auto&& i : node.GetChildren())
-    {
-      matchingNode = FindNodeInSceneGraph(id, *i);
-      if(matchingNode)
-      {
-        break;
-      }
-    }
-  }
-
-  return matchingNode;
-}
-
-} // unnamed namespace
-
-UpdateProxy::UpdateProxy(SceneGraph::UpdateManager& updateManager, SceneGraph::TransformManager& transformManager, SceneGraph::Node& rootNode)
-: mNodeContainer(),
-  mLastCachedIdNodePair({0u, nullptr}),
+UpdateProxy::UpdateProxy(SceneGraph::UpdateManager& updateManager, SceneGraph::TransformManager& transformManager, SceneGraph::Node& rootNode, SceneGraphTravelerPtr traveler)
+: mLastCachedIdNodePair({0u, nullptr}),
   mDirtyNodes(),
   mCurrentBufferIndex(0u),
   mUpdateManager(updateManager),
   mTransformManager(transformManager),
   mRootNode(rootNode),
+  mSceneGraphTraveler(traveler),
   mPropertyModifier(nullptr)
 {
 }
@@ -307,7 +280,6 @@ bool UpdateProxy::BakeColor(uint32_t id, const Vector4& color)
 void UpdateProxy::NodeHierarchyChanged()
 {
   mLastCachedIdNodePair = {0u, nullptr};
-  mNodeContainer.clear();
   mPropertyModifier.reset();
 }
 
@@ -339,26 +311,11 @@ SceneGraph::Node* UpdateProxy::GetNodeWithId(uint32_t id) const
   }
   else
   {
-    // Find node in vector
-    for(auto&& pair : mNodeContainer)
-    {
-      if(pair.id == id)
-      {
-        node                  = pair.node;
-        mLastCachedIdNodePair = pair;
-        break;
-      }
-    }
+    node = mSceneGraphTraveler->FindNode(id);
 
-    if(!node)
+    if(node)
     {
-      // Node not in vector, find in scene-graph
-      node = FindNodeInSceneGraph(id, mRootNode);
-      if(node)
-      {
-        mNodeContainer.push_back({id, node});
-        mLastCachedIdNodePair = *mNodeContainer.rbegin();
-      }
+      mLastCachedIdNodePair = {id, node};
     }
   }
 
@@ -378,7 +335,7 @@ void UpdateProxy::AddNodeResetters()
 {
   for(auto&& id : mDirtyNodes)
   {
-    SceneGraph::Node* node = FindNodeInSceneGraph(id, mRootNode);
+    SceneGraph::Node* node = GetNodeWithId(id);
     if(node)
     {
       mUpdateManager.AddNodeResetter(*node);
index 865a522..3beb989 100644 (file)
@@ -31,6 +31,8 @@
 #include <dali/public-api/math/matrix.h>
 #include <dali/public-api/math/vector3.h>
 
+#include <dali/internal/update/manager/scene-graph-traveler.h>
+
 namespace Dali
 {
 namespace Internal
@@ -55,8 +57,9 @@ public:
    * @param[in]  updateManager      Ref to the UpdateManager in order to add property resetters
    * @param[in]  transformManager   Ref to the TransformManager in order to set/get transform properties of nodes
    * @param[in]  rootNode           The root node for this proxy
+   * @param[in]  traveler           The cache of traversal for given rootNode
    */
-  UpdateProxy(SceneGraph::UpdateManager& updateManager, SceneGraph::TransformManager& transformManager, SceneGraph::Node& rootNode);
+  UpdateProxy(SceneGraph::UpdateManager& updateManager, SceneGraph::TransformManager& transformManager, SceneGraph::Node& rootNode, SceneGraphTravelerPtr traveler);
 
   /**
    * @brief Destructor.
@@ -232,16 +235,18 @@ private:
   class PropertyModifier;
   using PropertyModifierPtr = std::unique_ptr<PropertyModifier>;
 
-  mutable std::vector<IdNodePair> mNodeContainer;        ///< Used to store cached pointers to already searched for Nodes.
-  mutable IdNodePair              mLastCachedIdNodePair; ///< Used to cache the last retrieved id-node pair.
-  mutable std::vector<uint32_t>   mDirtyNodes;           ///< Used to store the ID of the dirty nodes with non-transform property modifications.
-  BufferIndex                     mCurrentBufferIndex;
+  mutable IdNodePair    mLastCachedIdNodePair; ///< Used to cache the last retrieved id-node pair.
+  std::vector<uint32_t> mDirtyNodes;           ///< Used to store the ID of the dirty nodes with non-transform property modifications.
+  BufferIndex           mCurrentBufferIndex;
+
+  SceneGraph::UpdateManager&    mUpdateManager;      ///< Reference to the Update Manager.
+  SceneGraph::TransformManager& mTransformManager;   ///< Reference to the Transform Manager.
+  SceneGraph::Node&             mRootNode;           ///< The root node of this update proxy.
+  SceneGraphTravelerPtr         mSceneGraphTraveler; ///< The cache system when we travel scene graph. (Not owned)
 
-  SceneGraph::UpdateManager&                    mUpdateManager;    ///< Reference to the Update Manager.
-  SceneGraph::TransformManager&                 mTransformManager; ///< Reference to the Transform Manager.
-  SceneGraph::Node&                             mRootNode;         ///< The root node of this update proxy.
   std::list<Dali::UpdateProxy::NotifySyncPoint> mSyncPoints;
-  PropertyModifierPtr                           mPropertyModifier; ///< To ensure non-transform property modifications reset to base values.
+
+  PropertyModifierPtr mPropertyModifier; ///< To ensure non-transform property modifications reset to base values.
 };
 
 } // namespace Internal