Ported parts of : Refactor Node Processing

* Node processing works on the concept of process groups.
* A node group can be inherited, run on main thread, or a sub-thread.
* Groups can be ordered.
* Process priority is now present for physics.
This is the first steps towards implementing godotengine/godot-proposals#6424.
No threading or thread guards exist yet in most of the scene code other than Node. That will have to be added later.
- reduz
98c655ec8d
- Only got the smaller improvements, and the thread safety for Node and SceneTree. I'm planning to implement a similar system, but I have a different way of doing it in mind.
This commit is contained in:
Relintai 2023-06-12 21:13:26 +02:00
parent de1763d40d
commit d4175f9676
11 changed files with 253 additions and 110 deletions

View File

@ -99,7 +99,7 @@ public:
}
void clear() {
if (elements == nullptr) {
if (elements == nullptr || num_elements == 0) {
return;
}
uint32_t capacity = hash_table_size_primes[capacity_index];
@ -517,7 +517,7 @@ private:
}
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
if (elements == nullptr) {
if (elements == nullptr || num_elements == 0) {
return false; // Failed lookups, no elements
}

View File

@ -79,7 +79,7 @@ private:
}
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
if (keys == nullptr) {
if (keys == nullptr || num_elements == 0) {
return false; // Failed lookups, no elements
}
@ -236,7 +236,7 @@ public:
}
void clear() {
if (keys == nullptr) {
if (keys == nullptr || num_elements == 0) {
return;
}
uint32_t capacity = hash_table_size_primes[capacity_index];

View File

@ -30,11 +30,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "core/error/error_macros.h"
#include "core/os/memory.h"
#include "core/containers/pool_vector.h"
#include "core/containers/sort_array.h"
#include "core/containers/vector.h"
#include "core/error/error_macros.h"
#include "core/os/memory.h"
template <class T, class U = uint32_t, bool force_trivial = false>
class LocalVector {
@ -94,11 +94,13 @@ public:
}
}
void erase(const T &p_val) {
_FORCE_INLINE_ bool erase(const T &p_val) {
int64_t idx = find(p_val);
if (idx >= 0) {
remove(idx);
return true;
}
return false;
}
U erase_multiple_unordered(const T &p_val) {
@ -280,7 +282,7 @@ public:
data[i] = r[i];
}
}
inline LocalVector &operator=(const LocalVector &p_from) {
resize(p_from.size());
for (U i = 0; i < p_from.count; i++) {

View File

@ -37,9 +37,9 @@
*/
#include "core/containers/cowdata.h"
#include "core/containers/sort_array.h"
#include "core/error/error_macros.h"
#include "core/os/memory.h"
#include "core/containers/sort_array.h"
template <class T>
class VectorWriteProxy {
@ -65,11 +65,13 @@ public:
bool push_back(T p_elem);
void remove(int p_index) { _cowdata.remove(p_index); }
void erase(const T &p_val) {
_FORCE_INLINE_ bool erase(const T &p_val) {
int idx = find(p_val);
if (idx >= 0) {
remove(idx);
return true;
}
return false;
};
void invert();

View File

@ -391,6 +391,10 @@ bool Object::_predelete() {
return _predelete_ok;
}
void Object::cancel_free() {
_predelete_ok = 0;
}
void Object::_postinitialize() {
_class_ptr = _get_class_namev();
_initialize_classv();
@ -957,10 +961,6 @@ void Object::property_list_changed_notify() {
_change_notify();
}
void Object::cancel_delete() {
_predelete_ok = true;
}
ObjectRC *Object::_use_rc() {
// The RC object is lazily created the first time it's requested;
// that way, there's no need to allocate and release it at all if this Object
@ -1734,6 +1734,7 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("tr", "message"), &Object::tr);
ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion);
ClassDB::bind_method(D_METHOD("cancel_free"), &Object::cancel_free);
ClassDB::add_virtual_method("Object", MethodInfo("free"), false);

View File

@ -33,12 +33,12 @@
#include "core/containers/hash_map.h"
#include "core/containers/list.h"
#include "core/containers/rb_map.h"
#include "core/containers/rb_set.h"
#include "core/containers/vmap.h"
#include "core/object/object_id.h"
#include "core/os/rw_lock.h"
#include "core/os/safe_refcount.h"
#include "core/containers/rb_set.h"
#include "core/variant/variant.h"
#include "core/containers/vmap.h"
#include <atomic>
@ -565,8 +565,6 @@ protected:
static void get_valid_parents_static(List<String> *p_parents);
static void _get_valid_parents_static(List<String> *p_parents);
void cancel_delete();
void property_list_changed_notify();
virtual void _changed_callback(Object *p_changed, const char *p_prop);
@ -809,6 +807,8 @@ public:
void clear_internal_resource_paths();
void cancel_free();
Object();
virtual ~Object();
};

View File

@ -96,6 +96,8 @@ public:
// get the ID of the main thread
_FORCE_INLINE_ static ID get_main_id() { return main_thread_id; }
_FORCE_INLINE_ static bool is_main_thread() { return get_caller_id() == main_thread_id; }
static Error set_name(const String &p_name);
void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings());
@ -110,6 +112,8 @@ public:
_FORCE_INLINE_ static ID get_caller_id() { return 0; }
// get the ID of the main thread
_FORCE_INLINE_ static ID get_main_id() { return 0; }
_FORCE_INLINE_ static bool is_main_thread() { return true; }
static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; }

View File

@ -100,6 +100,12 @@
Returns [code]true[/code] if the object can translate strings. See [method set_message_translation] and [method tr].
</description>
</method>
<method name="cancel_free">
<return type="void" />
<description>
If this method is called during [constant NOTIFICATION_PREDELETE], this object will reject being freed and will remain allocated. This is mostly an internal function used for error handling to avoid the user from freeing objects when they are not intended to.
</description>
</method>
<method name="connect">
<return type="int" enum="Error" />
<argument index="0" name="signal" type="StringName" />

View File

@ -179,6 +179,12 @@ void Node::_notification(int p_notification) {
data.in_constructor = false;
} break;
case NOTIFICATION_PREDELETE: {
if (data.inside_tree && !Thread::is_main_thread()) {
cancel_free();
ERR_PRINT("Attempted to free a node that is currently added to the SceneTree from a thread. This is not permitted, use queue_free() instead. Node has not been freed.");
return;
}
if (data.parent) {
data.parent->remove_child(this);
}
@ -420,6 +426,7 @@ void Node::_propagate_exit_tree() {
}
void Node::move_child(Node *p_child, int p_pos) {
ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Moving child node positions inside the SceneTree is only allowed from the main thread. Use call_deferred(\"move_child\",child,index).");
ERR_FAIL_NULL(p_child);
ERR_FAIL_INDEX_MSG(static_cast<uint32_t>(p_pos), data.children.size() + 1, vformat("Invalid new child position: %d.", p_pos));
ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node.");
@ -1270,6 +1277,8 @@ void Node::_set_name_nocheck(const StringName &p_name) {
}
void Node::set_name(const String &p_name) {
ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Changing the name to nodes inside the SceneTree is only allowed from the main thread. Use call_deferred(\"set_name\",new_name).");
String name = p_name.validate_node_name();
ERR_FAIL_COND(name == "");
@ -1504,6 +1513,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
}
void Node::add_child(Node *p_child, bool p_force_readable_name) {
ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Adding children to a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"add_child\",node).");
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself!
ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent
@ -1532,6 +1542,7 @@ void Node::add_child_below_node(Node *p_node, Node *p_child, bool p_force_readab
}
void Node::remove_child(Node *p_child) {
ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Removing children from a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"remove_child\",node).");
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, remove_node() failed. Consider using call_deferred(\"remove_child\", child) instead.");
@ -1881,6 +1892,8 @@ void Node::_acquire_unique_name_in_owner() {
}
void Node::set_unique_name_in_owner(bool p_enabled) {
ERR_FAIL_COND_MSG(is_inside_tree() && !Thread::is_main_thread(), "This function in this node can only be accessed from the main thread. Use call_deferred() instead.");
if (data.unique_name_in_owner == p_enabled) {
return;
}
@ -1902,6 +1915,8 @@ bool Node::is_unique_name_in_owner() const {
}
void Node::set_owner(Node *p_owner) {
ERR_FAIL_COND_MSG(is_inside_tree() && !Thread::is_main_thread(), "This function in this node can only be accessed from the main thread. Use call_deferred() instead.");
if (data.owner) {
if (data.unique_name_in_owner) {
_release_unique_name_in_owner();

View File

@ -467,6 +467,10 @@ public:
void set_process_unhandled_key_input(bool p_enable);
bool is_processing_unhandled_key_input() const;
_FORCE_INLINE_ bool _is_any_processing() const {
return data.idle_process || data.idle_process_internal || data.physics_process || data.physics_process_internal;
}
int get_position_in_parent() const;
Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const;

View File

@ -143,6 +143,8 @@ void SceneTree::node_added(Node *p_node) {
}
void SceneTree::node_removed(Node *p_node) {
// Nodes can only be removed from the main thread.
if (current_scene == p_node) {
current_scene = nullptr;
}
@ -157,6 +159,8 @@ void SceneTree::node_renamed(Node *p_node) {
}
SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_node) {
_THREAD_SAFE_METHOD_
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
E = group_map.insert(p_group, Group());
@ -170,6 +174,8 @@ SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_nod
}
void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) {
_THREAD_SAFE_METHOD_
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
ERR_FAIL_COND(!E);
@ -180,6 +186,8 @@ void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) {
}
void SceneTree::make_group_changed(const StringName &p_group) {
_THREAD_SAFE_METHOD_
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (E) {
E->get().changed = true;
@ -187,6 +195,8 @@ void SceneTree::make_group_changed(const StringName &p_group) {
}
void SceneTree::flush_transform_notifications() {
_THREAD_SAFE_METHOD_
SelfList<Node> *n = xform_change_list.first();
while (n) {
Node *node = n->self();
@ -239,47 +249,56 @@ void SceneTree::_update_group_order(Group &g, bool p_use_priority) {
}
void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, VARIANT_ARG_DECLARE) {
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
}
Vector<Node *> nodes_copy;
if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) {
ERR_FAIL_COND(ugc_locked);
{
_THREAD_SAFE_METHOD_
UGCall ug;
ug.call = p_function;
ug.group = p_group;
if (unique_group_calls.has(ug)) {
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
}
VARIANT_ARGPTRS;
if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) {
ERR_FAIL_COND(ugc_locked);
Vector<Variant> args;
for (int i = 0; i < VARIANT_ARG_MAX; i++) {
if (argptr[i]->get_type() == Variant::NIL) {
break;
UGCall ug;
ug.call = p_function;
ug.group = p_group;
if (unique_group_calls.has(ug)) {
return;
}
args.push_back(*argptr[i]);
VARIANT_ARGPTRS;
Vector<Variant> args;
for (int i = 0; i < VARIANT_ARG_MAX; i++) {
if (argptr[i]->get_type() == Variant::NIL) {
break;
}
args.push_back(*argptr[i]);
}
unique_group_calls[ug] = args;
return;
}
unique_group_calls[ug] = args;
return;
_update_group_order(g);
nodes_copy = g.nodes;
}
_update_group_order(g);
Vector<Node *> nodes_copy = g.nodes;
Node **nodes = nodes_copy.ptrw();
int node_count = nodes_copy.size();
_THREAD_SAFE_LOCK_
call_lock++;
_THREAD_SAFE_UNLOCK_
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = node_count - 1; i >= 0; i--) {
@ -316,29 +335,39 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou
}
}
_THREAD_SAFE_LOCK_
call_lock--;
if (call_lock == 0) {
call_skip.clear();
}
_THREAD_SAFE_UNLOCK_
}
void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification) {
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
Vector<Node *> nodes_copy;
{
_THREAD_SAFE_METHOD_
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
}
_update_group_order(g);
nodes_copy = g.nodes;
}
_update_group_order(g);
Vector<Node *> nodes_copy = g.nodes;
Node **nodes = nodes_copy.ptrw();
int node_count = nodes_copy.size();
_THREAD_SAFE_LOCK_
call_lock++;
_THREAD_SAFE_UNLOCK_
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = node_count - 1; i >= 0; i--) {
@ -367,29 +396,40 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
}
}
_THREAD_SAFE_LOCK_
call_lock--;
if (call_lock == 0) {
call_skip.clear();
}
_THREAD_SAFE_UNLOCK_
}
void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value) {
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
Vector<Node *> nodes_copy;
{
_THREAD_SAFE_METHOD_
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
}
_update_group_order(g);
nodes_copy = g.nodes;
}
_update_group_order(g);
Vector<Node *> nodes_copy = g.nodes;
Node **nodes = nodes_copy.ptrw();
int node_count = nodes_copy.size();
_THREAD_SAFE_LOCK_
call_lock++;
_THREAD_SAFE_UNLOCK_
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = node_count - 1; i >= 0; i--) {
@ -418,10 +458,12 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
}
}
_THREAD_SAFE_LOCK_
call_lock--;
if (call_lock == 0) {
call_skip.clear();
}
_THREAD_SAFE_UNLOCK_
}
void SceneTree::call_group(const StringName &p_group, const StringName &p_function, VARIANT_ARG_DECLARE) {
@ -531,6 +573,8 @@ void SceneTree::init() {
}
void SceneTree::set_physics_interpolation_enabled(bool p_enabled) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "set_physics_interpolation_enabled can only be set from the main thread.");
// disallow interpolation in editor
if (Engine::get_singleton()->is_editor_hint()) {
p_enabled = false;
@ -669,35 +713,38 @@ bool SceneTree::idle(float p_time) {
_flush_delete_queue();
//go through timers
{
_THREAD_SAFE_METHOD_
List<Ref<SceneTreeTimer>>::Element *L = timers.back(); //last element
List<Ref<SceneTreeTimer>>::Element *L = timers.back(); //last element
for (List<Ref<SceneTreeTimer>>::Element *E = timers.front(); E;) {
List<Ref<SceneTreeTimer>>::Element *N = E->next();
if (pause && !E->get()->is_pause_mode_process()) {
for (List<Ref<SceneTreeTimer>>::Element *E = timers.front(); E;) {
List<Ref<SceneTreeTimer>>::Element *N = E->next();
if (pause && !E->get()->is_pause_mode_process()) {
if (E == L) {
break; //break on last, so if new timers were added during list traversal, ignore them.
}
E = N;
continue;
}
float time_left = E->get()->get_time_left();
if (E->get()->is_ignore_time_scale()) {
time_left -= Engine::get_singleton()->get_idle_frame_step();
} else {
time_left -= p_time;
}
E->get()->set_time_left(time_left);
if (time_left < 0) {
E->get()->emit_signal("timeout");
timers.erase(E);
}
if (E == L) {
break; //break on last, so if new timers were added during list traversal, ignore them.
}
E = N;
continue;
}
float time_left = E->get()->get_time_left();
if (E->get()->is_ignore_time_scale()) {
time_left -= Engine::get_singleton()->get_idle_frame_step();
} else {
time_left -= p_time;
}
E->get()->set_time_left(time_left);
if (time_left < 0) {
E->get()->emit_signal("timeout");
timers.erase(E);
}
if (E == L) {
break; //break on last, so if new timers were added during list traversal, ignore them.
}
E = N;
}
process_tweens(p_time, false);
@ -746,6 +793,8 @@ bool SceneTree::idle(float p_time) {
}
void SceneTree::process_tweens(float p_delta, bool p_physics) {
_THREAD_SAFE_METHOD_
// This methods works similarly to how SceneTreeTimers are handled.
List<Ref<SceneTreeTween>>::Element *L = tweens.back();
@ -805,6 +854,8 @@ void SceneTree::finish() {
}
void SceneTree::quit(int p_exit_code) {
_THREAD_SAFE_METHOD_
if (p_exit_code >= 0) {
// Override the exit code if a positive argument is given (the default is `-1`).
// This is a shorthand for calling `set_exit_code()` on the OS singleton then quitting.
@ -956,6 +1007,8 @@ Color SceneTree::get_debug_collision_contact_color() const {
}
Ref<Material> SceneTree::get_debug_collision_material() {
_THREAD_SAFE_METHOD_
if (collision_material.is_valid()) {
return collision_material;
}
@ -973,6 +1026,8 @@ Ref<Material> SceneTree::get_debug_collision_material() {
}
Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
_THREAD_SAFE_METHOD_
if (debug_contact_mesh.is_valid()) {
return debug_contact_mesh;
}
@ -1030,6 +1085,8 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
}
void SceneTree::set_pause(bool p_enabled) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Pause can only be set from the main thread.");
if (p_enabled == pause) {
return;
}
@ -1047,20 +1104,26 @@ bool SceneTree::is_paused() const {
}
void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input) {
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
}
Vector<Node *> nodes_copy;
_update_group_order(g);
{
_THREAD_SAFE_METHOD_
//copy, so copy on write happens in case something is removed from process while being called
//performance is not lost because only if something is added/removed the vector is copied.
Vector<Node *> nodes_copy = g.nodes;
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
}
_update_group_order(g);
//copy, so copy on write happens in case something is removed from process while being called
//performance is not lost because only if something is added/removed the vector is copied.
nodes_copy = g.nodes;
}
int node_count = nodes_copy.size();
Node **nodes = nodes_copy.ptrw();
@ -1068,7 +1131,9 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p
Variant arg = p_input;
const Variant *v[1] = { &arg };
_THREAD_SAFE_LOCK_
call_lock++;
_THREAD_SAFE_UNLOCK_
for (int i = node_count - 1; i >= 0; i--) {
if (input_handled) {
@ -1088,32 +1153,41 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p
//ERR_FAIL_COND(node_count != g.nodes.size());
}
_THREAD_SAFE_LOCK_
call_lock--;
if (call_lock == 0) {
call_skip.clear();
}
_THREAD_SAFE_UNLOCK_
}
void SceneTree::_notify_group_pause(const StringName &p_group, int p_notification) {
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
}
Vector<Node *> nodes_copy;
_update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
{
_THREAD_SAFE_METHOD_
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
}
Group &g = E->get();
if (g.nodes.empty()) {
return;
}
//copy, so copy on write happens in case something is removed from process while being called
//performance is not lost because only if something is added/removed the vector is copied.
Vector<Node *> nodes_copy = g.nodes;
_update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
//copy, so copy on write happens in case something is removed from process while being called
//performance is not lost because only if something is added/removed the vector is copied.
nodes_copy = g.nodes;
}
int node_count = nodes_copy.size();
Node **nodes = nodes_copy.ptrw();
_THREAD_SAFE_LOCK_
call_lock++;
_THREAD_SAFE_UNLOCK_
for (int i = 0; i < node_count; i++) {
Node *n = nodes[i];
@ -1132,10 +1206,12 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio
//ERR_FAIL_COND(node_count != g.nodes.size());
}
_THREAD_SAFE_LOCK_
call_lock--;
if (call_lock == 0) {
call_skip.clear();
}
_THREAD_SAFE_UNLOCK_
}
/*
@ -1198,6 +1274,8 @@ int64_t SceneTree::get_event_count() const {
}
Array SceneTree::_get_nodes_in_group(const StringName &p_group) {
_THREAD_SAFE_METHOD_
Array ret;
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
@ -1221,9 +1299,13 @@ Array SceneTree::_get_nodes_in_group(const StringName &p_group) {
}
bool SceneTree::has_group(const StringName &p_identifier) const {
_THREAD_SAFE_METHOD_
return group_map.has(p_identifier);
}
void SceneTree::get_nodes_in_group(const StringName &p_group, List<Node *> *p_list) {
_THREAD_SAFE_METHOD_
RBMap<StringName, Group>::Element *E = group_map.find(p_group);
if (!E) {
return;
@ -1442,6 +1524,7 @@ Node *SceneTree::get_edited_scene_root() const {
}
void SceneTree::set_current_scene(Node *p_scene) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Changing scene can only be done from the main thread.");
ERR_FAIL_COND(p_scene && p_scene->get_parent() != root);
current_scene = p_scene;
}
@ -1451,6 +1534,8 @@ Node *SceneTree::get_current_scene() const {
}
void SceneTree::_change_scene(Node *p_to) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Changing scene can only be done from the main thread.");
if (current_scene) {
memdelete(current_scene);
current_scene = nullptr;
@ -1471,6 +1556,8 @@ void SceneTree::_change_scene(Node *p_to) {
}
Error SceneTree::change_scene(const String &p_path) {
ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), ERR_INVALID_PARAMETER, "Changing scene can only be done from the main thread.");
Ref<PackedScene> new_scene = ResourceLoader::load(p_path);
if (new_scene.is_null()) {
return ERR_CANT_OPEN;
@ -1491,12 +1578,14 @@ Error SceneTree::change_scene_to(const Ref<PackedScene> &p_scene) {
}
Error SceneTree::reload_current_scene() {
ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), ERR_INVALID_PARAMETER, "Reloading scene can only be done from the main thread.");
ERR_FAIL_COND_V(!current_scene, ERR_UNCONFIGURED);
String fname = current_scene->get_filename();
return change_scene(fname);
}
void SceneTree::add_current_scene(Node *p_current) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Adding a current scene can only be done from the main thread.");
current_scene = p_current;
root->add_child(p_current);
}
@ -1910,6 +1999,8 @@ void SceneTree::global_menu_action(const Variant &p_id, const Variant &p_meta) {
}
Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pause) {
_THREAD_SAFE_METHOD_
Ref<SceneTreeTimer> stt;
stt.instance();
stt->set_pause_mode_process(p_process_pause);
@ -1919,12 +2010,16 @@ Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pa
}
Ref<SceneTreeTween> SceneTree::create_tween() {
_THREAD_SAFE_METHOD_
Ref<SceneTreeTween> tween = memnew(SceneTreeTween(true));
tweens.push_back(tween);
return tween;
}
Array SceneTree::get_processed_tweens() {
_THREAD_SAFE_METHOD_
Array ret;
ret.resize(tweens.size());
@ -1956,10 +2051,14 @@ void SceneTree::_server_disconnected() {
}
Ref<MultiplayerAPI> SceneTree::get_multiplayer() const {
ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), Ref<MultiplayerAPI>(), "Multiplayer can only be manipulated from the main thread.");
return multiplayer;
}
void SceneTree::set_multiplayer_poll_enabled(bool p_enabled) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread.");
multiplayer_poll = p_enabled;
}
@ -1968,6 +2067,8 @@ bool SceneTree::is_multiplayer_poll_enabled() const {
}
void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread.");
ERR_FAIL_COND(!p_multiplayer.is_valid());
if (multiplayer.is_valid()) {
@ -1989,10 +2090,14 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
}
void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread.");
multiplayer->set_network_peer(p_network_peer);
}
Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const {
ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), Ref<NetworkedMultiplayerPeer>(), "Multiplayer can only be manipulated from the main thread.");
return multiplayer->get_network_peer();
}
@ -2017,6 +2122,8 @@ int SceneTree::get_rpc_sender_id() const {
}
void SceneTree::set_refuse_new_network_connections(bool p_refuse) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread.");
multiplayer->set_refuse_new_network_connections(p_refuse);
}
@ -2187,6 +2294,8 @@ void SceneTree::add_idle_callback(IdleCallback p_callback) {
}
void SceneTree::set_use_font_oversampling(bool p_oversampling) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Font Oversampling can only be set from the main thread.");
if (use_font_oversampling == p_oversampling) {
return;
}