Add children indexer map for fast getChildByTag & getChildByName support

This commit is contained in:
halx99 2021-11-07 16:14:31 +08:00
parent ac6636641b
commit b4549fd38c
3 changed files with 87 additions and 23 deletions

View File

@ -34,6 +34,7 @@ THE SOFTWARE.
#include <string> #include <string>
#include <regex> #include <regex>
#include "xxhash.h"
#include "base/CCDirector.h" #include "base/CCDirector.h"
#include "base/CCScheduler.h" #include "base/CCScheduler.h"
#include "base/CCEventDispatcher.h" #include "base/CCEventDispatcher.h"
@ -53,6 +54,10 @@ THE SOFTWARE.
#define RENDER_IN_SUBPIXEL(__ARGS__) (ceil(__ARGS__)) #define RENDER_IN_SUBPIXEL(__ARGS__) (ceil(__ARGS__))
#endif #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 NS_CC_BEGIN
@ -84,13 +89,14 @@ Node::Node()
, _additionalTransformDirty(false) , _additionalTransformDirty(false)
, _transformUpdated(true) , _transformUpdated(true)
// children (lazy allocs) // children (lazy allocs)
, _childrenIndexer(nullptr)
// lazy alloc // lazy alloc
, _localZOrder$Arrival(0LL) , _localZOrder$Arrival(0LL)
, _globalZOrder(0) , _globalZOrder(0)
, _parent(nullptr) , _parent(nullptr)
// "whole screen" objects. like Scenes and Layers, should set _ignoreAnchorPointForPosition to true // "whole screen" objects. like Scenes and Layers, should set _ignoreAnchorPointForPosition to true
, _tag(Node::INVALID_TAG) , _tag(Node::INVALID_TAG)
, _name("") , _name()
, _hashOfName(0) , _hashOfName(0)
// userData is always inited as nil // userData is always inited as nil
, _userData(nullptr) , _userData(nullptr)
@ -150,6 +156,8 @@ Node::~Node()
{ {
CCLOGINFO( "deallocing Node: %p - tag: %i", this, _tag ); CCLOGINFO( "deallocing Node: %p - tag: %i", this, _tag );
CC_SAFE_DELETE(_childrenIndexer);
#if CC_ENABLE_SCRIPT_BINDING #if CC_ENABLE_SCRIPT_BINDING
if (_updateScriptHandler) if (_updateScriptHandler)
{ {
@ -680,7 +688,15 @@ int Node::getTag() const
/// tag setter /// tag setter
void Node::setTag(int tag) 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 const std::string& Node::getName() const
@ -690,9 +706,30 @@ const std::string& Node::getName() const
void Node::setName(const std::string& name) 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; _name = name;
std::hash<std::string> h; _hashOfName = newHash;
_hashOfName = h(name); }
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 /// userData setter
@ -750,26 +787,41 @@ Node* Node::getChildByTag(int tag) const
{ {
CCASSERT(tag != Node::INVALID_TAG, "Invalid tag"); CCASSERT(tag != Node::INVALID_TAG, "Invalid tag");
for (const auto child : _children) if (_childrenIndexer)
{ {
if(child && child->_tag == tag) auto it = _childrenIndexer->find(tag);
return child; if (it != _childrenIndexer->end())
return it->second;
}
else
{
for (const auto child : _children)
{
if (child && child->_tag == tag)
return child;
}
} }
return nullptr; return nullptr;
} }
Node* Node::getChildByName(const std::string& name) const Node* Node::getChildByName(const std::string& name) const
{ {
CCASSERT(!name.empty(), "Invalid name"); // CCASSERT(!name.empty(), "Invalid name");
auto hash = CC_HASH_NODE_NAME(name);
std::hash<std::string> h; if (_childrenIndexer)
size_t hash = h(name);
for (const auto& child : _children)
{ {
// Different strings may have the same hash code, but can use it to compare first for speed auto it = _childrenIndexer->find(hash);
if(child->_hashOfName == hash && child->_name.compare(name) == 0) if (it != _childrenIndexer->end())
return child; 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; return nullptr;
} }
@ -938,13 +990,13 @@ void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::stri
this->insertChild(child, localZOrder); this->insertChild(child, localZOrder);
child->setParent(this);
if (setTag) if (setTag)
child->setTag(tag); child->setTag(tag);
else else
child->setName(name); child->setName(name);
child->setParent(this);
child->updateOrderOfArrival(); child->updateOrderOfArrival();
if( _running ) if( _running )
@ -1056,6 +1108,7 @@ void Node::removeAllChildrenWithCleanup(bool cleanup)
} }
_children.clear(); _children.clear();
CC_SAFE_DELETE(_childrenIndexer);
} }
void Node::resetChild(Node* child, bool cleanup) void Node::resetChild(Node* child, bool cleanup)

View File

@ -82,6 +82,8 @@ enum {
class EventListener; class EventListener;
typedef std::map<uint64_t, Node*> NodeIndexerMap_t;
/** @class Node /** @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. * @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. The most common Node objects are: Scene, Layer, Sprite, Menu, Label.
@ -1841,6 +1843,8 @@ protected:
private: private:
void addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag); void addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag);
NodeIndexerMap_t* getParentChildrenIndexer();
protected: protected:
float _rotationX; ///< rotation on the X-axis float _rotationX; ///< rotation on the X-axis
@ -1897,6 +1901,7 @@ protected:
static std::uint32_t s_globalOrderOfArrival; static std::uint32_t s_globalOrderOfArrival;
Vector<Node*> _children; ///< array of children nodes Vector<Node*> _children; ///< array of children nodes
NodeIndexerMap_t* _childrenIndexer; ///< The children indexer for fast find child
Node *_parent; ///< weak reference to parent node Node *_parent; ///< weak reference to parent node
Director* _director; //cached director pointer to improve rendering performance Director* _director; //cached director pointer to improve rendering performance
int _tag; ///< a tag. Can be any number you assigned just to identify this node int _tag; ///< a tag. Can be any number you assigned just to identify this node

View File

@ -4,8 +4,9 @@
Copyright (c) 2011 Zynga Inc. Copyright (c) 2011 Zynga Inc.
Copyright (c) 2013-2016 Chukong Technologies Inc. Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2019 Xiamen Yaji Software Co., Ltd. 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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; } 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 * returns whether or not the Director is in a valid state
*/ */
@ -638,12 +642,14 @@ protected:
bool _isStatusLabelUpdated = true; bool _isStatusLabelUpdated = true;
/* cocos2d thread id */
std::thread::id _cocos2d_thread_id;
/* whether or not the director is in a valid state */ /* whether or not the director is in a valid state */
bool _invalid = false; bool _invalid = false;
bool _childrenIndexerEnabled = false;
/* cocos2d thread id */
std::thread::id _cocos2d_thread_id;
// GLView will recreate stats labels to fit visible rect // GLView will recreate stats labels to fit visible rect
friend class GLView; friend class GLView;
}; };