You've already forked godot
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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user