From 4a1fa3ed1d45b11427803e16ae3b2f0a3af768f3 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Tue, 17 Oct 2023 17:50:53 +0900 Subject: [PATCH] Reduce update proxy performance 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 --- dali/internal/file.list | 1 + .../update/manager/frame-callback-processor.cpp | 46 +++++++- .../update/manager/frame-callback-processor.h | 8 ++ .../update/manager/scene-graph-frame-callback.cpp | 4 +- .../update/manager/scene-graph-frame-callback.h | 4 +- .../update/manager/scene-graph-traveler.cpp | 123 +++++++++++++++++++++ .../internal/update/manager/scene-graph-traveler.h | 115 +++++++++++++++++++ dali/internal/update/manager/update-proxy-impl.cpp | 57 ++-------- dali/internal/update/manager/update-proxy-impl.h | 23 ++-- 9 files changed, 318 insertions(+), 63 deletions(-) create mode 100644 dali/internal/update/manager/scene-graph-traveler.cpp create mode 100644 dali/internal/update/manager/scene-graph-traveler.h diff --git a/dali/internal/file.list b/dali/internal/file.list index ece9186..2ec62c0 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -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 diff --git a/dali/internal/update/manager/frame-callback-processor.cpp b/dali/internal/update/manager/frame-callback-processor.cpp index 6e17904..afa480e 100644 --- a/dali/internal/update/manager/frame-callback-processor.cpp +++ b/dali/internal/update/manager/frame-callback-processor.cpp @@ -24,6 +24,7 @@ // INTERNAL INCLUDES #include #include +#include #include 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& frame { Node& node = const_cast(*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 diff --git a/dali/internal/update/manager/frame-callback-processor.h b/dali/internal/update/manager/frame-callback-processor.h index d4aa2c4..1f92b65 100644 --- a/dali/internal/update/manager/frame-callback-processor.h +++ b/dali/internal/update/manager/frame-callback-processor.h @@ -20,11 +20,13 @@ // EXTERNAL INCLUDES #include +#include // INTERNAL INCLUDES #include #include #include +#include #include #include @@ -102,12 +104,18 @@ public: } private: + SceneGraphTravelerPtr GetSceneGraphTraveler(Node* rootNode); + +private: std::vector > mFrameCallbacks; ///< A container of all the frame-callbacks & accompanying update-proxies. UpdateManager& mUpdateManager; TransformManager& mTransformManager; + using TravelerContainer = std::unordered_map; + TravelerContainer mTravelerMap; + bool mNodeHierarchyChanged; ///< Set to true if the node hierarchy changes }; diff --git a/dali/internal/update/manager/scene-graph-frame-callback.cpp b/dali/internal/update/manager/scene-graph-frame-callback.cpp index 8be731c..30024b6 100644 --- a/dali/internal/update/manager/scene-graph-frame-callback.cpp +++ b/dali/internal/update/manager/scene-graph-frame-callback.cpp @@ -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(new UpdateProxy(updateManager, transformManager, rootNode)); + mUpdateProxy = std::unique_ptr(new UpdateProxy(updateManager, transformManager, rootNode, traveler)); rootNode.AddObserver(*this); } diff --git a/dali/internal/update/manager/scene-graph-frame-callback.h b/dali/internal/update/manager/scene-graph-frame-callback.h index cb8feaf..9eaf71d 100644 --- a/dali/internal/update/manager/scene-graph-frame-callback.h +++ b/dali/internal/update/manager/scene-graph-frame-callback.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -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 index 0000000..30bf0f3 --- /dev/null +++ b/dali/internal/update/manager/scene-graph-traveler.cpp @@ -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 + +// INTERNAL INCLUDES +#include + +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, ¤tNode}); + + if(currentNode.mId == id) + { + node = ¤tNode; + 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 index 0000000..a14b539 --- /dev/null +++ b/dali/internal/update/manager/scene-graph-traveler.h @@ -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 + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + +namespace Dali +{ +namespace Internal +{ +namespace SceneGraph +{ +class Node; +} // namespace SceneGraph + +class SceneGraphTraveler; +using SceneGraphTravelerPtr = IntrusivePtr; + +/** + * @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> mNodeStack; ///< Depth first search stack. Pair of node, and it's index of children that we need to iterate next. + + std::unordered_map 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 diff --git a/dali/internal/update/manager/update-proxy-impl.cpp b/dali/internal/update/manager/update-proxy-impl.cpp index f6ffa20..04006ff 100644 --- a/dali/internal/update/manager/update-proxy-impl.cpp +++ b/dali/internal/update/manager/update-proxy-impl.cpp @@ -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); diff --git a/dali/internal/update/manager/update-proxy-impl.h b/dali/internal/update/manager/update-proxy-impl.h index 865a522..3beb989 100644 --- a/dali/internal/update/manager/update-proxy-impl.h +++ b/dali/internal/update/manager/update-proxy-impl.h @@ -31,6 +31,8 @@ #include #include +#include + 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; - mutable std::vector 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 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 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 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 -- 2.7.4