1
0
mirror of https://github.com/godotengine/godot.git synced 2025-12-03 16:55:53 +00:00

Merge pull request #62444 from lawnjelly/faster_queue_free3

[3.x] Faster queue free
This commit is contained in:
Rémi Verschelde
2023-01-10 10:55:36 +01:00
committed by GitHub
3 changed files with 67 additions and 5 deletions

View File

@@ -1251,20 +1251,77 @@ void SceneTree::get_nodes_in_group(const StringName &p_group, List<Node *> *p_li
void SceneTree::_flush_delete_queue() {
_THREAD_SAFE_METHOD_
while (delete_queue.size()) {
Object *obj = ObjectDB::get_instance(delete_queue.front()->get());
// Sorting the delete queue by child count (in respect to their parent)
// is an optimization because nodes benefit immensely from being deleted
// in reverse order to their child count. This is partly due to ordered_remove(), and partly
// due to notifications being sent to children that are moved, further in the child list.
struct ObjectIDComparator {
_FORCE_INLINE_ bool operator()(const DeleteQueueElement &p, const DeleteQueueElement &q) const {
return (p.child_list_id > q.child_list_id);
}
};
delete_queue.sort_custom<ObjectIDComparator>();
for (uint32_t e = 0; e < delete_queue.size(); e++) {
ObjectID id = delete_queue[e].id;
Object *obj = ObjectDB::get_instance(id);
if (obj) {
memdelete(obj);
}
delete_queue.pop_front();
}
delete_queue.clear();
}
void SceneTree::queue_delete(Object *p_object) {
_THREAD_SAFE_METHOD_
ERR_FAIL_NULL(p_object);
// Guard against the user queueing multiple times,
// which is unnecessary.
if (p_object->is_queued_for_deletion()) {
return;
}
p_object->_is_queued_for_deletion = true;
delete_queue.push_back(p_object->get_instance_id());
DeleteQueueElement e;
e.id = p_object->get_instance_id();
// Storing the list id within the parent allows us
// to sort the delete queue in reverse for more efficient
// deletion.
// Note that data.pos could alternatively be read during flush_delete_queue(),
// however reading it here avoids an extra lookup, and should be correct in most cases.
// And worst case if the child_list_id changes in the meantime, it will still work, it may just
// be slightly slower.
const Node *node = Object::cast_to<Node>(p_object);
if (node) {
e.child_list_id = node->data.pos;
// Have some grouping by parent object ID,
// so that children tend to be deleted together.
// This should be more cache friendly.
if (node->data.parent) {
ObjectID parent_id = node->data.parent->get_instance_id();
// Use a prime number to combine the group with the child id.
// Provided there are less than the prime number children in a node,
// there will be no collisions. Even if there are collisions, it is no problem.
uint32_t group = parent_id * 937;
// Rollover the group, we never want the group + the child id
// to overflow 31 bits
group &= ~(0b111 << 29);
e.child_list_id += (int32_t)group;
}
} else {
// For non-nodes, there is no point in sorting them.
e.child_list_id = -2;
}
delete_queue.push_back(e);
}
int SceneTree::get_node_count() const {