Improve Scene Tree editor performance
We now cache the Node*<>TreeItem* mapping in the SceneTreeEditor. This allows us to make targeted updates to the Tree used to display the scene tree in the editor. Previously on almost all changes to the scene tree the editor would rebuild the entire widget, causing a large number of deallocations an allocations. We now carefully manipulate the Tree widget in-situ saving a large number of these allocations. In order to know what Nodes need to be updated we add a editor_state_changed signal to Node, this is a TOOLS_ENABLED, editor-only signal fired when changes to Node happen that are relevant to editor state. We also now make sure that when nodes are moved/renamed we don't check expensive properties that cannot contain NodePaths. This saves a lot of time when SceneTreeDock renames a node in a scene with a lot of MeshInstances. This makes renaming nodes go from ~27 seconds to ~2 seconds on large scenes. SceneTreeEditor instances will now also not do all of the potentially expensive update work if they are invisible. This behavior is turned off by default so it won't affect existing users. This change allows the editor to only update SceneTreeEditors that actually in view. In practice this means that for most changes instead of updating 6 SceneTreeEditors we only update 1 instantly, and the others only when they become visible. There is definitely more that could be done, but this is already a massive improvement. In complex scenes we see an improvement of 10x, things that used to take ~30 seconds now only take 2. This fixes #83460 I want to thank KoBeWi, TokisanGames, a-johnston, aniel080400 for their tireless testing. And AeioMuch for their testing and providing a fix for the hover issue.
This commit is contained in:
parent
44dfa7e710
commit
6f7525c396
15 changed files with 818 additions and 181 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -58,6 +58,57 @@ class SceneTreeEditor : public Control {
|
|||
BUTTON_UNIQUE = 9,
|
||||
};
|
||||
|
||||
struct CachedNode {
|
||||
Node *node = nullptr;
|
||||
TreeItem *item = nullptr;
|
||||
int index = -1;
|
||||
bool dirty = true;
|
||||
bool has_moved_children = false;
|
||||
bool removed = false;
|
||||
|
||||
// Store the iterator for faster removal. This is safe as
|
||||
// HashMap never moves elements.
|
||||
HashMap<Node *, CachedNode>::Iterator cache_iterator;
|
||||
// This is safe because it gets compared to a uint8_t.
|
||||
uint16_t delete_serial = UINT16_MAX;
|
||||
|
||||
// To know whether to update children or not.
|
||||
bool can_process = false;
|
||||
|
||||
CachedNode() = delete; // Always an error.
|
||||
CachedNode(Node *p_node, TreeItem *p_item) :
|
||||
node(p_node), item(p_item) {}
|
||||
};
|
||||
|
||||
struct NodeCache {
|
||||
~NodeCache() {
|
||||
clear();
|
||||
}
|
||||
|
||||
NodeCache(SceneTreeEditor *p_editor) :
|
||||
editor(p_editor) {}
|
||||
|
||||
HashMap<Node *, CachedNode>::Iterator add(Node *p_node, TreeItem *p_item);
|
||||
HashMap<Node *, CachedNode>::Iterator get(Node *p_node, bool p_deleted_ok = true);
|
||||
void remove(Node *p_node, bool p_recursive = false);
|
||||
void mark_dirty(Node *p_node, bool p_parents = true);
|
||||
void mark_children_dirty(Node *p_node, bool p_recursive = false);
|
||||
|
||||
void delete_pending();
|
||||
void clear();
|
||||
|
||||
SceneTreeEditor *editor;
|
||||
HashMap<Node *, CachedNode> cache;
|
||||
HashSet<CachedNode *> to_delete;
|
||||
Node *current_scene_node = nullptr;
|
||||
Node *current_pinned_node = nullptr;
|
||||
bool current_has_pin = false;
|
||||
bool force_update = false;
|
||||
uint8_t delete_serial = 0;
|
||||
};
|
||||
|
||||
NodeCache node_cache;
|
||||
|
||||
Tree *tree = nullptr;
|
||||
Node *selected = nullptr;
|
||||
ObjectID instance_node;
|
||||
|
|
@ -77,17 +128,30 @@ class SceneTreeEditor : public Control {
|
|||
bool auto_expand_selected = true;
|
||||
bool connect_to_script_mode = false;
|
||||
bool connecting_signal = false;
|
||||
bool update_when_invisible = true;
|
||||
|
||||
int blocked;
|
||||
|
||||
void _compute_hash(Node *p_node, uint64_t &hash);
|
||||
void _reset();
|
||||
|
||||
void _update_node_path(Node *p_node, bool p_recursive = true);
|
||||
void _update_node_subtree(Node *p_node, TreeItem *p_parent, bool p_force = false);
|
||||
void _update_node(Node *p_node, TreeItem *p_item, bool p_part_of_subscene);
|
||||
void _update_if_clean();
|
||||
|
||||
void _add_nodes(Node *p_node, TreeItem *p_parent);
|
||||
void _test_update_tree();
|
||||
bool _update_filter(TreeItem *p_parent = nullptr, bool p_scroll_to_selected = false);
|
||||
bool _item_matches_all_terms(TreeItem *p_item, const PackedStringArray &p_terms);
|
||||
void _tree_changed();
|
||||
void _tree_process_mode_changed();
|
||||
|
||||
void _move_node_children(HashMap<Node *, CachedNode>::Iterator &p_I);
|
||||
void _move_node_item(TreeItem *p_parent, HashMap<Node *, CachedNode>::Iterator &p_I);
|
||||
|
||||
void _node_child_order_changed(Node *p_node);
|
||||
void _node_editor_state_changed(Node *p_node);
|
||||
void _node_added(Node *p_node);
|
||||
void _node_removed(Node *p_node);
|
||||
void _node_renamed(Node *p_node);
|
||||
|
||||
|
|
@ -142,6 +206,7 @@ class SceneTreeEditor : public Control {
|
|||
void _rmb_select(const Vector2 &p_pos, MouseButton p_button = MouseButton::RIGHT);
|
||||
|
||||
void _warning_changed(Node *p_for_node);
|
||||
void _update_marking_list(const HashSet<Node *> &p_marked);
|
||||
|
||||
Timer *update_timer = nullptr;
|
||||
|
||||
|
|
@ -182,6 +247,7 @@ public:
|
|||
void set_auto_expand_selected(bool p_auto, bool p_update_settings);
|
||||
void set_connect_to_script_mode(bool p_enable);
|
||||
void set_connecting_signal(bool p_enable);
|
||||
void set_update_when_invisible(bool p_enable);
|
||||
|
||||
Tree *get_scene_tree() { return tree; }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue