From b4549fd38c01f28589410db2568b4ac95b5b8a56 Mon Sep 17 00:00:00 2001 From: halx99 Date: Sun, 7 Nov 2021 16:14:31 +0800 Subject: [PATCH] Add children indexer map for fast getChildByTag & getChildByName support --- cocos/2d/CCNode.cpp | 91 ++++++++++++++++++++++++++++++++--------- cocos/2d/CCNode.h | 5 +++ cocos/base/CCDirector.h | 14 +++++-- 3 files changed, 87 insertions(+), 23 deletions(-) diff --git a/cocos/2d/CCNode.cpp b/cocos/2d/CCNode.cpp index 606595eefe..66636b723f 100644 --- a/cocos/2d/CCNode.cpp +++ b/cocos/2d/CCNode.cpp @@ -34,6 +34,7 @@ THE SOFTWARE. #include #include +#include "xxhash.h" #include "base/CCDirector.h" #include "base/CCScheduler.h" #include "base/CCEventDispatcher.h" @@ -53,6 +54,10 @@ THE SOFTWARE. #define RENDER_IN_SUBPIXEL(__ARGS__) (ceil(__ARGS__)) #endif +/* +* 4.5x faster than std::hash in release mode +*/ +#define CC_HASH_NODE_NAME(name) (!name.empty() ? XXH3_64bits(name.c_str(), name.length()) : 0) NS_CC_BEGIN @@ -84,13 +89,14 @@ Node::Node() , _additionalTransformDirty(false) , _transformUpdated(true) // children (lazy allocs) +, _childrenIndexer(nullptr) // lazy alloc , _localZOrder$Arrival(0LL) , _globalZOrder(0) , _parent(nullptr) // "whole screen" objects. like Scenes and Layers, should set _ignoreAnchorPointForPosition to true , _tag(Node::INVALID_TAG) -, _name("") +, _name() , _hashOfName(0) // userData is always inited as nil , _userData(nullptr) @@ -149,6 +155,8 @@ Node * Node::create() Node::~Node() { CCLOGINFO( "deallocing Node: %p - tag: %i", this, _tag ); + + CC_SAFE_DELETE(_childrenIndexer); #if CC_ENABLE_SCRIPT_BINDING if (_updateScriptHandler) @@ -680,7 +688,15 @@ int Node::getTag() const /// tag setter void Node::setTag(int tag) { - _tag = tag ; + auto parentChildrenIndexer = getParentChildrenIndexer(); + if (parentChildrenIndexer) + { + if (_tag != tag) + parentChildrenIndexer->erase(_tag); + (*parentChildrenIndexer)[tag] = this; + } + + _tag = tag; } const std::string& Node::getName() const @@ -690,9 +706,30 @@ const std::string& Node::getName() const void Node::setName(const std::string& name) { + uint64_t newHash = CC_HASH_NODE_NAME(name); + auto parentChildrenIndexer = getParentChildrenIndexer(); + if (parentChildrenIndexer) + { + auto oldHash = CC_HASH_NODE_NAME(_name); + if (oldHash != newHash) + parentChildrenIndexer->erase(oldHash); + (*parentChildrenIndexer)[newHash] = this; + } + _name = name; - std::hash h; - _hashOfName = h(name); + _hashOfName = newHash; +} + +NodeIndexerMap_t* Node::getParentChildrenIndexer() +{ + if (!_director->isChildrenIndexerEnabled()) + return nullptr; + auto parent = getParent(); + + NodeIndexerMap_t* indexer = nullptr; + if (parent && !parent->_childrenIndexer) + indexer = parent->_childrenIndexer = new NodeIndexerMap_t(); + return indexer; } /// userData setter @@ -750,26 +787,41 @@ Node* Node::getChildByTag(int tag) const { CCASSERT(tag != Node::INVALID_TAG, "Invalid tag"); - for (const auto child : _children) + if (_childrenIndexer) { - if(child && child->_tag == tag) - return child; + auto it = _childrenIndexer->find(tag); + if (it != _childrenIndexer->end()) + return it->second; + } + else + { + for (const auto child : _children) + { + if (child && child->_tag == tag) + return child; + } } return nullptr; } Node* Node::getChildByName(const std::string& name) const { - CCASSERT(!name.empty(), "Invalid name"); - - std::hash h; - size_t hash = h(name); - - for (const auto& child : _children) + // CCASSERT(!name.empty(), "Invalid name"); + auto hash = CC_HASH_NODE_NAME(name); + if (_childrenIndexer) { - // Different strings may have the same hash code, but can use it to compare first for speed - if(child->_hashOfName == hash && child->_name.compare(name) == 0) - return child; + auto it = _childrenIndexer->find(hash); + if (it != _childrenIndexer->end()) + return it->second; + } + else + { + for (const auto& child : _children) + { + // Different strings may have the same hash code, but can use it to compare first for speed + if (child->_hashOfName == hash && child->_name.compare(name) == 0) + return child; + } } return nullptr; } @@ -937,14 +989,14 @@ void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::stri } this->insertChild(child, localZOrder); - + + child->setParent(this); + if (setTag) child->setTag(tag); else child->setName(name); - child->setParent(this); - child->updateOrderOfArrival(); if( _running ) @@ -1056,6 +1108,7 @@ void Node::removeAllChildrenWithCleanup(bool cleanup) } _children.clear(); + CC_SAFE_DELETE(_childrenIndexer); } void Node::resetChild(Node* child, bool cleanup) diff --git a/cocos/2d/CCNode.h b/cocos/2d/CCNode.h index 63cef196c4..019331ef35 100644 --- a/cocos/2d/CCNode.h +++ b/cocos/2d/CCNode.h @@ -82,6 +82,8 @@ enum { class EventListener; +typedef std::map NodeIndexerMap_t; + /** @class Node * @brief Node is the base element of the Scene Graph. Elements of the Scene Graph must be Node objects or subclasses of it. The most common Node objects are: Scene, Layer, Sprite, Menu, Label. @@ -1841,6 +1843,8 @@ protected: private: void addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag); + NodeIndexerMap_t* getParentChildrenIndexer(); + protected: float _rotationX; ///< rotation on the X-axis @@ -1897,6 +1901,7 @@ protected: static std::uint32_t s_globalOrderOfArrival; Vector _children; ///< array of children nodes + NodeIndexerMap_t* _childrenIndexer; ///< The children indexer for fast find child Node *_parent; ///< weak reference to parent node Director* _director; //cached director pointer to improve rendering performance int _tag; ///< a tag. Can be any number you assigned just to identify this node diff --git a/cocos/base/CCDirector.h b/cocos/base/CCDirector.h index 15895e43fb..90c072c5d1 100644 --- a/cocos/base/CCDirector.h +++ b/cocos/base/CCDirector.h @@ -4,8 +4,9 @@ Copyright (c) 2011 Zynga Inc. Copyright (c) 2013-2016 Chukong Technologies Inc. Copyright (c) 2017-2019 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2021 Bytedance Inc. -http://www.cocos2d-x.org +https://adxe.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -502,6 +503,9 @@ public: */ const std::thread::id& getCocos2dThreadId() const { return _cocos2d_thread_id; } + void setChildrenIndexerEnabled(bool enable) { _childrenIndexerEnabled = enable; } + bool isChildrenIndexerEnabled() const { return _childrenIndexerEnabled; } + /** * returns whether or not the Director is in a valid state */ @@ -638,12 +642,14 @@ protected: bool _isStatusLabelUpdated = true; - /* cocos2d thread id */ - std::thread::id _cocos2d_thread_id; - /* whether or not the director is in a valid state */ bool _invalid = false; + bool _childrenIndexerEnabled = false; + + /* cocos2d thread id */ + std::thread::id _cocos2d_thread_id; + // GLView will recreate stats labels to fit visible rect friend class GLView; };