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