FTI - Optimize SceneTree traversal

This commit is contained in:
lawnjelly
2025-05-10 11:47:02 +01:00
parent 7a0ab9d561
commit c7764ef26b
8 changed files with 713 additions and 48 deletions

View File

@ -36,6 +36,12 @@
class Node3D;
class Node;
struct Transform3D;
class SceneTreeFTITests;
#ifdef DEV_ENABLED
// Uncomment this to verify traversal method results.
// #define GODOT_SCENE_TREE_FTI_VERIFY
#endif
#ifdef _3D_DISABLED
// Stubs
@ -62,19 +68,37 @@ public:
// but this covers the custom case of multiple scene trees.
class SceneTreeFTI {
friend class SceneTreeFTITests;
enum TraversalMode : unsigned {
TM_DEFAULT,
TM_LEGACY,
TM_DEBUG,
};
struct Data {
static const uint32_t scene_tree_depth_limit = 32;
// Prev / Curr lists of Node3Ds having local xforms pumped.
LocalVector<Node3D *> tick_xform_list[2];
// The frame lists are changed nodes that need to start traversal,
// either longterm (on the tick list) or single frame forced.
LocalVector<Node3D *> frame_xform_list;
LocalVector<Node3D *> frame_xform_list_forced;
// Prev / Curr lists of Node3Ds having actively interpolated properties.
LocalVector<Node3D *> tick_property_list[2];
LocalVector<Node3D *> frame_property_list;
LocalVector<Node3D *> request_reset_list;
LocalVector<Node3D *> dirty_node_depth_lists[scene_tree_depth_limit];
// When we are using two alternating lists,
// which one is current.
uint32_t mirror = 0;
// Global on / off switch for SceneTreeFTI.
bool enabled = false;
// Whether we are in physics ticks, or in a frame.
@ -85,10 +109,21 @@ class SceneTreeFTI {
Mutex mutex;
bool debug = false;
TraversalMode traversal_mode = TM_DEFAULT;
bool use_optimized_traversal_method = true;
// DEBUGGING
bool periodic_debug_log = false;
uint32_t debug_node_count = 0;
uint32_t debug_nodes_processed = 0;
} data;
void _update_dirty_nodes(Node *p_node, uint32_t p_current_frame, float p_interpolation_fraction, bool p_active, const Transform3D *p_parent_global_xform = nullptr, int p_depth = 0);
#ifdef GODOT_SCENE_TREE_FTI_VERIFY
SceneTreeFTITests *_tests = nullptr;
#endif
void _update_dirty_nodes(Node *p_node, uint32_t p_current_half_frame, float p_interpolation_fraction, bool p_active, const Transform3D *p_parent_global_xform = nullptr, int p_depth = 0);
void _update_request_resets();
void _reset_flags(Node *p_node);
@ -96,6 +131,12 @@ class SceneTreeFTI {
void _node_3d_notify_set_xform(Node3D &r_node);
void _node_3d_notify_set_property(Node3D &r_node);
void _node_add_to_frame_list(Node3D &r_node, bool p_forced);
void _node_remove_from_frame_list(Node3D &r_node, bool p_forced);
void _create_depth_lists();
void _clear_depth_lists();
public:
// Hottest function, allow inlining the data.enabled check.
void node_3d_notify_changed(Node3D &r_node, bool p_transform_changed) {
@ -123,7 +164,10 @@ public:
void set_enabled(Node *p_root, bool p_enabled);
bool is_enabled() const { return data.enabled; }
void set_debug_next_frame() { data.debug = true; }
void set_debug_next_frame() { data.periodic_debug_log = true; }
SceneTreeFTI();
~SceneTreeFTI();
};
#endif // ndef _3D_DISABLED