feat(node): 优化节点管理和空间索引处理

- 在 SpatialHash 中增加节点存在性检查,避免重复插入
- 为 Node 类添加批量添加子节点方法 addChildren
- 使用哈希表优化子节点和动作的查找性能
- 重构成员变量布局以优化内存使用
- 改进世界变换计算方式,防止栈溢出
- 增强渲染命令收集功能,支持递归子节点
This commit is contained in:
ChestnutYueyue 2026-02-10 03:21:54 +08:00
parent 8b28f3c6df
commit 82c44b966f
3 changed files with 217 additions and 60 deletions

View File

@ -30,6 +30,13 @@ public:
// 层级管理 // 层级管理
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void addChild(Ptr<Node> child); void addChild(Ptr<Node> child);
/**
* @brief
* @param children
*/
void addChildren(std::vector<Ptr<Node>> &&children);
void removeChild(Ptr<Node> child); void removeChild(Ptr<Node> child);
void removeChildByName(const std::string &name); void removeChildByName(const std::string &name);
void removeFromParent(); void removeFromParent();
@ -161,42 +168,62 @@ protected:
float getOpacityRef() { return opacity_; } float getOpacityRef() { return opacity_; }
private: private:
// 层级 // ==========================================================================
WeakPtr<Node> parent_; // 成员变量按类型大小降序排列,减少内存对齐填充
std::vector<Ptr<Node>> children_; // 64位系统对齐std::string(32) > glm::mat4(64) > std::vector(24) >
bool childrenOrderDirty_ = false; // double(8) > float(4) > int(4) > bool(1)
// ==========================================================================
// 变换 // 1. 大块内存64字节
Vec2 position_ = Vec2::Zero(); mutable glm::mat4 localTransform_; // 64 bytes
float rotation_ = 0.0f; mutable glm::mat4 worldTransform_; // 64 bytes
Vec2 scale_ = Vec2(1.0f, 1.0f);
Vec2 anchor_ = Vec2(0.5f, 0.5f);
Vec2 skew_ = Vec2::Zero();
float opacity_ = 1.0f;
bool visible_ = true;
int zOrder_ = 0;
// 缓存 // 2. 字符串和容器24-32字节
mutable bool transformDirty_ = true; std::string name_; // 32 bytes
mutable bool worldTransformDirty_ = true; // 世界变换独立的脏标记 std::vector<Ptr<Node>> children_; // 24 bytes
mutable glm::mat4 localTransform_;
mutable glm::mat4 worldTransform_;
// 元数据 // 3. 子节点索引(加速查找)
std::string name_; std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
int tag_ = -1; std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
// 状态 // 4. 动作系统(使用 unordered_map 加速 tag 查找)
bool running_ = false; std::unordered_map<int, Ptr<Action>> actionByTag_; // 56 bytes
Scene *scene_ = nullptr; std::vector<Ptr<Action>> actions_; // 24 bytes无 tag 的 Action
bool spatialIndexed_ = true; // 是否参与空间索引
Rect lastSpatialBounds_; // 上一次的空间索引边界(用于检测变化)
// 动作 // 5. 事件分发器
std::vector<Ptr<Action>> actions_; EventDispatcher eventDispatcher_; // 大小取决于实现
// 事件 // 6. 父节点引用
EventDispatcher eventDispatcher_; WeakPtr<Node> parent_; // 16 bytes
// 7. 变换属性(按访问频率分组)
Vec2 position_ = Vec2::Zero(); // 8 bytes
Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes
Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes
Vec2 skew_ = Vec2::Zero(); // 8 bytes
// 8. 边界框(用于空间索引)
Rect lastSpatialBounds_; // 16 bytes
// 9. 浮点属性
float rotation_ = 0.0f; // 4 bytes
float opacity_ = 1.0f; // 4 bytes
// 10. 整数属性
int zOrder_ = 0; // 4 bytes
int tag_ = -1; // 4 bytes
// 11. 场景指针
Scene *scene_ = nullptr; // 8 bytes
// 12. 布尔标志(打包在一起)
mutable bool transformDirty_ = true; // 1 byte
mutable bool worldTransformDirty_ = true; // 1 byte
bool childrenOrderDirty_ = false; // 1 byte
bool visible_ = true; // 1 byte
bool running_ = false; // 1 byte
bool spatialIndexed_ = true; // 1 byte
// 填充 2 bytes 到 8 字节对齐
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -25,6 +25,14 @@ void Node::addChild(Ptr<Node> child) {
children_.push_back(child); children_.push_back(child);
childrenOrderDirty_ = true; childrenOrderDirty_ = true;
// 更新索引
if (!child->getName().empty()) {
nameIndex_[child->getName()] = child;
}
if (child->getTag() != -1) {
tagIndex_[child->getTag()] = child;
}
if (running_) { if (running_) {
child->onEnter(); child->onEnter();
if (scene_) { if (scene_) {
@ -33,16 +41,63 @@ void Node::addChild(Ptr<Node> child) {
} }
} }
void Node::addChildren(std::vector<Ptr<Node>> &&children) {
// 预留空间,避免多次扩容
size_t newSize = children_.size() + children.size();
if (newSize > children_.capacity()) {
children_.reserve(newSize);
}
for (auto &child : children) {
if (!child || child.get() == this) {
continue;
}
child->removeFromParent();
child->parent_ = weak_from_this();
children_.push_back(child);
// 更新索引
if (!child->getName().empty()) {
nameIndex_[child->getName()] = child;
}
if (child->getTag() != -1) {
tagIndex_[child->getTag()] = child;
}
if (running_) {
child->onEnter();
if (scene_) {
child->onAttachToScene(scene_);
}
}
}
if (!children.empty()) {
childrenOrderDirty_ = true;
}
}
void Node::removeChild(Ptr<Node> child) { void Node::removeChild(Ptr<Node> child) {
if (!child) if (!child)
return; return;
auto it = std::find(children_.begin(), children_.end(), child); auto it = std::find(children_.begin(), children_.end(), child);
if (it != children_.end()) { if (it != children_.end()) {
if (running_) { // 始终从空间索引中移除(无论 running_ 状态)
// 这确保节点被正确清理
(*it)->onDetachFromScene(); (*it)->onDetachFromScene();
if (running_) {
(*it)->onExit(); (*it)->onExit();
} }
// 从索引中移除
if (!(*it)->getName().empty()) {
nameIndex_.erase((*it)->getName());
}
if ((*it)->getTag() != -1) {
tagIndex_.erase((*it)->getTag());
}
(*it)->parent_.reset(); (*it)->parent_.reset();
children_.erase(it); children_.erase(it);
} }
@ -80,22 +135,24 @@ void Node::removeAllChildren() {
child->parent_.reset(); child->parent_.reset();
} }
children_.clear(); children_.clear();
nameIndex_.clear();
tagIndex_.clear();
} }
Ptr<Node> Node::getChildByName(const std::string &name) const { Ptr<Node> Node::getChildByName(const std::string &name) const {
for (const auto &child : children_) { // 使用哈希索引O(1) 查找
if (child->getName() == name) { auto it = nameIndex_.find(name);
return child; if (it != nameIndex_.end()) {
} return it->second.lock();
} }
return nullptr; return nullptr;
} }
Ptr<Node> Node::getChildByTag(int tag) const { Ptr<Node> Node::getChildByTag(int tag) const {
for (const auto &child : children_) { // 使用哈希索引O(1) 查找
if (child->getTag() == tag) { auto it = tagIndex_.find(tag);
return child; if (it != tagIndex_.end()) {
} return it->second.lock();
} }
return nullptr; return nullptr;
} }
@ -197,12 +254,26 @@ glm::mat4 Node::getLocalTransform() const {
glm::mat4 Node::getWorldTransform() const { glm::mat4 Node::getWorldTransform() const {
if (worldTransformDirty_) { if (worldTransformDirty_) {
worldTransform_ = getLocalTransform(); // 迭代计算世界变换,避免深层级时的栈溢出
// 收集父节点链
auto p = parent_.lock(); std::vector<const Node *> nodeChain;
if (p) { const Node *current = this;
worldTransform_ = p->getWorldTransform() * worldTransform_; while (current) {
nodeChain.push_back(current);
auto p = current->parent_.lock();
current = p.get();
// 限制最大深度,防止异常循环
if (nodeChain.size() > 1000) {
break;
} }
}
// 从根节点开始计算
glm::mat4 transform = glm::mat4(1.0f);
for (auto it = nodeChain.rbegin(); it != nodeChain.rend(); ++it) {
transform = transform * (*it)->getLocalTransform();
}
worldTransform_ = transform;
worldTransformDirty_ = false; worldTransformDirty_ = false;
} }
return worldTransform_; return worldTransform_;
@ -313,33 +384,71 @@ void Node::updateSpatialIndex() {
} }
void Node::runAction(Ptr<Action> action) { void Node::runAction(Ptr<Action> action) {
if (action) { if (!action) {
action->start(this); return;
actions_.push_back(action);
} }
action->start(this);
int tag = action->getTag();
if (tag != -1) {
// 有 tag 的 Action 存入哈希表O(1) 查找
// 如果已存在相同 tag 的 Action先停止它
auto it = actionByTag_.find(tag);
if (it != actionByTag_.end()) {
// 从 vector 中移除旧的 Action
auto oldAction = it->second;
auto vecIt = std::find(actions_.begin(), actions_.end(), oldAction);
if (vecIt != actions_.end()) {
actions_.erase(vecIt);
}
}
actionByTag_[tag] = action;
}
actions_.push_back(action);
} }
void Node::stopAllActions() { actions_.clear(); } void Node::stopAllActions() {
actions_.clear();
actionByTag_.clear();
}
void Node::stopAction(Ptr<Action> action) { void Node::stopAction(Ptr<Action> action) {
if (!action) {
return;
}
// 从 vector 中移除
auto it = std::find(actions_.begin(), actions_.end(), action); auto it = std::find(actions_.begin(), actions_.end(), action);
if (it != actions_.end()) { if (it != actions_.end()) {
// 如果有 tag从哈希表中也移除
int tag = action->getTag();
if (tag != -1) {
actionByTag_.erase(tag);
}
actions_.erase(it); actions_.erase(it);
} }
} }
void Node::stopActionByTag(int tag) { void Node::stopActionByTag(int tag) {
auto it = std::remove_if( auto it = actionByTag_.find(tag);
actions_.begin(), actions_.end(), if (it != actionByTag_.end()) {
[tag](const Ptr<Action> &action) { return action->getTag() == tag; }); auto action = it->second;
actions_.erase(it, actions_.end()); // 从 vector 中移除
auto vecIt = std::find(actions_.begin(), actions_.end(), action);
if (vecIt != actions_.end()) {
actions_.erase(vecIt);
}
actionByTag_.erase(it);
}
} }
Ptr<Action> Node::getActionByTag(int tag) const { Ptr<Action> Node::getActionByTag(int tag) const {
for (const auto &action : actions_) { // O(1) 哈希查找
if (action->getTag() == tag) { auto it = actionByTag_.find(tag);
return action; if (it != actionByTag_.end()) {
} return it->second;
} }
return nullptr; return nullptr;
} }
@ -389,13 +498,20 @@ void Node::sortChildren() {
void Node::collectRenderCommands(std::vector<RenderCommand> &commands, void Node::collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder) { int parentZOrder) {
// 暂时最小化实现以测试
if (!visible_) if (!visible_)
return; return;
// 不排序,不递归,只生成当前节点的命令 // 计算累积 Z 序
int accumulatedZOrder = parentZOrder + zOrder_; int accumulatedZOrder = parentZOrder + zOrder_;
// 生成当前节点的渲染命令
generateRenderCommand(commands, accumulatedZOrder); generateRenderCommand(commands, accumulatedZOrder);
// 递归收集子节点的渲染命令
// 注意:这里假设子节点已经按 Z 序排序
for (auto &child : children_) {
child->collectRenderCommands(commands, accumulatedZOrder);
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -86,9 +86,17 @@ void SpatialHash::insert(Node *node, const Rect &bounds) {
if (!node) if (!node)
return; return;
insertIntoCells(node, bounds); // 检查节点是否已存在,如果存在则先移除
auto it = objectBounds_.find(node);
if (it != objectBounds_.end()) {
removeFromCells(node, it->second);
it->second = bounds;
} else {
objectBounds_[node] = bounds; objectBounds_[node] = bounds;
objectCount_++; objectCount_++;
}
insertIntoCells(node, bounds);
} }
void SpatialHash::remove(Node *node) { void SpatialHash::remove(Node *node) {
@ -100,6 +108,12 @@ void SpatialHash::remove(Node *node) {
removeFromCells(node, it->second); removeFromCells(node, it->second);
objectBounds_.erase(it); objectBounds_.erase(it);
objectCount_--; objectCount_--;
} else {
// 节点不在 objectBounds_ 中,但可能还在 grid_ 的某些单元格中
// 需要遍历所有单元格来移除
for (auto &[cellKey, cell] : grid_) {
cell.remove(node);
}
} }
} }