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);
/**
* @brief
* @param children
*/
void addChildren(std::vector<Ptr<Node>> &&children);
void removeChild(Ptr<Node> child);
void removeChildByName(const std::string &name);
void removeFromParent();
@ -161,42 +168,62 @@ protected:
float getOpacityRef() { return opacity_; }
private:
// 层级
WeakPtr<Node> parent_;
std::vector<Ptr<Node>> children_;
bool childrenOrderDirty_ = false;
// ==========================================================================
// 成员变量按类型大小降序排列,减少内存对齐填充
// 64位系统对齐std::string(32) > glm::mat4(64) > std::vector(24) >
// double(8) > float(4) > int(4) > bool(1)
// ==========================================================================
// 变换
Vec2 position_ = Vec2::Zero();
float rotation_ = 0.0f;
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;
// 1. 大块内存64字节
mutable glm::mat4 localTransform_; // 64 bytes
mutable glm::mat4 worldTransform_; // 64 bytes
// 缓存
mutable bool transformDirty_ = true;
mutable bool worldTransformDirty_ = true; // 世界变换独立的脏标记
mutable glm::mat4 localTransform_;
mutable glm::mat4 worldTransform_;
// 2. 字符串和容器24-32字节
std::string name_; // 32 bytes
std::vector<Ptr<Node>> children_; // 24 bytes
// 元数据
std::string name_;
int tag_ = -1;
// 3. 子节点索引(加速查找)
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
// 状态
bool running_ = false;
Scene *scene_ = nullptr;
bool spatialIndexed_ = true; // 是否参与空间索引
Rect lastSpatialBounds_; // 上一次的空间索引边界(用于检测变化)
// 4. 动作系统(使用 unordered_map 加速 tag 查找)
std::unordered_map<int, Ptr<Action>> actionByTag_; // 56 bytes
std::vector<Ptr<Action>> actions_; // 24 bytes无 tag 的 Action
// 动作
std::vector<Ptr<Action>> actions_;
// 5. 事件分发器
EventDispatcher eventDispatcher_; // 大小取决于实现
// 事件
EventDispatcher eventDispatcher_;
// 6. 父节点引用
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

View File

@ -25,6 +25,14 @@ void Node::addChild(Ptr<Node> child) {
children_.push_back(child);
childrenOrderDirty_ = true;
// 更新索引
if (!child->getName().empty()) {
nameIndex_[child->getName()] = child;
}
if (child->getTag() != -1) {
tagIndex_[child->getTag()] = child;
}
if (running_) {
child->onEnter();
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) {
if (!child)
return;
auto it = std::find(children_.begin(), children_.end(), child);
if (it != children_.end()) {
if (running_) {
// 始终从空间索引中移除(无论 running_ 状态)
// 这确保节点被正确清理
(*it)->onDetachFromScene();
if (running_) {
(*it)->onExit();
}
// 从索引中移除
if (!(*it)->getName().empty()) {
nameIndex_.erase((*it)->getName());
}
if ((*it)->getTag() != -1) {
tagIndex_.erase((*it)->getTag());
}
(*it)->parent_.reset();
children_.erase(it);
}
@ -80,22 +135,24 @@ void Node::removeAllChildren() {
child->parent_.reset();
}
children_.clear();
nameIndex_.clear();
tagIndex_.clear();
}
Ptr<Node> Node::getChildByName(const std::string &name) const {
for (const auto &child : children_) {
if (child->getName() == name) {
return child;
}
// 使用哈希索引O(1) 查找
auto it = nameIndex_.find(name);
if (it != nameIndex_.end()) {
return it->second.lock();
}
return nullptr;
}
Ptr<Node> Node::getChildByTag(int tag) const {
for (const auto &child : children_) {
if (child->getTag() == tag) {
return child;
}
// 使用哈希索引O(1) 查找
auto it = tagIndex_.find(tag);
if (it != tagIndex_.end()) {
return it->second.lock();
}
return nullptr;
}
@ -197,12 +254,26 @@ glm::mat4 Node::getLocalTransform() const {
glm::mat4 Node::getWorldTransform() const {
if (worldTransformDirty_) {
worldTransform_ = getLocalTransform();
auto p = parent_.lock();
if (p) {
worldTransform_ = p->getWorldTransform() * worldTransform_;
// 迭代计算世界变换,避免深层级时的栈溢出
// 收集父节点链
std::vector<const Node *> nodeChain;
const Node *current = this;
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;
}
return worldTransform_;
@ -313,33 +384,71 @@ void Node::updateSpatialIndex() {
}
void Node::runAction(Ptr<Action> action) {
if (action) {
if (!action) {
return;
}
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();
actionByTag_.clear();
}
void Node::stopAllActions() { actions_.clear(); }
void Node::stopAction(Ptr<Action> action) {
if (!action) {
return;
}
// 从 vector 中移除
auto it = std::find(actions_.begin(), actions_.end(), action);
if (it != actions_.end()) {
// 如果有 tag从哈希表中也移除
int tag = action->getTag();
if (tag != -1) {
actionByTag_.erase(tag);
}
actions_.erase(it);
}
}
void Node::stopActionByTag(int tag) {
auto it = std::remove_if(
actions_.begin(), actions_.end(),
[tag](const Ptr<Action> &action) { return action->getTag() == tag; });
actions_.erase(it, actions_.end());
auto it = actionByTag_.find(tag);
if (it != actionByTag_.end()) {
auto action = it->second;
// 从 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 {
for (const auto &action : actions_) {
if (action->getTag() == tag) {
return action;
}
// O(1) 哈希查找
auto it = actionByTag_.find(tag);
if (it != actionByTag_.end()) {
return it->second;
}
return nullptr;
}
@ -389,13 +498,20 @@ void Node::sortChildren() {
void Node::collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder) {
// 暂时最小化实现以测试
if (!visible_)
return;
// 不排序,不递归,只生成当前节点的命令
// 计算累积 Z 序
int accumulatedZOrder = parentZOrder + zOrder_;
// 生成当前节点的渲染命令
generateRenderCommand(commands, accumulatedZOrder);
// 递归收集子节点的渲染命令
// 注意:这里假设子节点已经按 Z 序排序
for (auto &child : children_) {
child->collectRenderCommands(commands, accumulatedZOrder);
}
}
} // namespace extra2d

View File

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