feat(node): 优化节点管理和空间索引处理
- 在 SpatialHash 中增加节点存在性检查,避免重复插入 - 为 Node 类添加批量添加子节点方法 addChildren - 使用哈希表优化子节点和动作的查找性能 - 重构成员变量布局以优化内存使用 - 改进世界变换计算方式,防止栈溢出 - 增强渲染命令收集功能,支持递归子节点
This commit is contained in:
parent
8b28f3c6df
commit
82c44b966f
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue