Optimize threading-sensitive node data for single-threaded processing
This commit is contained in:
parent
44cc0d2ec2
commit
50cf3d6966
8 changed files with 142 additions and 60 deletions
|
@ -50,11 +50,14 @@
|
|||
// value and, as an important benefit, you can be sure the value is properly synchronized
|
||||
// even with threads that are already running.
|
||||
|
||||
// This is used in very specific areas of the engine where it's critical that these guarantees are held
|
||||
// These are used in very specific areas of the engine where it's critical that these guarantees are held
|
||||
#define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \
|
||||
static_assert(sizeof(SafeNumeric<m_type>) == sizeof(m_type)); \
|
||||
static_assert(alignof(SafeNumeric<m_type>) == alignof(m_type)); \
|
||||
static_assert(std::is_trivially_destructible<std::atomic<m_type>>::value);
|
||||
#define SAFE_FLAG_TYPE_PUN_GUARANTEES \
|
||||
static_assert(sizeof(SafeFlag) == sizeof(bool)); \
|
||||
static_assert(alignof(SafeFlag) == alignof(bool));
|
||||
|
||||
template <class T>
|
||||
class SafeNumeric {
|
||||
|
|
|
@ -112,12 +112,24 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void Node2D::_update_xform_values() {
|
||||
void Node2D::_set_xform_dirty(bool p_dirty) const {
|
||||
if (is_group_processing()) {
|
||||
if (p_dirty) {
|
||||
xform_dirty.mt.set();
|
||||
} else {
|
||||
xform_dirty.mt.clear();
|
||||
}
|
||||
} else {
|
||||
xform_dirty.st = p_dirty;
|
||||
}
|
||||
}
|
||||
|
||||
void Node2D::_update_xform_values() const {
|
||||
rotation = transform.get_rotation();
|
||||
skew = transform.get_skew();
|
||||
position = transform.columns[2];
|
||||
scale = transform.get_scale();
|
||||
xform_dirty.clear();
|
||||
_set_xform_dirty(false);
|
||||
}
|
||||
|
||||
void Node2D::_update_transform() {
|
||||
|
@ -144,8 +156,8 @@ void Node2D::reparent(Node *p_parent, bool p_keep_global_transform) {
|
|||
|
||||
void Node2D::set_position(const Point2 &p_pos) {
|
||||
ERR_THREAD_GUARD;
|
||||
if (xform_dirty.is_set()) {
|
||||
const_cast<Node2D *>(this)->_update_xform_values();
|
||||
if (_is_xform_dirty()) {
|
||||
_update_xform_values();
|
||||
}
|
||||
position = p_pos;
|
||||
_update_transform();
|
||||
|
@ -153,8 +165,8 @@ void Node2D::set_position(const Point2 &p_pos) {
|
|||
|
||||
void Node2D::set_rotation(real_t p_radians) {
|
||||
ERR_THREAD_GUARD;
|
||||
if (xform_dirty.is_set()) {
|
||||
const_cast<Node2D *>(this)->_update_xform_values();
|
||||
if (_is_xform_dirty()) {
|
||||
_update_xform_values();
|
||||
}
|
||||
rotation = p_radians;
|
||||
_update_transform();
|
||||
|
@ -167,8 +179,8 @@ void Node2D::set_rotation_degrees(real_t p_degrees) {
|
|||
|
||||
void Node2D::set_skew(real_t p_radians) {
|
||||
ERR_THREAD_GUARD;
|
||||
if (xform_dirty.is_set()) {
|
||||
const_cast<Node2D *>(this)->_update_xform_values();
|
||||
if (_is_xform_dirty()) {
|
||||
_update_xform_values();
|
||||
}
|
||||
skew = p_radians;
|
||||
_update_transform();
|
||||
|
@ -176,8 +188,8 @@ void Node2D::set_skew(real_t p_radians) {
|
|||
|
||||
void Node2D::set_scale(const Size2 &p_scale) {
|
||||
ERR_THREAD_GUARD;
|
||||
if (xform_dirty.is_set()) {
|
||||
const_cast<Node2D *>(this)->_update_xform_values();
|
||||
if (_is_xform_dirty()) {
|
||||
_update_xform_values();
|
||||
}
|
||||
scale = p_scale;
|
||||
// Avoid having 0 scale values, can lead to errors in physics and rendering.
|
||||
|
@ -192,8 +204,8 @@ void Node2D::set_scale(const Size2 &p_scale) {
|
|||
|
||||
Point2 Node2D::get_position() const {
|
||||
ERR_READ_THREAD_GUARD_V(Point2());
|
||||
if (xform_dirty.is_set()) {
|
||||
const_cast<Node2D *>(this)->_update_xform_values();
|
||||
if (_is_xform_dirty()) {
|
||||
_update_xform_values();
|
||||
}
|
||||
|
||||
return position;
|
||||
|
@ -201,8 +213,8 @@ Point2 Node2D::get_position() const {
|
|||
|
||||
real_t Node2D::get_rotation() const {
|
||||
ERR_READ_THREAD_GUARD_V(0);
|
||||
if (xform_dirty.is_set()) {
|
||||
const_cast<Node2D *>(this)->_update_xform_values();
|
||||
if (_is_xform_dirty()) {
|
||||
_update_xform_values();
|
||||
}
|
||||
|
||||
return rotation;
|
||||
|
@ -215,8 +227,8 @@ real_t Node2D::get_rotation_degrees() const {
|
|||
|
||||
real_t Node2D::get_skew() const {
|
||||
ERR_READ_THREAD_GUARD_V(0);
|
||||
if (xform_dirty.is_set()) {
|
||||
const_cast<Node2D *>(this)->_update_xform_values();
|
||||
if (_is_xform_dirty()) {
|
||||
_update_xform_values();
|
||||
}
|
||||
|
||||
return skew;
|
||||
|
@ -224,8 +236,8 @@ real_t Node2D::get_skew() const {
|
|||
|
||||
Size2 Node2D::get_scale() const {
|
||||
ERR_READ_THREAD_GUARD_V(Size2());
|
||||
if (xform_dirty.is_set()) {
|
||||
const_cast<Node2D *>(this)->_update_xform_values();
|
||||
if (_is_xform_dirty()) {
|
||||
_update_xform_values();
|
||||
}
|
||||
|
||||
return scale;
|
||||
|
@ -362,7 +374,7 @@ 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.set();
|
||||
_set_xform_dirty(true);
|
||||
|
||||
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform);
|
||||
|
||||
|
|
|
@ -36,17 +36,20 @@
|
|||
class Node2D : public CanvasItem {
|
||||
GDCLASS(Node2D, CanvasItem);
|
||||
|
||||
SafeFlag xform_dirty;
|
||||
Point2 position;
|
||||
real_t rotation = 0.0;
|
||||
Size2 scale = Vector2(1, 1);
|
||||
real_t skew = 0.0;
|
||||
mutable MTFlag xform_dirty;
|
||||
mutable Point2 position;
|
||||
mutable real_t rotation = 0.0;
|
||||
mutable Size2 scale = Vector2(1, 1);
|
||||
mutable real_t skew = 0.0;
|
||||
|
||||
Transform2D transform;
|
||||
|
||||
_FORCE_INLINE_ bool _is_xform_dirty() const { return is_group_processing() ? xform_dirty.mt.is_set() : xform_dirty.st; }
|
||||
void _set_xform_dirty(bool p_dirty) const;
|
||||
|
||||
void _update_transform();
|
||||
|
||||
void _update_xform_values();
|
||||
void _update_xform_values() const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
|
|
|
@ -87,7 +87,7 @@ 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.bit_and(~DIRTY_LOCAL_TRANSFORM);
|
||||
_clear_dirty_bits(DIRTY_LOCAL_TRANSFORM);
|
||||
}
|
||||
|
||||
void Node3D::_update_rotation_and_scale() const {
|
||||
|
@ -95,7 +95,7 @@ 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);
|
||||
_clear_dirty_bits(DIRTY_EULER_ROTATION_AND_SCALE);
|
||||
}
|
||||
|
||||
void Node3D::_propagate_transform_changed_deferred() {
|
||||
|
@ -127,7 +127,7 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) {
|
|||
MessageQueue::get_singleton()->push_callable(callable_mp(this, &Node3D::_propagate_transform_changed_deferred));
|
||||
}
|
||||
}
|
||||
data.dirty.bit_or(DIRTY_GLOBAL_TRANSFORM);
|
||||
_set_dirty_bits(DIRTY_GLOBAL_TRANSFORM);
|
||||
}
|
||||
|
||||
void Node3D::_notification(int p_what) {
|
||||
|
@ -151,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.set(DIRTY_EULER_ROTATION_AND_SCALE); // As local transform was updated, rot/scale should be dirty.
|
||||
_replace_dirty_mask(DIRTY_EULER_ROTATION_AND_SCALE); // As local transform was updated, rot/scale should be dirty.
|
||||
}
|
||||
data.top_level_active = true;
|
||||
}
|
||||
|
||||
data.dirty.bit_or(DIRTY_GLOBAL_TRANSFORM); // Global is always dirty upon entering a scene.
|
||||
_set_dirty_bits(DIRTY_GLOBAL_TRANSFORM); // Global is always dirty upon entering a scene.
|
||||
_notify_dirty();
|
||||
|
||||
notification(NOTIFICATION_ENTER_WORLD);
|
||||
|
@ -230,16 +230,16 @@ void Node3D::set_basis(const Basis &p_basis) {
|
|||
void Node3D::set_quaternion(const Quaternion &p_quaternion) {
|
||||
ERR_THREAD_GUARD;
|
||||
|
||||
if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) {
|
||||
if (_test_dirty_bits(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.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE);
|
||||
_clear_dirty_bits(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.set(DIRTY_NONE);
|
||||
_replace_dirty_mask(DIRTY_NONE);
|
||||
|
||||
_propagate_transform_changed(this);
|
||||
if (data.notify_local_transform) {
|
||||
|
@ -286,7 +286,7 @@ void Node3D::set_global_rotation_degrees(const Vector3 &p_euler_degrees) {
|
|||
void Node3D::set_transform(const Transform3D &p_transform) {
|
||||
ERR_THREAD_GUARD;
|
||||
data.local_transform = p_transform;
|
||||
data.dirty.set(DIRTY_EULER_ROTATION_AND_SCALE); // Make rot/scale dirty.
|
||||
_replace_dirty_mask(DIRTY_EULER_ROTATION_AND_SCALE); // Make rot/scale dirty.
|
||||
|
||||
_propagate_transform_changed(this);
|
||||
if (data.notify_local_transform) {
|
||||
|
@ -314,7 +314,7 @@ void Node3D::set_global_transform(const Transform3D &p_transform) {
|
|||
|
||||
Transform3D Node3D::get_transform() const {
|
||||
ERR_READ_THREAD_GUARD_V(Transform3D());
|
||||
if (data.dirty.get() & DIRTY_LOCAL_TRANSFORM) {
|
||||
if (_test_dirty_bits(DIRTY_LOCAL_TRANSFORM)) {
|
||||
// This update can happen if needed over multiple threads.
|
||||
_update_local_transform();
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ Transform3D Node3D::get_global_transform() const {
|
|||
* the dirty/update process is thread safe by utilizing atomic copies.
|
||||
*/
|
||||
|
||||
uint32_t dirty = data.dirty.get();
|
||||
uint32_t dirty = _read_dirty_mask();
|
||||
if (dirty & DIRTY_GLOBAL_TRANSFORM) {
|
||||
if (dirty & DIRTY_LOCAL_TRANSFORM) {
|
||||
_update_local_transform(); // Update local transform atomically.
|
||||
|
@ -348,7 +348,7 @@ Transform3D Node3D::get_global_transform() const {
|
|||
}
|
||||
|
||||
data.global_transform = new_global;
|
||||
data.dirty.bit_and(~DIRTY_GLOBAL_TRANSFORM);
|
||||
_clear_dirty_bits(DIRTY_GLOBAL_TRANSFORM);
|
||||
}
|
||||
|
||||
return data.global_transform;
|
||||
|
@ -404,14 +404,14 @@ void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) {
|
|||
}
|
||||
|
||||
bool transform_changed = false;
|
||||
if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && !(data.dirty.get() & DIRTY_LOCAL_TRANSFORM)) {
|
||||
if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && !_test_dirty_bits(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.get() & DIRTY_EULER_ROTATION_AND_SCALE)) {
|
||||
if (p_mode == ROTATION_EDIT_MODE_EULER && _test_dirty_bits(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.
|
||||
|
||||
|
@ -442,13 +442,14 @@ void Node3D::set_rotation_order(EulerOrder p_order) {
|
|||
ERR_FAIL_INDEX(int32_t(p_order), 6);
|
||||
bool transform_changed = false;
|
||||
|
||||
if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) {
|
||||
uint32_t dirty = _read_dirty_mask();
|
||||
if ((dirty & DIRTY_EULER_ROTATION_AND_SCALE)) {
|
||||
_update_rotation_and_scale();
|
||||
} else if (data.dirty.get() & DIRTY_LOCAL_TRANSFORM) {
|
||||
} else if ((dirty & 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.bit_or(DIRTY_LOCAL_TRANSFORM);
|
||||
_set_dirty_bits(DIRTY_LOCAL_TRANSFORM);
|
||||
transform_changed = true;
|
||||
}
|
||||
|
||||
|
@ -470,14 +471,14 @@ EulerOrder Node3D::get_rotation_order() const {
|
|||
|
||||
void Node3D::set_rotation(const Vector3 &p_euler_rad) {
|
||||
ERR_THREAD_GUARD;
|
||||
if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) {
|
||||
if (_test_dirty_bits(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.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE);
|
||||
_clear_dirty_bits(DIRTY_EULER_ROTATION_AND_SCALE);
|
||||
}
|
||||
|
||||
data.euler_rotation = p_euler_rad;
|
||||
data.dirty.set(DIRTY_LOCAL_TRANSFORM);
|
||||
_replace_dirty_mask(DIRTY_LOCAL_TRANSFORM);
|
||||
_propagate_transform_changed(this);
|
||||
if (data.notify_local_transform) {
|
||||
notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
|
||||
|
@ -492,14 +493,14 @@ void Node3D::set_rotation_degrees(const Vector3 &p_euler_degrees) {
|
|||
|
||||
void Node3D::set_scale(const Vector3 &p_scale) {
|
||||
ERR_THREAD_GUARD;
|
||||
if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) {
|
||||
if (_test_dirty_bits(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.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE);
|
||||
_clear_dirty_bits(DIRTY_EULER_ROTATION_AND_SCALE);
|
||||
}
|
||||
|
||||
data.scale = p_scale;
|
||||
data.dirty.set(DIRTY_LOCAL_TRANSFORM);
|
||||
_replace_dirty_mask(DIRTY_LOCAL_TRANSFORM);
|
||||
_propagate_transform_changed(this);
|
||||
if (data.notify_local_transform) {
|
||||
notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
|
||||
|
@ -513,7 +514,7 @@ Vector3 Node3D::get_position() const {
|
|||
|
||||
Vector3 Node3D::get_rotation() const {
|
||||
ERR_READ_THREAD_GUARD_V(Vector3());
|
||||
if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) {
|
||||
if (_test_dirty_bits(DIRTY_EULER_ROTATION_AND_SCALE)) {
|
||||
_update_rotation_and_scale();
|
||||
}
|
||||
|
||||
|
@ -528,7 +529,7 @@ Vector3 Node3D::get_rotation_degrees() const {
|
|||
|
||||
Vector3 Node3D::get_scale() const {
|
||||
ERR_READ_THREAD_GUARD_V(Vector3());
|
||||
if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) {
|
||||
if (_test_dirty_bits(DIRTY_EULER_ROTATION_AND_SCALE)) {
|
||||
_update_rotation_and_scale();
|
||||
}
|
||||
|
||||
|
@ -645,6 +646,30 @@ Vector<Ref<Node3DGizmo>> Node3D::get_gizmos() const {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Node3D::_replace_dirty_mask(uint32_t p_mask) const {
|
||||
if (is_group_processing()) {
|
||||
data.dirty.mt.set(p_mask);
|
||||
} else {
|
||||
data.dirty.st = p_mask;
|
||||
}
|
||||
}
|
||||
|
||||
void Node3D::_set_dirty_bits(uint32_t p_bits) const {
|
||||
if (is_group_processing()) {
|
||||
data.dirty.mt.bit_or(p_bits);
|
||||
} else {
|
||||
data.dirty.st |= p_bits;
|
||||
}
|
||||
}
|
||||
|
||||
void Node3D::_clear_dirty_bits(uint32_t p_bits) const {
|
||||
if (is_group_processing()) {
|
||||
data.dirty.mt.bit_and(~p_bits);
|
||||
} else {
|
||||
data.dirty.st &= ~p_bits;
|
||||
}
|
||||
}
|
||||
|
||||
void Node3D::_update_gizmos() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (data.gizmos_disabled || !is_inside_world() || !data.gizmos_dirty) {
|
||||
|
|
|
@ -97,7 +97,7 @@ private:
|
|||
mutable Vector3 scale = Vector3(1, 1, 1);
|
||||
mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER;
|
||||
|
||||
mutable SafeNumeric<uint32_t> dirty;
|
||||
mutable MTNumeric<uint32_t> dirty;
|
||||
|
||||
Viewport *viewport = nullptr;
|
||||
|
||||
|
@ -129,6 +129,12 @@ private:
|
|||
|
||||
NodePath visibility_parent_path;
|
||||
|
||||
_FORCE_INLINE_ uint32_t _read_dirty_mask() const { return is_group_processing() ? data.dirty.mt.get() : data.dirty.st; }
|
||||
_FORCE_INLINE_ bool _test_dirty_bits(uint32_t p_bits) const { return is_group_processing() ? data.dirty.mt.bit_and(p_bits) : (data.dirty.st & p_bits); }
|
||||
void _replace_dirty_mask(uint32_t p_mask) const;
|
||||
void _set_dirty_bits(uint32_t p_bits) const;
|
||||
void _clear_dirty_bits(uint32_t p_bits) const;
|
||||
|
||||
void _update_gizmos();
|
||||
void _notify_dirty();
|
||||
void _propagate_transform_changed(Node3D *p_origin);
|
||||
|
|
|
@ -148,7 +148,7 @@ void CanvasItem::_redraw_callback() {
|
|||
}
|
||||
|
||||
void CanvasItem::_invalidate_global_transform() {
|
||||
global_invalid.set();
|
||||
_set_global_invalid(true);
|
||||
}
|
||||
|
||||
Transform2D CanvasItem::get_global_transform_with_canvas() const {
|
||||
|
@ -171,7 +171,7 @@ Transform2D CanvasItem::get_screen_transform() const {
|
|||
Transform2D CanvasItem::get_global_transform() const {
|
||||
ERR_READ_THREAD_GUARD_V(Transform2D());
|
||||
|
||||
if (global_invalid.is_set()) {
|
||||
if (_is_global_invalid()) {
|
||||
// This code can enter multiple times from threads if dirty, this is expected.
|
||||
const CanvasItem *pi = get_parent_item();
|
||||
Transform2D new_global;
|
||||
|
@ -182,12 +182,24 @@ Transform2D CanvasItem::get_global_transform() const {
|
|||
}
|
||||
|
||||
global_transform = new_global;
|
||||
global_invalid.clear();
|
||||
_set_global_invalid(false);
|
||||
}
|
||||
|
||||
return global_transform;
|
||||
}
|
||||
|
||||
void CanvasItem::_set_global_invalid(bool p_invalid) const {
|
||||
if (is_group_processing()) {
|
||||
if (p_invalid) {
|
||||
global_invalid.mt.set();
|
||||
} else {
|
||||
global_invalid.mt.clear();
|
||||
}
|
||||
} else {
|
||||
global_invalid.st = p_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasItem::_top_level_raise_self() {
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
|
@ -308,7 +320,7 @@ void CanvasItem::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
global_invalid.set();
|
||||
_set_global_invalid(true);
|
||||
_enter_canvas();
|
||||
|
||||
RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, is_visible_in_tree()); // The visibility of the parent may change.
|
||||
|
@ -341,7 +353,7 @@ void CanvasItem::_notification(int p_what) {
|
|||
window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed));
|
||||
window = nullptr;
|
||||
}
|
||||
global_invalid.set();
|
||||
_set_global_invalid(true);
|
||||
parent_visible_in_tree = false;
|
||||
|
||||
if (get_viewport()) {
|
||||
|
@ -869,11 +881,11 @@ void CanvasItem::_notify_transform(CanvasItem *p_node) {
|
|||
* notification anyway).
|
||||
*/
|
||||
|
||||
if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid.is_set()) {
|
||||
if (/*p_node->xform_change.in_list() &&*/ p_node->_is_global_invalid()) {
|
||||
return; //nothing to do
|
||||
}
|
||||
|
||||
p_node->global_invalid.set();
|
||||
p_node->_set_global_invalid(true);
|
||||
|
||||
if (p_node->notify_transform && !p_node->xform_change.in_list()) {
|
||||
if (!p_node->block_transform_notify) {
|
||||
|
|
|
@ -118,7 +118,10 @@ private:
|
|||
Ref<Material> material;
|
||||
|
||||
mutable Transform2D global_transform;
|
||||
mutable SafeFlag global_invalid;
|
||||
mutable MTFlag global_invalid;
|
||||
|
||||
_FORCE_INLINE_ bool _is_global_invalid() const { return is_group_processing() ? global_invalid.mt.is_set() : global_invalid.st; }
|
||||
void _set_global_invalid(bool p_invalid) const;
|
||||
|
||||
void _top_level_raise_self();
|
||||
|
||||
|
|
|
@ -42,9 +42,25 @@ class SceneState;
|
|||
class Tween;
|
||||
class PropertyTweener;
|
||||
|
||||
SAFE_FLAG_TYPE_PUN_GUARANTEES
|
||||
SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
|
||||
|
||||
class Node : public Object {
|
||||
GDCLASS(Node, Object);
|
||||
|
||||
protected:
|
||||
// During group processing, these are thread-safe.
|
||||
// Outside group processing, these avoid the cost of sync by working as plain primitive types.
|
||||
union MTFlag {
|
||||
SafeFlag mt{};
|
||||
bool st;
|
||||
};
|
||||
template <class T>
|
||||
union MTNumeric {
|
||||
SafeNumeric<T> mt{};
|
||||
T st;
|
||||
};
|
||||
|
||||
public:
|
||||
enum ProcessMode {
|
||||
PROCESS_MODE_INHERIT, // same as parent node
|
||||
|
@ -538,6 +554,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ static bool is_group_processing() { return current_process_thread_group; }
|
||||
|
||||
void set_process_thread_messages(BitField<ProcessThreadMessages> p_flags);
|
||||
BitField<ProcessThreadMessages> get_process_thread_messages() const;
|
||||
|
||||
|
|
Loading…
Reference in a new issue