From 6ebef31b3c0c26ee1275eb3e0a6f0f21a7fef494 Mon Sep 17 00:00:00 2001
From: Ryan <73148864+Ryan-000@users.noreply.github.com>
Date: Fri, 22 Aug 2025 23:02:51 -0400
Subject: [PATCH] Make Node::orphan_node_count thread-safe
---
doc/classes/Performance.xml | 1 +
main/performance.cpp | 12 +++++++++++-
main/performance.h | 1 +
scene/main/node.cpp | 16 +++++++++-------
scene/main/node.h | 4 +++-
5 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml
index 20f858e1e65..6925e3d70a8 100644
--- a/doc/classes/Performance.xml
+++ b/doc/classes/Performance.xml
@@ -154,6 +154,7 @@
Number of orphan nodes, i.e. nodes which are not parented to a node of the scene tree. [i]Lower is better.[/i]
+ [b]Note:[/b] This is only available in debug mode and will always return [code]0[/code] when used in a project exported in release mode.
The total number of objects in the last rendered frame. This metric doesn't include culled objects (either via hiding nodes, frustum culling or occlusion culling). [i]Lower is better.[/i]
diff --git a/main/performance.cpp b/main/performance.cpp
index 8f0bedbde67..3c355d01e17 100644
--- a/main/performance.cpp
+++ b/main/performance.cpp
@@ -141,6 +141,16 @@ int Performance::_get_node_count() const {
return sml->get_node_count();
}
+int Performance::_get_orphan_node_count() const {
+#ifdef DEBUG_ENABLED
+ const int total_node_count = Node::total_node_count.get();
+ const int orphan_node_count = total_node_count - _get_node_count();
+ return orphan_node_count;
+#else
+ return 0;
+#endif
+}
+
String Performance::get_monitor_name(Monitor p_monitor) const {
ERR_FAIL_INDEX_V(p_monitor, MONITOR_MAX, String());
static const char *names[MONITOR_MAX] = {
@@ -240,7 +250,7 @@ double Performance::get_monitor(Monitor p_monitor) const {
case OBJECT_NODE_COUNT:
return _get_node_count();
case OBJECT_ORPHAN_NODE_COUNT:
- return Node::orphan_node_count;
+ return _get_orphan_node_count();
case RENDER_TOTAL_OBJECTS_IN_FRAME:
return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TOTAL_OBJECTS_IN_FRAME);
case RENDER_TOTAL_PRIMITIVES_IN_FRAME:
diff --git a/main/performance.h b/main/performance.h
index 0fdac285cd7..cf822bbb706 100644
--- a/main/performance.h
+++ b/main/performance.h
@@ -46,6 +46,7 @@ class Performance : public Object {
static void _bind_methods();
int _get_node_count() const;
+ int _get_orphan_node_count() const;
double _process_time;
double _physics_process_time;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 9fe529155e5..a443033f420 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -44,7 +44,9 @@
#include "scene/resources/packed_scene.h"
#include "viewport.h"
-int Node::orphan_node_count = 0;
+#ifdef DEBUG_ENABLED
+SafeNumeric Node::total_node_count{ 0 };
+#endif
thread_local Node *Node::current_process_thread_group = nullptr;
@@ -165,7 +167,6 @@ void Node::_notification(int p_notification) {
}
data.tree->nodes_in_tree_count++;
- orphan_node_count--;
} break;
@@ -193,7 +194,6 @@ void Node::_notification(int p_notification) {
}
data.tree->nodes_in_tree_count--;
- orphan_node_count++;
if (data.input) {
remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id()));
@@ -4058,9 +4058,9 @@ String Node::_get_name_num_separator() {
Node::Node() {
_define_ancestry(AncestralClass::NODE);
-
- orphan_node_count++;
-
+#ifdef DEBUG_ENABLED
+ total_node_count.increment();
+#endif
// Default member initializer for bitfield is a C++20 extension, so:
data.process_mode = PROCESS_MODE_INHERIT;
@@ -4107,7 +4107,9 @@ Node::~Node() {
ERR_FAIL_COND(data.parent);
ERR_FAIL_COND(data.children_cache.size());
- orphan_node_count--;
+#ifdef DEBUG_ENABLED
+ total_node_count.decrement();
+#endif
}
////////////////////////////////
diff --git a/scene/main/node.h b/scene/main/node.h
index 35562339ab0..43076f46b08 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -130,7 +130,9 @@ public:
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
};
- static int orphan_node_count;
+#ifdef DEBUG_ENABLED
+ static SafeNumeric total_node_count;
+#endif
void _update_process(bool p_enable, bool p_for_children);