From 0a9f72d5a80c8957ef5172f546c9076089862cef Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Fri, 12 May 2023 13:53:15 +0200 Subject: [PATCH] Make more base nodes thread safe Ongoing work to make more of the base nodes thread safe. --- core/core_bind.cpp | 60 +++++----- core/core_bind.h | 30 ++--- core/object/object.h | 41 ++++--- core/templates/safe_refcount.h | 11 ++ editor/editor_resource_preview.cpp | 11 +- editor/editor_resource_preview.h | 4 + scene/2d/node_2d.cpp | 61 +++++++++-- scene/2d/node_2d.h | 3 +- scene/3d/node_3d.cpp | 169 ++++++++++++++++++++++------- scene/3d/node_3d.h | 5 +- scene/gui/control.cpp | 157 +++++++++++++++++++++++++++ scene/main/canvas_item.cpp | 134 +++++++++++++++++++++-- scene/main/canvas_item.h | 4 +- scene/main/node.cpp | 90 +++++++++++++++ scene/main/node.h | 36 ++++++ scene/main/viewport.cpp | 148 +++++++++++++++++++++++++ scene/main/window.cpp | 134 +++++++++++++++++++++++ 17 files changed, 965 insertions(+), 133 deletions(-) diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 8fa7aad0acf..a9c0c4687b6 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1301,11 +1301,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const { } } -bool ClassDB::has_signal(StringName p_class, StringName p_signal) const { +bool ClassDB::class_has_signal(StringName p_class, StringName p_signal) const { return ::ClassDB::has_signal(p_class, p_signal); } -Dictionary ClassDB::get_signal(StringName p_class, StringName p_signal) const { +Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) const { MethodInfo signal; if (::ClassDB::get_signal(p_class, p_signal, &signal)) { return signal.operator Dictionary(); @@ -1314,7 +1314,7 @@ Dictionary ClassDB::get_signal(StringName p_class, StringName p_signal) const { } } -TypedArray ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const { +TypedArray ClassDB::class_get_signal_list(StringName p_class, bool p_no_inheritance) const { List signals; ::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance); TypedArray ret; @@ -1326,7 +1326,7 @@ TypedArray ClassDB::get_signal_list(StringName p_class, bool p_no_in return ret; } -TypedArray ClassDB::get_property_list(StringName p_class, bool p_no_inheritance) const { +TypedArray ClassDB::class_get_property_list(StringName p_class, bool p_no_inheritance) const { List plist; ::ClassDB::get_property_list(p_class, &plist, p_no_inheritance); TypedArray ret; @@ -1337,13 +1337,13 @@ TypedArray ClassDB::get_property_list(StringName p_class, bool p_no_ return ret; } -Variant ClassDB::get_property(Object *p_object, const StringName &p_property) const { +Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const { Variant ret; ::ClassDB::get_property(p_object, p_property, ret); return ret; } -Error ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const { +Error ClassDB::class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const { Variant ret; bool valid; if (!::ClassDB::set_property(p_object, p_property, p_value, &valid)) { @@ -1354,11 +1354,11 @@ Error ClassDB::set_property(Object *p_object, const StringName &p_property, cons return OK; } -bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const { +bool ClassDB::class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const { return ::ClassDB::has_method(p_class, p_method, p_no_inheritance); } -TypedArray ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const { +TypedArray ClassDB::class_get_method_list(StringName p_class, bool p_no_inheritance) const { List methods; ::ClassDB::get_method_list(p_class, &methods, p_no_inheritance); TypedArray ret; @@ -1376,7 +1376,7 @@ TypedArray ClassDB::get_method_list(StringName p_class, bool p_no_in return ret; } -PackedStringArray ClassDB::get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const { +PackedStringArray ClassDB::class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const { List constants; ::ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance); @@ -1390,24 +1390,24 @@ PackedStringArray ClassDB::get_integer_constant_list(const StringName &p_class, return ret; } -bool ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name) const { +bool ClassDB::class_has_integer_constant(const StringName &p_class, const StringName &p_name) const { bool success; ::ClassDB::get_integer_constant(p_class, p_name, &success); return success; } -int64_t ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name) const { +int64_t ClassDB::class_get_integer_constant(const StringName &p_class, const StringName &p_name) const { bool found; int64_t c = ::ClassDB::get_integer_constant(p_class, p_name, &found); ERR_FAIL_COND_V(!found, 0); return c; } -bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { +bool ClassDB::class_has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { return ::ClassDB::has_enum(p_class, p_name, p_no_inheritance); } -PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_inheritance) const { +PackedStringArray ClassDB::class_get_enum_list(const StringName &p_class, bool p_no_inheritance) const { List enums; ::ClassDB::get_enum_list(p_class, &enums, p_no_inheritance); @@ -1421,7 +1421,7 @@ PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_in return ret; } -PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const { +PackedStringArray ClassDB::class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const { List constants; ::ClassDB::get_enum_constants(p_class, p_enum, &constants, p_no_inheritance); @@ -1435,7 +1435,7 @@ PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const S return ret; } -StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { +StringName ClassDB::class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance); } @@ -1452,27 +1452,27 @@ void ClassDB::_bind_methods() { ::ClassDB::bind_method(D_METHOD("can_instantiate", "class"), &ClassDB::can_instantiate); ::ClassDB::bind_method(D_METHOD("instantiate", "class"), &ClassDB::instantiate); - ::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::has_signal); - ::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::get_signal); - ::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::get_signal_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::class_has_signal); + ::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::class_get_signal); + ::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::get_property_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::get_property); - ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::set_property); + ::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property); + ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property); - ::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::has_method, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::class_has_method, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::get_method_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::get_integer_constant_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::has_integer_constant); - ::ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &ClassDB::get_integer_constant); + ::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::class_has_integer_constant); + ::ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &ClassDB::class_get_integer_constant); - ::ClassDB::bind_method(D_METHOD("class_has_enum", "class", "name", "no_inheritance"), &ClassDB::has_enum, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::get_enum_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::get_enum_constants, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::get_integer_constant_enum, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_has_enum", "class", "name", "no_inheritance"), &ClassDB::class_has_enum, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::class_get_enum_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::class_get_enum_constants, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::class_get_integer_constant_enum, DEFVAL(false)); ::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled); } diff --git a/core/core_bind.h b/core/core_bind.h index be43ae2c9d2..6083cf2e2fd 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -424,26 +424,26 @@ public: bool can_instantiate(const StringName &p_class) const; Variant instantiate(const StringName &p_class) const; - bool has_signal(StringName p_class, StringName p_signal) const; - Dictionary get_signal(StringName p_class, StringName p_signal) const; - TypedArray get_signal_list(StringName p_class, bool p_no_inheritance = false) const; + bool class_has_signal(StringName p_class, StringName p_signal) const; + Dictionary class_get_signal(StringName p_class, StringName p_signal) const; + TypedArray class_get_signal_list(StringName p_class, bool p_no_inheritance = false) const; - TypedArray get_property_list(StringName p_class, bool p_no_inheritance = false) const; - Variant get_property(Object *p_object, const StringName &p_property) const; - Error set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; + TypedArray class_get_property_list(StringName p_class, bool p_no_inheritance = false) const; + Variant class_get_property(Object *p_object, const StringName &p_property) const; + Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; - bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const; + bool class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const; - TypedArray get_method_list(StringName p_class, bool p_no_inheritance = false) const; + TypedArray class_get_method_list(StringName p_class, bool p_no_inheritance = false) const; - PackedStringArray get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const; - bool has_integer_constant(const StringName &p_class, const StringName &p_name) const; - int64_t get_integer_constant(const StringName &p_class, const StringName &p_name) const; + PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const; + bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const; + int64_t class_get_integer_constant(const StringName &p_class, const StringName &p_name) const; - bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; - PackedStringArray get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const; - PackedStringArray get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; - StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; + bool class_has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; + PackedStringArray class_get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const; + PackedStringArray class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; + StringName class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; bool is_class_enabled(StringName p_class) const; diff --git a/core/object/object.h b/core/object/object.h index c633208d7cd..e61ddc8113d 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -836,14 +836,21 @@ public: /* SCRIPT */ - void set_script(const Variant &p_script); - Variant get_script() const; +// When in debug, some non-virtual functions can be overridden for multithreaded guards. +#ifdef DEBUG_ENABLED +#define MTVIRTUAL virtual +#else +#define MTVIRTUAL +#endif - bool has_meta(const StringName &p_name) const; - void set_meta(const StringName &p_name, const Variant &p_value); - void remove_meta(const StringName &p_name); - Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const; - void get_meta_list(List *p_list) const; + MTVIRTUAL void set_script(const Variant &p_script); + MTVIRTUAL Variant get_script() const; + + MTVIRTUAL bool has_meta(const StringName &p_name) const; + MTVIRTUAL void set_meta(const StringName &p_name, const Variant &p_value); + MTVIRTUAL void remove_meta(const StringName &p_name); + MTVIRTUAL Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const; + MTVIRTUAL void get_meta_list(List *p_list) const; #ifdef TOOLS_ENABLED void set_edited(bool p_edited); @@ -870,17 +877,17 @@ public: return emit_signalp(p_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } - Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount); - bool has_signal(const StringName &p_name) const; - void get_signal_list(List *p_signals) const; - void get_signal_connection_list(const StringName &p_signal, List *p_connections) const; - void get_all_signal_connections(List *p_connections) const; - int get_persistent_signal_connection_count() const; - void get_signals_connected_to_this(List *p_connections) const; + MTVIRTUAL Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount); + MTVIRTUAL bool has_signal(const StringName &p_name) const; + MTVIRTUAL void get_signal_list(List *p_signals) const; + MTVIRTUAL void get_signal_connection_list(const StringName &p_signal, List *p_connections) const; + MTVIRTUAL void get_all_signal_connections(List *p_connections) const; + MTVIRTUAL int get_persistent_signal_connection_count() const; + MTVIRTUAL void get_signals_connected_to_this(List *p_connections) const; - Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0); - void disconnect(const StringName &p_signal, const Callable &p_callable); - bool is_connected(const StringName &p_signal, const Callable &p_callable) const; + MTVIRTUAL Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0); + MTVIRTUAL void disconnect(const StringName &p_signal, const Callable &p_callable); + MTVIRTUAL bool is_connected(const StringName &p_signal, const Callable &p_callable) const; template void call_deferred(const StringName &p_name, VarArgs... p_args) { diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h index 58ed0192872..8669bcaeeb3 100644 --- a/core/templates/safe_refcount.h +++ b/core/templates/safe_refcount.h @@ -102,6 +102,17 @@ public: return value.fetch_sub(p_value, std::memory_order_acq_rel) - p_value; } + _ALWAYS_INLINE_ T bit_or(T p_value) { + return value.fetch_or(p_value, std::memory_order_acq_rel); + } + _ALWAYS_INLINE_ T bit_and(T p_value) { + return value.fetch_and(p_value, std::memory_order_acq_rel); + } + + _ALWAYS_INLINE_ T bit_xor(T p_value) { + return value.fetch_xor(p_value, std::memory_order_acq_rel); + } + // Returns the original value instead of the new one _ALWAYS_INLINE_ T postsub(T p_value) { return value.fetch_sub(p_value, std::memory_order_acq_rel); diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 7dea1a7e016..1db37005287 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -158,8 +158,6 @@ void EditorResourcePreview::_generate_preview(Ref &r_texture, Ref< } r_texture = generated; - int small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"))->get_width(); // Kind of a workaround to retrieve the default icon size - if (preview_generators[i]->can_generate_small_preview()) { Ref generated_small; Dictionary d; @@ -340,9 +338,16 @@ void EditorResourcePreview::_thread() { exited.set(); } +void EditorResourcePreview::_update_thumbnail_sizes() { + if (small_thumbnail_size == -1) { + small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"))->get_width(); // Kind of a workaround to retrieve the default icon size + } +} + void EditorResourcePreview::queue_edited_resource_preview(const Ref &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) { ERR_FAIL_NULL(p_receiver); ERR_FAIL_COND(!p_res.is_valid()); + _update_thumbnail_sizes(); { MutexLock lock(preview_mutex); @@ -370,6 +375,8 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref &p } void EditorResourcePreview::queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) { + _update_thumbnail_sizes(); + ERR_FAIL_NULL(p_receiver); { MutexLock lock(preview_mutex); diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index c97e153c0a8..f87bbf22bd6 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -97,6 +97,8 @@ class EditorResourcePreview : public Node { void _preview_ready(const String &p_path, int p_hash, const Ref &p_texture, const Ref &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata); void _generate_preview(Ref &r_texture, Ref &r_small_texture, const QueueItem &p_item, const String &cache_base, Dictionary &p_metadata); + int small_thumbnail_size = -1; + static void _thread_func(void *ud); void _thread(); void _iterate(); @@ -106,6 +108,8 @@ class EditorResourcePreview : public Node { Vector> preview_generators; + void _update_thumbnail_sizes(); + protected: static void _bind_methods(); diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 43a2f62163b..24478fd847d 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -113,11 +113,11 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) { #endif void Node2D::_update_xform_values() { - position = transform.columns[2]; rotation = transform.get_rotation(); - scale = transform.get_scale(); skew = transform.get_skew(); - _xform_dirty = false; + position = transform.columns[2]; + scale = transform.get_scale(); + xform_dirty.clear(); } void Node2D::_update_transform() { @@ -134,6 +134,7 @@ void Node2D::_update_transform() { } void Node2D::reparent(Node *p_parent, bool p_keep_global_transform) { + ERR_THREAD_GUARD; Transform2D temp = get_global_transform(); Node::reparent(p_parent); if (p_keep_global_transform) { @@ -142,7 +143,8 @@ void Node2D::reparent(Node *p_parent, bool p_keep_global_transform) { } void Node2D::set_position(const Point2 &p_pos) { - if (_xform_dirty) { + ERR_THREAD_GUARD; + if (xform_dirty.is_set()) { const_cast(this)->_update_xform_values(); } position = p_pos; @@ -150,7 +152,8 @@ void Node2D::set_position(const Point2 &p_pos) { } void Node2D::set_rotation(real_t p_radians) { - if (_xform_dirty) { + ERR_THREAD_GUARD; + if (xform_dirty.is_set()) { const_cast(this)->_update_xform_values(); } rotation = p_radians; @@ -158,11 +161,13 @@ void Node2D::set_rotation(real_t p_radians) { } void Node2D::set_rotation_degrees(real_t p_degrees) { + ERR_THREAD_GUARD; set_rotation(Math::deg_to_rad(p_degrees)); } void Node2D::set_skew(real_t p_radians) { - if (_xform_dirty) { + ERR_THREAD_GUARD; + if (xform_dirty.is_set()) { const_cast(this)->_update_xform_values(); } skew = p_radians; @@ -170,7 +175,8 @@ void Node2D::set_skew(real_t p_radians) { } void Node2D::set_scale(const Size2 &p_scale) { - if (_xform_dirty) { + ERR_THREAD_GUARD; + if (xform_dirty.is_set()) { const_cast(this)->_update_xform_values(); } scale = p_scale; @@ -185,14 +191,17 @@ void Node2D::set_scale(const Size2 &p_scale) { } Point2 Node2D::get_position() const { - if (_xform_dirty) { + ERR_READ_THREAD_GUARD_V(Point2()); + if (xform_dirty.is_set()) { const_cast(this)->_update_xform_values(); } + return position; } real_t Node2D::get_rotation() const { - if (_xform_dirty) { + ERR_READ_THREAD_GUARD_V(0); + if (xform_dirty.is_set()) { const_cast(this)->_update_xform_values(); } @@ -200,11 +209,13 @@ real_t Node2D::get_rotation() const { } real_t Node2D::get_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(0); return Math::rad_to_deg(get_rotation()); } real_t Node2D::get_skew() const { - if (_xform_dirty) { + ERR_READ_THREAD_GUARD_V(0); + if (xform_dirty.is_set()) { const_cast(this)->_update_xform_values(); } @@ -212,7 +223,8 @@ real_t Node2D::get_skew() const { } Size2 Node2D::get_scale() const { - if (_xform_dirty) { + ERR_READ_THREAD_GUARD_V(Size2()); + if (xform_dirty.is_set()) { const_cast(this)->_update_xform_values(); } @@ -220,26 +232,32 @@ Size2 Node2D::get_scale() const { } Transform2D Node2D::get_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return transform; } void Node2D::rotate(real_t p_radians) { + ERR_THREAD_GUARD; set_rotation(get_rotation() + p_radians); } void Node2D::translate(const Vector2 &p_amount) { + ERR_THREAD_GUARD; set_position(get_position() + p_amount); } void Node2D::global_translate(const Vector2 &p_amount) { + ERR_THREAD_GUARD; set_global_position(get_global_position() + p_amount); } void Node2D::apply_scale(const Size2 &p_amount) { + ERR_THREAD_GUARD; set_scale(get_scale() * p_amount); } void Node2D::move_x(real_t p_delta, bool p_scaled) { + ERR_THREAD_GUARD; Transform2D t = get_transform(); Vector2 m = t[0]; if (!p_scaled) { @@ -249,6 +267,7 @@ void Node2D::move_x(real_t p_delta, bool p_scaled) { } void Node2D::move_y(real_t p_delta, bool p_scaled) { + ERR_THREAD_GUARD; Transform2D t = get_transform(); Vector2 m = t[1]; if (!p_scaled) { @@ -258,10 +277,12 @@ void Node2D::move_y(real_t p_delta, bool p_scaled) { } Point2 Node2D::get_global_position() const { + ERR_READ_THREAD_GUARD_V(Point2()); return get_global_transform().get_origin(); } void Node2D::set_global_position(const Point2 &p_pos) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { Transform2D inv = parent->get_global_transform().affine_inverse(); @@ -272,18 +293,22 @@ void Node2D::set_global_position(const Point2 &p_pos) { } real_t Node2D::get_global_rotation() const { + ERR_READ_THREAD_GUARD_V(0); return get_global_transform().get_rotation(); } real_t Node2D::get_global_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(0); return Math::rad_to_deg(get_global_rotation()); } real_t Node2D::get_global_skew() const { + ERR_READ_THREAD_GUARD_V(0); return get_global_transform().get_skew(); } void Node2D::set_global_rotation(const real_t p_radians) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { Transform2D parent_global_transform = parent->get_global_transform(); @@ -297,10 +322,12 @@ void Node2D::set_global_rotation(const real_t p_radians) { } void Node2D::set_global_rotation_degrees(const real_t p_degrees) { + ERR_THREAD_GUARD; set_global_rotation(Math::deg_to_rad(p_degrees)); } void Node2D::set_global_skew(const real_t p_radians) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { Transform2D parent_global_transform = parent->get_global_transform(); @@ -314,10 +341,12 @@ void Node2D::set_global_skew(const real_t p_radians) { } Size2 Node2D::get_global_scale() const { + ERR_READ_THREAD_GUARD_V(Size2()); return get_global_transform().get_scale(); } void Node2D::set_global_scale(const Size2 &p_scale) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { Transform2D parent_global_transform = parent->get_global_transform(); @@ -331,8 +360,9 @@ void Node2D::set_global_scale(const Size2 &p_scale) { } void Node2D::set_transform(const Transform2D &p_transform) { + ERR_THREAD_GUARD; transform = p_transform; - _xform_dirty = true; + xform_dirty.set(); RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform); @@ -344,6 +374,7 @@ void Node2D::set_transform(const Transform2D &p_transform) { } void Node2D::set_global_transform(const Transform2D &p_transform) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { set_transform(parent->get_global_transform().affine_inverse() * p_transform); @@ -353,6 +384,7 @@ void Node2D::set_global_transform(const Transform2D &p_transform) { } Transform2D Node2D::get_relative_transform_to_parent(const Node *p_parent) const { + ERR_READ_THREAD_GUARD_V(Transform2D()); if (p_parent == this) { return Transform2D(); } @@ -368,22 +400,27 @@ Transform2D Node2D::get_relative_transform_to_parent(const Node *p_parent) const } void Node2D::look_at(const Vector2 &p_pos) { + ERR_THREAD_GUARD; rotate(get_angle_to(p_pos)); } real_t Node2D::get_angle_to(const Vector2 &p_pos) const { + ERR_READ_THREAD_GUARD_V(0); return (to_local(p_pos) * get_scale()).angle(); } Point2 Node2D::to_local(Point2 p_global) const { + ERR_READ_THREAD_GUARD_V(Point2()); return get_global_transform().affine_inverse().xform(p_global); } Point2 Node2D::to_global(Point2 p_local) const { + ERR_READ_THREAD_GUARD_V(Point2()); return get_global_transform().xform(p_local); } void Node2D::_notification(int p_notification) { + ERR_THREAD_GUARD; switch (p_notification) { case NOTIFICATION_ENTER_TREE: { if (get_viewport()) { diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 119e23cd98f..6bdf5d6eb57 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -36,6 +36,7 @@ class Node2D : public CanvasItem { GDCLASS(Node2D, CanvasItem); + SafeFlag xform_dirty; Point2 position; real_t rotation = 0.0; Size2 scale = Vector2(1, 1); @@ -43,8 +44,6 @@ class Node2D : public CanvasItem { Transform2D transform; - bool _xform_dirty = false; - void _update_transform(); void _update_xform_values(); diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 66e8831d15a..446d9f6ee88 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -86,10 +86,8 @@ void Node3D::_notify_dirty() { void Node3D::_update_local_transform() const { // This function is called when the local transform (data.local_transform) is dirty and the right value is contained in the Euler rotation and scale. - data.local_transform.basis.set_euler_scale(data.euler_rotation, data.scale, data.euler_rotation_order); - - data.dirty &= ~DIRTY_LOCAL_TRANSFORM; + data.dirty.bit_and(~DIRTY_LOCAL_TRANSFORM); } void Node3D::_update_rotation_and_scale() const { @@ -97,8 +95,13 @@ void Node3D::_update_rotation_and_scale() const { data.scale = data.local_transform.basis.get_scale(); data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); + data.dirty.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE); +} - data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; +void Node3D::_propagate_transform_changed_deferred() { + if (is_inside_tree() && !xform_change.in_list()) { + get_tree()->xform_change_list.add(&xform_change); + } } void Node3D::_propagate_transform_changed(Node3D *p_origin) { @@ -106,8 +109,6 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) { return; } - data.children_lock++; - for (Node3D *&E : data.children) { if (E->data.top_level_active) { continue; //don't propagate to a top_level @@ -119,14 +120,19 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) { #else if (data.notify_transform && !data.ignore_notification && !xform_change.in_list()) { #endif - get_tree()->xform_change_list.add(&xform_change); + if (likely(is_accessible_from_caller_thread())) { + get_tree()->xform_change_list.add(&xform_change); + } else { + // This should very rarely happen, but if it does at least make sure the notification is received eventually. + MessageQueue::get_singleton()->push_callable(callable_mp(this, &Node3D::_propagate_transform_changed_deferred)); + } } - data.dirty |= DIRTY_GLOBAL_TRANSFORM; - - data.children_lock--; + data.dirty.bit_or(DIRTY_GLOBAL_TRANSFORM); } void Node3D::_notification(int p_what) { + ERR_THREAD_GUARD; + switch (p_what) { case NOTIFICATION_ENTER_TREE: { ERR_FAIL_COND(!get_tree()); @@ -145,12 +151,12 @@ void Node3D::_notification(int p_what) { if (data.top_level && !Engine::get_singleton()->is_editor_hint()) { if (data.parent) { data.local_transform = data.parent->get_global_transform() * get_transform(); - data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // As local transform was updated, rot/scale should be dirty. + data.dirty.set(DIRTY_EULER_ROTATION_AND_SCALE); // As local transform was updated, rot/scale should be dirty. } data.top_level_active = true; } - data.dirty |= DIRTY_GLOBAL_TRANSFORM; // Global is always dirty upon entering a scene. + data.dirty.bit_or(DIRTY_GLOBAL_TRANSFORM); // Global is always dirty upon entering a scene. _notify_dirty(); notification(NOTIFICATION_ENTER_WORLD); @@ -217,19 +223,23 @@ void Node3D::_notification(int p_what) { } void Node3D::set_basis(const Basis &p_basis) { + ERR_THREAD_GUARD; + set_transform(Transform3D(p_basis, data.local_transform.origin)); } void Node3D::set_quaternion(const Quaternion &p_quaternion) { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_THREAD_GUARD; + + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { // We need the scale part, so if these are dirty, update it data.scale = data.local_transform.basis.get_scale(); - data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; + data.dirty.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE); } data.local_transform.basis = Basis(p_quaternion, data.scale); // Rotscale should not be marked dirty because that would cause precision loss issues with the scale. Instead reconstruct rotation now. data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); - data.dirty = DIRTY_NONE; + data.dirty.set(DIRTY_NONE); _propagate_transform_changed(this); if (data.notify_local_transform) { @@ -238,38 +248,45 @@ void Node3D::set_quaternion(const Quaternion &p_quaternion) { } Vector3 Node3D::get_global_position() const { + ERR_READ_THREAD_GUARD_V(Vector3()); return get_global_transform().get_origin(); } void Node3D::set_global_position(const Vector3 &p_position) { + ERR_THREAD_GUARD; Transform3D transform = get_global_transform(); transform.set_origin(p_position); set_global_transform(transform); } Vector3 Node3D::get_global_rotation() const { + ERR_READ_THREAD_GUARD_V(Vector3()); return get_global_transform().get_basis().get_euler(); } Vector3 Node3D::get_global_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(Vector3()); Vector3 radians = get_global_rotation(); return Vector3(Math::rad_to_deg(radians.x), Math::rad_to_deg(radians.y), Math::rad_to_deg(radians.z)); } void Node3D::set_global_rotation(const Vector3 &p_euler_rad) { + ERR_THREAD_GUARD; Transform3D transform = get_global_transform(); transform.basis = Basis::from_euler(p_euler_rad); set_global_transform(transform); } void Node3D::set_global_rotation_degrees(const Vector3 &p_euler_degrees) { + ERR_THREAD_GUARD; Vector3 radians(Math::deg_to_rad(p_euler_degrees.x), Math::deg_to_rad(p_euler_degrees.y), Math::deg_to_rad(p_euler_degrees.z)); set_global_rotation(radians); } void Node3D::set_transform(const Transform3D &p_transform) { + ERR_THREAD_GUARD; data.local_transform = p_transform; - data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // Make rot/scale dirty. + data.dirty.set(DIRTY_EULER_ROTATION_AND_SCALE); // Make rot/scale dirty. _propagate_transform_changed(this); if (data.notify_local_transform) { @@ -278,18 +295,16 @@ void Node3D::set_transform(const Transform3D &p_transform) { } Basis Node3D::get_basis() const { + ERR_READ_THREAD_GUARD_V(Basis()); return get_transform().basis; } Quaternion Node3D::get_quaternion() const { - if (data.dirty & DIRTY_LOCAL_TRANSFORM) { - _update_local_transform(); - } - - return data.local_transform.basis.get_rotation_quaternion(); + return get_transform().basis.get_rotation_quaternion(); } void Node3D::set_global_transform(const Transform3D &p_transform) { + ERR_THREAD_GUARD; Transform3D xform = (data.parent && !data.top_level_active) ? data.parent->get_global_transform().affine_inverse() * p_transform : p_transform; @@ -298,31 +313,42 @@ void Node3D::set_global_transform(const Transform3D &p_transform) { } Transform3D Node3D::get_transform() const { - if (data.dirty & DIRTY_LOCAL_TRANSFORM) { + ERR_READ_THREAD_GUARD_V(Transform3D()); + if (data.dirty.get() & DIRTY_LOCAL_TRANSFORM) { + // This update can happen if needed over multiple threads. _update_local_transform(); } return data.local_transform; } + Transform3D Node3D::get_global_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); - if (data.dirty & DIRTY_GLOBAL_TRANSFORM) { - if (data.dirty & DIRTY_LOCAL_TRANSFORM) { - _update_local_transform(); + /* Due to how threads work at scene level, while this global transform won't be able to be changed from outside a thread, + * it is possible that multiple threads can access it while it's dirty from previous work. Due to this, we must ensure that + * the dirty/update process is thread safe by utilizing atomic copies. + */ + + uint32_t dirty = data.dirty.get(); + if (dirty & DIRTY_GLOBAL_TRANSFORM) { + if (dirty & DIRTY_LOCAL_TRANSFORM) { + _update_local_transform(); // Update local transform atomically. } + Transform3D new_global; if (data.parent && !data.top_level_active) { - data.global_transform = data.parent->get_global_transform() * data.local_transform; + new_global = data.parent->get_global_transform() * data.local_transform; } else { - data.global_transform = data.local_transform; + new_global = data.local_transform; } if (data.disable_scale) { - data.global_transform.basis.orthonormalize(); + new_global.basis.orthonormalize(); } - data.dirty &= ~DIRTY_GLOBAL_TRANSFORM; + data.global_transform = new_global; + data.dirty.bit_and(~DIRTY_GLOBAL_TRANSFORM); } return data.global_transform; @@ -339,6 +365,7 @@ Transform3D Node3D::get_local_gizmo_transform() const { #endif Node3D *Node3D::get_parent_node_3d() const { + ERR_READ_THREAD_GUARD_V(nullptr); // This can't be changed on threads anyway. if (data.top_level) { return nullptr; } @@ -347,6 +374,7 @@ Node3D *Node3D::get_parent_node_3d() const { } Transform3D Node3D::get_relative_transform(const Node *p_parent) const { + ERR_READ_THREAD_GUARD_V(Transform3D()); if (p_parent == this) { return Transform3D(); } @@ -361,6 +389,7 @@ Transform3D Node3D::get_relative_transform(const Node *p_parent) const { } void Node3D::set_position(const Vector3 &p_position) { + ERR_THREAD_GUARD; data.local_transform.origin = p_position; _propagate_transform_changed(this); if (data.notify_local_transform) { @@ -369,19 +398,20 @@ void Node3D::set_position(const Vector3 &p_position) { } void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) { + ERR_THREAD_GUARD; if (data.rotation_edit_mode == p_mode) { return; } bool transform_changed = false; - if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && !(data.dirty & DIRTY_LOCAL_TRANSFORM)) { + if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && !(data.dirty.get() & DIRTY_LOCAL_TRANSFORM)) { data.local_transform.orthogonalize(); transform_changed = true; } data.rotation_edit_mode = p_mode; - if (p_mode == ROTATION_EDIT_MODE_EULER && (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE)) { + if (p_mode == ROTATION_EDIT_MODE_EULER && (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE)) { // If going to Euler mode, ensure that vectors are _not_ dirty, else the retrieved value may be wrong. // Otherwise keep what is there, so switching back and forth between modes does not break the vectors. @@ -399,10 +429,12 @@ void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) { } Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const { + ERR_READ_THREAD_GUARD_V(ROTATION_EDIT_MODE_EULER); return data.rotation_edit_mode; } void Node3D::set_rotation_order(EulerOrder p_order) { + ERR_THREAD_GUARD; if (data.euler_rotation_order == p_order) { return; } @@ -410,13 +442,13 @@ void Node3D::set_rotation_order(EulerOrder p_order) { ERR_FAIL_INDEX(int32_t(p_order), 6); bool transform_changed = false; - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { _update_rotation_and_scale(); - } else if (data.dirty & DIRTY_LOCAL_TRANSFORM) { + } else if (data.dirty.get() & DIRTY_LOCAL_TRANSFORM) { data.euler_rotation = Basis::from_euler(data.euler_rotation, data.euler_rotation_order).get_euler_normalized(p_order); transform_changed = true; } else { - data.dirty |= DIRTY_LOCAL_TRANSFORM; + data.dirty.bit_or(DIRTY_LOCAL_TRANSFORM); transform_changed = true; } @@ -432,18 +464,20 @@ void Node3D::set_rotation_order(EulerOrder p_order) { } EulerOrder Node3D::get_rotation_order() const { + ERR_READ_THREAD_GUARD_V(EulerOrder::XYZ); return data.euler_rotation_order; } void Node3D::set_rotation(const Vector3 &p_euler_rad) { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_THREAD_GUARD; + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { // Update scale only if rotation and scale are dirty, as rotation will be overridden. data.scale = data.local_transform.basis.get_scale(); - data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; + data.dirty.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE); } data.euler_rotation = p_euler_rad; - data.dirty = DIRTY_LOCAL_TRANSFORM; + data.dirty.set(DIRTY_LOCAL_TRANSFORM); _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); @@ -451,19 +485,21 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) { } void Node3D::set_rotation_degrees(const Vector3 &p_euler_degrees) { + ERR_THREAD_GUARD; Vector3 radians(Math::deg_to_rad(p_euler_degrees.x), Math::deg_to_rad(p_euler_degrees.y), Math::deg_to_rad(p_euler_degrees.z)); set_rotation(radians); } void Node3D::set_scale(const Vector3 &p_scale) { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_THREAD_GUARD; + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { // Update rotation only if rotation and scale are dirty, as scale will be overridden. data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); - data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; + data.dirty.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE); } data.scale = p_scale; - data.dirty = DIRTY_LOCAL_TRANSFORM; + data.dirty.set(DIRTY_LOCAL_TRANSFORM); _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); @@ -471,11 +507,13 @@ void Node3D::set_scale(const Vector3 &p_scale) { } Vector3 Node3D::get_position() const { + ERR_READ_THREAD_GUARD_V(Vector3()); return data.local_transform.origin; } Vector3 Node3D::get_rotation() const { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_READ_THREAD_GUARD_V(Vector3()); + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { _update_rotation_and_scale(); } @@ -483,12 +521,14 @@ Vector3 Node3D::get_rotation() const { } Vector3 Node3D::get_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(Vector3()); Vector3 radians = get_rotation(); return Vector3(Math::rad_to_deg(radians.x), Math::rad_to_deg(radians.y), Math::rad_to_deg(radians.z)); } Vector3 Node3D::get_scale() const { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_READ_THREAD_GUARD_V(Vector3()); + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { _update_rotation_and_scale(); } @@ -496,6 +536,7 @@ Vector3 Node3D::get_scale() const { } void Node3D::update_gizmos() { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED if (!is_inside_world()) { return; @@ -514,6 +555,7 @@ void Node3D::update_gizmos() { } void Node3D::set_subgizmo_selection(Ref p_gizmo, int p_id, Transform3D p_transform) { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED if (!is_inside_world()) { return; @@ -526,6 +568,7 @@ void Node3D::set_subgizmo_selection(Ref p_gizmo, int p_id, Transfor } void Node3D::clear_subgizmo_selection() { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED if (!is_inside_world()) { return; @@ -542,6 +585,7 @@ void Node3D::clear_subgizmo_selection() { } void Node3D::add_gizmo(Ref p_gizmo) { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED if (data.gizmos_disabled || p_gizmo.is_null()) { return; @@ -559,6 +603,7 @@ void Node3D::add_gizmo(Ref p_gizmo) { } void Node3D::remove_gizmo(Ref p_gizmo) { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED int idx = data.gizmos.find(p_gizmo); if (idx != -1) { @@ -569,6 +614,7 @@ void Node3D::remove_gizmo(Ref p_gizmo) { } void Node3D::clear_gizmos() { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED for (int i = 0; i < data.gizmos.size(); i++) { data.gizmos.write[i]->free(); @@ -578,6 +624,7 @@ void Node3D::clear_gizmos() { } TypedArray Node3D::get_gizmos_bind() const { + ERR_THREAD_GUARD_V(TypedArray()); TypedArray ret; #ifdef TOOLS_ENABLED @@ -590,6 +637,7 @@ TypedArray Node3D::get_gizmos_bind() const { } Vector> Node3D::get_gizmos() const { + ERR_THREAD_GUARD_V(Vector>()); #ifdef TOOLS_ENABLED return data.gizmos; #else @@ -615,6 +663,7 @@ void Node3D::_update_gizmos() { } void Node3D::set_disable_gizmos(bool p_enabled) { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED data.gizmos_disabled = p_enabled; if (!p_enabled) { @@ -624,6 +673,7 @@ void Node3D::set_disable_gizmos(bool p_enabled) { } void Node3D::reparent(Node *p_parent, bool p_keep_global_transform) { + ERR_THREAD_GUARD; Transform3D temp = get_global_transform(); Node::reparent(p_parent); if (p_keep_global_transform) { @@ -632,14 +682,17 @@ void Node3D::reparent(Node *p_parent, bool p_keep_global_transform) { } void Node3D::set_disable_scale(bool p_enabled) { + ERR_THREAD_GUARD; data.disable_scale = p_enabled; } bool Node3D::is_scale_disabled() const { + ERR_READ_THREAD_GUARD_V(false); return data.disable_scale; } void Node3D::set_as_top_level(bool p_enabled) { + ERR_THREAD_GUARD; if (data.top_level == p_enabled) { return; } @@ -658,10 +711,12 @@ void Node3D::set_as_top_level(bool p_enabled) { } bool Node3D::is_set_as_top_level() const { + ERR_READ_THREAD_GUARD_V(false); return data.top_level; } Ref Node3D::get_world_3d() const { + ERR_READ_THREAD_GUARD_V(Ref()); // World3D can only be set from main thread, so it's safe to obtain on threads. ERR_FAIL_COND_V(!is_inside_world(), Ref()); ERR_FAIL_COND_V(!data.viewport, Ref()); @@ -688,14 +743,17 @@ void Node3D::_propagate_visibility_changed() { } void Node3D::show() { + ERR_MAIN_THREAD_GUARD; set_visible(true); } void Node3D::hide() { + ERR_MAIN_THREAD_GUARD; set_visible(false); } void Node3D::set_visible(bool p_visible) { + ERR_MAIN_THREAD_GUARD; if (data.visible == p_visible) { return; } @@ -709,10 +767,12 @@ void Node3D::set_visible(bool p_visible) { } bool Node3D::is_visible() const { + ERR_READ_THREAD_GUARD_V(false); return data.visible; } bool Node3D::is_visible_in_tree() const { + ERR_READ_THREAD_GUARD_V(false); // Since visibility can only be changed from main thread, this is safe to call. const Node3D *s = this; while (s) { @@ -726,42 +786,49 @@ bool Node3D::is_visible_in_tree() const { } void Node3D::rotate_object_local(const Vector3 &p_axis, real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate_local(p_axis, p_angle); set_transform(t); } void Node3D::rotate(const Vector3 &p_axis, real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate(p_axis, p_angle); set_transform(t); } void Node3D::rotate_x(real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate(Vector3(1, 0, 0), p_angle); set_transform(t); } void Node3D::rotate_y(real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate(Vector3(0, 1, 0), p_angle); set_transform(t); } void Node3D::rotate_z(real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate(Vector3(0, 0, 1), p_angle); set_transform(t); } void Node3D::translate(const Vector3 &p_offset) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.translate_local(p_offset); set_transform(t); } void Node3D::translate_object_local(const Vector3 &p_offset) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); Transform3D s; @@ -770,52 +837,61 @@ void Node3D::translate_object_local(const Vector3 &p_offset) { } void Node3D::scale(const Vector3 &p_ratio) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.scale(p_ratio); set_transform(t); } void Node3D::scale_object_local(const Vector3 &p_scale) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.scale_local(p_scale); set_transform(t); } void Node3D::global_rotate(const Vector3 &p_axis, real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_global_transform(); t.basis.rotate(p_axis, p_angle); set_global_transform(t); } void Node3D::global_scale(const Vector3 &p_scale) { + ERR_THREAD_GUARD; Transform3D t = get_global_transform(); t.basis.scale(p_scale); set_global_transform(t); } void Node3D::global_translate(const Vector3 &p_offset) { + ERR_THREAD_GUARD; Transform3D t = get_global_transform(); t.origin += p_offset; set_global_transform(t); } void Node3D::orthonormalize() { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.orthonormalize(); set_transform(t); } void Node3D::set_identity() { + ERR_THREAD_GUARD; set_transform(Transform3D()); } void Node3D::look_at(const Vector3 &p_target, const Vector3 &p_up) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!is_inside_tree(), "Node not inside tree. Use look_at_from_position() instead."); Vector3 origin = get_global_transform().origin; look_at_from_position(origin, p_target, p_up); } void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(p_pos.is_equal_approx(p_target), "Node origin and target are in the same position, look_at() failed."); ERR_FAIL_COND_MSG(p_up.is_zero_approx(), "The up vector can't be zero, look_at() failed."); ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_zero_approx(), "Up vector and direction between node origin and target are aligned, look_at() failed."); @@ -827,30 +903,37 @@ void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target } Vector3 Node3D::to_local(Vector3 p_global) const { + ERR_READ_THREAD_GUARD_V(Vector3()); return get_global_transform().affine_inverse().xform(p_global); } Vector3 Node3D::to_global(Vector3 p_local) const { + ERR_READ_THREAD_GUARD_V(Vector3()); return get_global_transform().xform(p_local); } void Node3D::set_notify_transform(bool p_enabled) { + ERR_THREAD_GUARD; data.notify_transform = p_enabled; } bool Node3D::is_transform_notification_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return data.notify_transform; } void Node3D::set_notify_local_transform(bool p_enabled) { + ERR_THREAD_GUARD; data.notify_local_transform = p_enabled; } bool Node3D::is_local_transform_notification_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return data.notify_local_transform; } void Node3D::force_update_transform() { + ERR_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); if (!xform_change.in_list()) { return; //nothing to update @@ -894,6 +977,7 @@ void Node3D::_update_visibility_parent(bool p_update_root) { } void Node3D::set_visibility_parent(const NodePath &p_path) { + ERR_MAIN_THREAD_GUARD; visibility_parent_path = p_path; if (is_inside_tree()) { _update_visibility_parent(true); @@ -901,6 +985,7 @@ void Node3D::set_visibility_parent(const NodePath &p_path) { } NodePath Node3D::get_visibility_parent() const { + ERR_READ_THREAD_GUARD_V(NodePath()); return visibility_parent_path; } diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 98bcab5fd49..4fb77eeb9f5 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -88,6 +88,7 @@ private: mutable SelfList xform_change; // This Data struct is to avoid namespace pollution in derived classes. + struct Data { mutable Transform3D global_transform; mutable Transform3D local_transform; @@ -96,7 +97,7 @@ private: mutable Vector3 scale = Vector3(1, 1, 1); mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER; - mutable int dirty = DIRTY_NONE; + mutable SafeNumeric dirty; Viewport *viewport = nullptr; @@ -106,7 +107,6 @@ private: RID visibility_parent; - int children_lock = 0; Node3D *parent = nullptr; List children; List::Element *C = nullptr; @@ -137,6 +137,7 @@ private: void _propagate_visibility_parent(); void _update_visibility_parent(bool p_update_root); + void _propagate_transform_changed_deferred(); protected: _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 6ef39c88d8a..39bfa556e51 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -187,6 +187,7 @@ Size2 Control::_edit_get_minimum_size() const { #endif void Control::reparent(Node *p_parent, bool p_keep_global_transform) { + ERR_MAIN_THREAD_GUARD; Transform2D temp = get_global_transform(); Node::reparent(p_parent); if (p_keep_global_transform) { @@ -197,6 +198,7 @@ void Control::reparent(Node *p_parent, bool p_keep_global_transform) { // Editor integration. void Control::get_argument_options(const StringName &p_function, int p_idx, List *r_options) const { + ERR_READ_THREAD_GUARD; Node::get_argument_options(p_function, p_idx, r_options); if (p_idx == 0) { @@ -222,6 +224,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List } PackedStringArray Control::get_configuration_warnings() const { + ERR_READ_THREAD_GUARD_V(PackedStringArray()); PackedStringArray warnings = Node::get_configuration_warnings(); if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) { @@ -236,6 +239,7 @@ PackedStringArray Control::get_configuration_warnings() const { } bool Control::is_text_field() const { + ERR_READ_THREAD_GUARD_V(false); return false; } @@ -257,6 +261,7 @@ String Control::properties_managed_by_container[] = { }; bool Control::_set(const StringName &p_name, const Variant &p_value) { + ERR_MAIN_THREAD_GUARD_V(false); String name = p_name; if (!name.begins_with("theme_override")) { return false; @@ -327,6 +332,7 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { } bool Control::_get(const StringName &p_name, Variant &r_ret) const { + ERR_MAIN_THREAD_GUARD_V(false); String sname = p_name; if (!sname.begins_with("theme_override")) { return false; @@ -358,6 +364,7 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const { } void Control::_get_property_list(List *p_list) const { + ERR_MAIN_THREAD_GUARD; Ref default_theme = ThemeDB::get_singleton()->get_default_theme(); p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Theme Overrides", "theme_override_"), PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP)); @@ -603,18 +610,22 @@ bool Control::_property_get_revert(const StringName &p_name, Variant &r_property // Global relations. bool Control::is_top_level_control() const { + ERR_READ_THREAD_GUARD_V(false); return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level()); } Control *Control::get_parent_control() const { + ERR_READ_THREAD_GUARD_V(nullptr); return data.parent_control; } Window *Control::get_parent_window() const { + ERR_READ_THREAD_GUARD_V(nullptr); return data.parent_window; } Control *Control::get_root_parent_control() const { + ERR_READ_THREAD_GUARD_V(nullptr); const CanvasItem *ci = this; const Control *root = this; @@ -635,6 +646,7 @@ Control *Control::get_root_parent_control() const { } Rect2 Control::get_parent_anchorable_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); if (!is_inside_tree()) { return Rect2(); } @@ -662,6 +674,7 @@ Rect2 Control::get_parent_anchorable_rect() const { } Size2 Control::get_parent_area_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); return get_parent_anchorable_rect().size; } @@ -689,6 +702,7 @@ void Control::_update_canvas_item_transform() { } Transform2D Control::get_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); Transform2D xform = _get_internal_transform(); xform[2] += get_position(); return xform; @@ -707,6 +721,7 @@ void Control::_set_anchor(Side p_side, real_t p_anchor) { } void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_side, 4); Rect2 parent_rect = get_parent_anchorable_rect(); @@ -739,12 +754,14 @@ void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool } real_t Control::get_anchor(Side p_side) const { + ERR_READ_THREAD_GUARD_V(0); ERR_FAIL_INDEX_V(int(p_side), 4, 0.0); return data.anchor[p_side]; } void Control::set_offset(Side p_side, real_t p_value) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_side, 4); if (data.offset[p_side] == p_value) { return; @@ -755,17 +772,20 @@ void Control::set_offset(Side p_side, real_t p_value) { } real_t Control::get_offset(Side p_side) const { + ERR_READ_THREAD_GUARD_V(0); ERR_FAIL_INDEX_V((int)p_side, 4, 0); return data.offset[p_side]; } void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) { + ERR_MAIN_THREAD_GUARD; set_anchor(p_side, p_anchor, false, p_push_opposite_anchor); set_offset(p_side, p_pos); } void Control::set_begin(const Point2 &p_point) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!isfinite(p_point.x) || !isfinite(p_point.y)); if (data.offset[0] == p_point.x && data.offset[1] == p_point.y) { return; @@ -777,10 +797,12 @@ void Control::set_begin(const Point2 &p_point) { } Point2 Control::get_begin() const { + ERR_READ_THREAD_GUARD_V(Vector2()); return Point2(data.offset[0], data.offset[1]); } void Control::set_end(const Point2 &p_point) { + ERR_MAIN_THREAD_GUARD; if (data.offset[2] == p_point.x && data.offset[3] == p_point.y) { return; } @@ -791,10 +813,12 @@ void Control::set_end(const Point2 &p_point) { } Point2 Control::get_end() const { + ERR_READ_THREAD_GUARD_V(Point2()); return Point2(data.offset[2], data.offset[3]); } void Control::set_h_grow_direction(GrowDirection p_direction) { + ERR_MAIN_THREAD_GUARD; if (data.h_grow == p_direction) { return; } @@ -806,10 +830,12 @@ void Control::set_h_grow_direction(GrowDirection p_direction) { } Control::GrowDirection Control::get_h_grow_direction() const { + ERR_READ_THREAD_GUARD_V(GROW_DIRECTION_BEGIN); return data.h_grow; } void Control::set_v_grow_direction(GrowDirection p_direction) { + ERR_MAIN_THREAD_GUARD; if (data.v_grow == p_direction) { return; } @@ -821,6 +847,7 @@ void Control::set_v_grow_direction(GrowDirection p_direction) { } Control::GrowDirection Control::get_v_grow_direction() const { + ERR_READ_THREAD_GUARD_V(GROW_DIRECTION_BEGIN); return data.v_grow; } @@ -1045,6 +1072,7 @@ int Control::_get_anchors_layout_preset() const { } void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_preset, 16); //Left @@ -1161,6 +1189,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { } void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_preset, 16); ERR_FAIL_INDEX((int)p_resize_mode, 4); @@ -1296,11 +1325,13 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz } void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) { + ERR_MAIN_THREAD_GUARD; set_anchors_preset(p_preset); set_offsets_preset(p_preset, p_resize_mode, p_margin); } void Control::set_grow_direction_preset(LayoutPreset p_preset) { + ERR_MAIN_THREAD_GUARD; // Select correct horizontal grow direction. switch (p_preset) { case PRESET_TOP_LEFT: @@ -1363,6 +1394,7 @@ void Control::_set_position(const Point2 &p_point) { } void Control::set_position(const Point2 &p_point, bool p_keep_offsets) { + ERR_MAIN_THREAD_GUARD; if (p_keep_offsets) { _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor); } else { @@ -1372,6 +1404,7 @@ void Control::set_position(const Point2 &p_point, bool p_keep_offsets) { } Size2 Control::get_position() const { + ERR_READ_THREAD_GUARD_V(Size2()); return data.pos_cache; } @@ -1380,6 +1413,7 @@ void Control::_set_global_position(const Point2 &p_point) { } void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) { + ERR_MAIN_THREAD_GUARD; Transform2D inv; if (data.parent_canvas_item) { @@ -1390,10 +1424,12 @@ void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) { } Point2 Control::get_global_position() const { + ERR_READ_THREAD_GUARD_V(Point2()); return get_global_transform().get_origin(); } Point2 Control::get_screen_position() const { + ERR_READ_THREAD_GUARD_V(Point2()); ERR_FAIL_COND_V(!is_inside_tree(), Point2()); return get_screen_transform().get_origin(); } @@ -1408,6 +1444,7 @@ void Control::_set_size(const Size2 &p_size) { } void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!isfinite(p_size.x) || !isfinite(p_size.y)); Size2 new_size = p_size; Size2 min = get_combined_minimum_size(); @@ -1427,14 +1464,17 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { } Size2 Control::get_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); return data.size_cache; } void Control::reset_size() { + ERR_MAIN_THREAD_GUARD; set_size(Size2()); } void Control::set_rect(const Rect2 &p_rect) { + ERR_MAIN_THREAD_GUARD; for (int i = 0; i < 4; i++) { data.anchor[i] = ANCHOR_BEGIN; } @@ -1446,16 +1486,19 @@ void Control::set_rect(const Rect2 &p_rect) { } Rect2 Control::get_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); Transform2D xform = get_transform(); return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_global_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); Transform2D xform = get_global_transform(); return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_screen_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); Transform2D xform = get_screen_transform(); @@ -1463,10 +1506,12 @@ Rect2 Control::get_screen_rect() const { } Rect2 Control::get_anchorable_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); return Rect2(Point2(), get_size()); } void Control::set_scale(const Vector2 &p_scale) { + ERR_MAIN_THREAD_GUARD; if (data.scale == p_scale) { return; } @@ -1484,10 +1529,12 @@ void Control::set_scale(const Vector2 &p_scale) { } Vector2 Control::get_scale() const { + ERR_READ_THREAD_GUARD_V(Vector2()); return data.scale; } void Control::set_rotation(real_t p_radians) { + ERR_MAIN_THREAD_GUARD; if (data.rotation == p_radians) { return; } @@ -1498,18 +1545,22 @@ void Control::set_rotation(real_t p_radians) { } void Control::set_rotation_degrees(real_t p_degrees) { + ERR_MAIN_THREAD_GUARD; set_rotation(Math::deg_to_rad(p_degrees)); } real_t Control::get_rotation() const { + ERR_READ_THREAD_GUARD_V(0); return data.rotation; } real_t Control::get_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(0); return Math::rad_to_deg(get_rotation()); } void Control::set_pivot_offset(const Vector2 &p_pivot) { + ERR_MAIN_THREAD_GUARD; if (data.pivot_offset == p_pivot) { return; } @@ -1520,6 +1571,7 @@ void Control::set_pivot_offset(const Vector2 &p_pivot) { } Vector2 Control::get_pivot_offset() const { + ERR_READ_THREAD_GUARD_V(Vector2()); return data.pivot_offset; } @@ -1541,6 +1593,7 @@ void Control::_update_minimum_size() { } void Control::update_minimum_size() { + ERR_MAIN_THREAD_GUARD; if (!is_inside_tree() || data.block_minimum_size_adjust) { return; } @@ -1577,16 +1630,19 @@ void Control::update_minimum_size() { } void Control::set_block_minimum_size_adjust(bool p_block) { + ERR_MAIN_THREAD_GUARD; data.block_minimum_size_adjust = p_block; } Size2 Control::get_minimum_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); Vector2 ms; GDVIRTUAL_CALL(_get_minimum_size, ms); return ms; } void Control::set_custom_minimum_size(const Size2 &p_custom) { + ERR_MAIN_THREAD_GUARD; if (p_custom == data.custom_minimum_size) { return; } @@ -1601,6 +1657,7 @@ void Control::set_custom_minimum_size(const Size2 &p_custom) { } Size2 Control::get_custom_minimum_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); return data.custom_minimum_size; } @@ -1623,6 +1680,7 @@ void Control::_update_minimum_size_cache() { } Size2 Control::get_combined_minimum_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); if (!data.minimum_size_valid) { const_cast(this)->_update_minimum_size_cache(); } @@ -1696,6 +1754,7 @@ void Control::_clear_size_warning() { // Container sizing. void Control::set_h_size_flags(BitField p_flags) { + ERR_MAIN_THREAD_GUARD; if ((int)data.h_size_flags == (int)p_flags) { return; } @@ -1704,10 +1763,12 @@ void Control::set_h_size_flags(BitField p_flags) { } BitField Control::get_h_size_flags() const { + ERR_READ_THREAD_GUARD_V(SIZE_EXPAND_FILL); return data.h_size_flags; } void Control::set_v_size_flags(BitField p_flags) { + ERR_MAIN_THREAD_GUARD; if ((int)data.v_size_flags == (int)p_flags) { return; } @@ -1716,10 +1777,12 @@ void Control::set_v_size_flags(BitField p_flags) { } BitField Control::get_v_size_flags() const { + ERR_READ_THREAD_GUARD_V(SIZE_EXPAND_FILL); return data.v_size_flags; } void Control::set_stretch_ratio(real_t p_ratio) { + ERR_MAIN_THREAD_GUARD; if (data.expand == p_ratio) { return; } @@ -1729,6 +1792,7 @@ void Control::set_stretch_ratio(real_t p_ratio) { } real_t Control::get_stretch_ratio() const { + ERR_READ_THREAD_GUARD_V(0); return data.expand; } @@ -1755,12 +1819,14 @@ void Control::gui_input(const Ref &p_event) { } void Control::accept_event() { + ERR_MAIN_THREAD_GUARD; if (is_inside_tree()) { get_viewport()->_gui_accept_event(); } } bool Control::has_point(const Point2 &p_point) const { + ERR_READ_THREAD_GUARD_V(false); bool ret; if (GDVIRTUAL_CALL(_has_point, p_point, ret)) { return ret; @@ -1769,6 +1835,7 @@ bool Control::has_point(const Point2 &p_point) const { } void Control::set_mouse_filter(MouseFilter p_filter) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_filter, 3); data.mouse_filter = p_filter; notify_property_list_changed(); @@ -1776,23 +1843,28 @@ void Control::set_mouse_filter(MouseFilter p_filter) { } Control::MouseFilter Control::get_mouse_filter() const { + ERR_READ_THREAD_GUARD_V(MOUSE_FILTER_IGNORE); return data.mouse_filter; } void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) { + ERR_MAIN_THREAD_GUARD; data.force_pass_scroll_events = p_force_pass_scroll_events; } bool Control::is_force_pass_scroll_events() const { + ERR_READ_THREAD_GUARD_V(false); return data.force_pass_scroll_events; } void Control::warp_mouse(const Point2 &p_position) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position)); } void Control::set_shortcut_context(const Node *p_node) { + ERR_MAIN_THREAD_GUARD; if (p_node != nullptr) { data.shortcut_context = p_node->get_instance_id(); } else { @@ -1801,6 +1873,7 @@ void Control::set_shortcut_context(const Node *p_node) { } Node *Control::get_shortcut_context() const { + ERR_READ_THREAD_GUARD_V(nullptr); Object *ctx_obj = ObjectDB::get_instance(data.shortcut_context); Node *ctx_node = Object::cast_to(ctx_obj); @@ -1808,6 +1881,7 @@ Node *Control::get_shortcut_context() const { } bool Control::is_focus_owner_in_shortcut_context() const { + ERR_READ_THREAD_GUARD_V(false); if (data.shortcut_context == ObjectID()) { // No context, therefore global - always "in" context. return true; @@ -1823,12 +1897,14 @@ bool Control::is_focus_owner_in_shortcut_context() const { // Drag and drop handling. void Control::set_drag_forwarding(const Callable &p_drag, const Callable &p_can_drop, const Callable &p_drop) { + ERR_MAIN_THREAD_GUARD; data.forward_drag = p_drag; data.forward_can_drop = p_can_drop; data.forward_drop = p_drop; } Variant Control::get_drag_data(const Point2 &p_point) { + ERR_READ_THREAD_GUARD_V(Variant()); Variant ret; if (data.forward_drag.is_valid()) { Variant p = p_point; @@ -1846,6 +1922,7 @@ Variant Control::get_drag_data(const Point2 &p_point) { } bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + ERR_READ_THREAD_GUARD_V(false); if (data.forward_can_drop.is_valid()) { Variant ret; Variant p = p_point; @@ -1864,6 +1941,7 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const } void Control::drop_data(const Point2 &p_point, const Variant &p_data) { + ERR_READ_THREAD_GUARD; if (data.forward_drop.is_valid()) { Variant ret; Variant p = p_point; @@ -1880,6 +1958,7 @@ void Control::drop_data(const Point2 &p_point, const Variant &p_data) { } void Control::force_drag(const Variant &p_data, Control *p_control) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(p_data.get_type() == Variant::NIL); @@ -1887,18 +1966,21 @@ void Control::force_drag(const Variant &p_data, Control *p_control) { } void Control::set_drag_preview(Control *p_control) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!get_viewport()->gui_is_dragging()); get_viewport()->_gui_set_drag_preview(this, p_control); } bool Control::is_drag_successful() const { + ERR_READ_THREAD_GUARD_V(false); return is_inside_tree() && get_viewport()->gui_is_drag_successful(); } // Focus. void Control::set_focus_mode(FocusMode p_focus_mode) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_focus_mode, 3); if (is_inside_tree() && p_focus_mode == FOCUS_NONE && data.focus_mode != FOCUS_NONE && has_focus()) { @@ -1909,14 +1991,17 @@ void Control::set_focus_mode(FocusMode p_focus_mode) { } Control::FocusMode Control::get_focus_mode() const { + ERR_READ_THREAD_GUARD_V(FOCUS_NONE); return data.focus_mode; } bool Control::has_focus() const { + ERR_READ_THREAD_GUARD_V(false); return is_inside_tree() && get_viewport()->_gui_control_has_focus(this); } void Control::grab_focus() { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); if (data.focus_mode == FOCUS_NONE) { @@ -1928,12 +2013,14 @@ void Control::grab_focus() { } void Control::grab_click_focus() { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); get_viewport()->_gui_grab_click_focus(this); } void Control::release_focus() { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); if (!has_focus()) { @@ -1970,6 +2057,7 @@ static Control *_next_control(Control *p_from) { } Control *Control::find_next_valid_focus() const { + ERR_READ_THREAD_GUARD_V(nullptr); Control *from = const_cast(this); while (true) { @@ -2060,6 +2148,7 @@ static Control *_prev_control(Control *p_from) { } Control *Control::find_prev_valid_focus() const { + ERR_READ_THREAD_GUARD_V(nullptr); Control *from = const_cast(this); while (true) { @@ -2122,28 +2211,34 @@ Control *Control::find_prev_valid_focus() const { } void Control::set_focus_neighbor(Side p_side, const NodePath &p_neighbor) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_side, 4); data.focus_neighbor[p_side] = p_neighbor; } NodePath Control::get_focus_neighbor(Side p_side) const { + ERR_READ_THREAD_GUARD_V(NodePath()); ERR_FAIL_INDEX_V((int)p_side, 4, NodePath()); return data.focus_neighbor[p_side]; } void Control::set_focus_next(const NodePath &p_next) { + ERR_MAIN_THREAD_GUARD; data.focus_next = p_next; } NodePath Control::get_focus_next() const { + ERR_READ_THREAD_GUARD_V(NodePath()); return data.focus_next; } void Control::set_focus_previous(const NodePath &p_prev) { + ERR_MAIN_THREAD_GUARD; data.focus_prev = p_prev; } NodePath Control::get_focus_previous() const { + ERR_READ_THREAD_GUARD_V(NodePath()); return data.focus_prev; } @@ -2290,6 +2385,7 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons // Rendering. void Control::set_default_cursor_shape(CursorShape p_shape) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(int(p_shape), CURSOR_MAX); if (data.default_cursor == p_shape) { @@ -2308,14 +2404,17 @@ void Control::set_default_cursor_shape(CursorShape p_shape) { } Control::CursorShape Control::get_default_cursor_shape() const { + ERR_READ_THREAD_GUARD_V(CURSOR_ARROW); return data.default_cursor; } Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const { + ERR_READ_THREAD_GUARD_V(CURSOR_ARROW); return data.default_cursor; } void Control::set_disable_visibility_clip(bool p_ignore) { + ERR_MAIN_THREAD_GUARD; if (data.disable_visibility_clip == p_ignore) { return; } @@ -2324,10 +2423,12 @@ void Control::set_disable_visibility_clip(bool p_ignore) { } bool Control::is_visibility_clip_disabled() const { + ERR_READ_THREAD_GUARD_V(false); return data.disable_visibility_clip; } void Control::set_clip_contents(bool p_clip) { + ERR_MAIN_THREAD_GUARD; if (data.clip_contents == p_clip) { return; } @@ -2336,6 +2437,7 @@ void Control::set_clip_contents(bool p_clip) { } bool Control::is_clipping_contents() { + ERR_READ_THREAD_GUARD_V(false); return data.clip_contents; } @@ -2366,18 +2468,22 @@ void Control::_update_theme_item_cache() { } void Control::set_theme_owner_node(Node *p_node) { + ERR_MAIN_THREAD_GUARD; data.theme_owner->set_owner_node(p_node); } Node *Control::get_theme_owner_node() const { + ERR_READ_THREAD_GUARD_V(nullptr); return data.theme_owner->get_owner_node(); } bool Control::has_theme_owner_node() const { + ERR_READ_THREAD_GUARD_V(false); return data.theme_owner->has_owner_node(); } void Control::set_theme(const Ref &p_theme) { + ERR_MAIN_THREAD_GUARD; if (data.theme == p_theme) { return; } @@ -2409,10 +2515,12 @@ void Control::set_theme(const Ref &p_theme) { } Ref Control::get_theme() const { + ERR_READ_THREAD_GUARD_V(Ref()); return data.theme; } void Control::set_theme_type_variation(const StringName &p_theme_type) { + ERR_MAIN_THREAD_GUARD; if (data.theme_type_variation == p_theme_type) { return; } @@ -2423,12 +2531,14 @@ void Control::set_theme_type_variation(const StringName &p_theme_type) { } StringName Control::get_theme_type_variation() const { + ERR_READ_THREAD_GUARD_V(StringName()); return data.theme_type_variation; } /// Theme property lookup. Ref Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref()); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2452,6 +2562,7 @@ Ref Control::get_theme_icon(const StringName &p_name, const StringNam } Ref Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref()); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2475,6 +2586,7 @@ Ref Control::get_theme_stylebox(const StringName &p_name, const String } Ref Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref()); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2498,6 +2610,7 @@ Ref Control::get_theme_font(const StringName &p_name, const StringName &p_ } int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(0); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2521,6 +2634,7 @@ int Control::get_theme_font_size(const StringName &p_name, const StringName &p_t } Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Color()); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2544,6 +2658,7 @@ Color Control::get_theme_color(const StringName &p_name, const StringName &p_the } int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(0); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2567,6 +2682,7 @@ int Control::get_theme_constant(const StringName &p_name, const StringName &p_th } bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2583,6 +2699,7 @@ bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme } bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2599,6 +2716,7 @@ bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_t } bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2615,6 +2733,7 @@ bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme } bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2631,6 +2750,7 @@ bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_ } bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2647,6 +2767,7 @@ bool Control::has_theme_color(const StringName &p_name, const StringName &p_them } bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2665,6 +2786,7 @@ bool Control::has_theme_constant(const StringName &p_name, const StringName &p_t /// Local property overrides. void Control::add_theme_icon_override(const StringName &p_name, const Ref &p_icon) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!p_icon.is_valid()); if (data.theme_icon_override.has(p_name)) { @@ -2677,6 +2799,7 @@ void Control::add_theme_icon_override(const StringName &p_name, const Ref &p_style) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!p_style.is_valid()); if (data.theme_style_override.has(p_name)) { @@ -2689,6 +2812,7 @@ void Control::add_theme_style_override(const StringName &p_name, const Ref