Fix #143697: UI freezes with lots of Action slots (in the Outliner)
Only create an outliner tree element for all Action slots in specific modes of the outliner. The "object-hierarchical" modes (Scene, View Layer) show the Action underneath each user of that Action; in these cases, only the assigned slot is shown now. Previously all slots would be shown, in all modes of the Outliner. This meant quadratic complexity, as each Action is included in the tree for each of its users, and then each of those tree items would show all slots. This means the number of tree items for the slots was multiplied by the number of users. Pull Request: https://projects.blender.org/blender/blender/pulls/144864
This commit is contained in:
@@ -45,6 +45,11 @@ void TreeElementAnimData::expand(SpaceOutliner & /*space_outliner*/) const
|
||||
expand_NLA_tracks();
|
||||
}
|
||||
|
||||
animrig::slot_handle_t TreeElementAnimData::get_slot_handle() const
|
||||
{
|
||||
return this->anim_data_.slot_handle;
|
||||
}
|
||||
|
||||
void TreeElementAnimData::expand_drivers() const
|
||||
{
|
||||
if (BLI_listbase_is_empty(&anim_data_.drivers)) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "tree_element.hh"
|
||||
|
||||
#include "BKE_action.hh"
|
||||
|
||||
struct AnimData;
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
@@ -22,6 +24,8 @@ class TreeElementAnimData final : public AbstractTreeElement {
|
||||
|
||||
void expand(SpaceOutliner &space_outliner) const override;
|
||||
|
||||
animrig::slot_handle_t get_slot_handle() const;
|
||||
|
||||
private:
|
||||
void expand_drivers() const;
|
||||
void expand_NLA_tracks() const;
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
#include "DNA_action_types.h"
|
||||
#include "DNA_outliner_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "../outliner_intern.hh"
|
||||
|
||||
#include "tree_element_anim_data.hh"
|
||||
#include "tree_element_id_action.hh"
|
||||
|
||||
#include "ANIM_action.hh"
|
||||
@@ -20,19 +22,63 @@ namespace blender::ed::outliner {
|
||||
TreeElementIDAction::TreeElementIDAction(TreeElement &legacy_te, bAction &action)
|
||||
: TreeElementID(legacy_te, action.id), action_(action)
|
||||
{
|
||||
/* Fetch the assigned slot handle from the parent node in the tree. This is done this way,
|
||||
* because AbstractTreeElement::add_element() constructs the element and immediately calls its
|
||||
* expand() function. That means that there is no time for the creator of this
|
||||
* TreeElementIDAction to pass us the slot handle explicitly.
|
||||
*
|
||||
* Adding a constructor parameter for this is also not feasible, due to the generic nature of the
|
||||
* code that constructs this TreeElement. */
|
||||
const TreeElement *legacy_parent = legacy_te.parent;
|
||||
if (!legacy_parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const TreeElementAnimData *parent_anim_te = dynamic_cast<const TreeElementAnimData *>(
|
||||
legacy_parent->abstract_element.get());
|
||||
if (!parent_anim_te) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->slot_handle_ = parent_anim_te->get_slot_handle();
|
||||
}
|
||||
|
||||
void TreeElementIDAction::expand(SpaceOutliner & /* space_outliner */) const
|
||||
void TreeElementIDAction::expand(SpaceOutliner &space_outliner) const
|
||||
{
|
||||
blender::animrig::Action &action = action_.wrap();
|
||||
for (blender::animrig::Slot *slot : action.slots()) {
|
||||
add_element(&legacy_te_.subtree,
|
||||
reinterpret_cast<ID *>(&action_),
|
||||
slot,
|
||||
&legacy_te_,
|
||||
TSE_ACTION_SLOT,
|
||||
0);
|
||||
/* If the outliner is showing the Action because it's in some hierarchical data mode, only show
|
||||
* the slot that is used by the parent ID tree element. Showing all slots will create quadratic
|
||||
* complexity, as each user of the Action has a child tree element for the Action. This means the
|
||||
* complexity is O(U x S), where U = the number of users of the Action, and S = the number of
|
||||
* slots. Typically U = S. */
|
||||
|
||||
const bool may_show_all_slots = ELEM(
|
||||
space_outliner.outlinevis, SO_SEQUENCE, SO_LIBRARIES, SO_ID_ORPHANS, SO_OVERRIDES_LIBRARY);
|
||||
|
||||
animrig::Action &action = action_.wrap();
|
||||
if (may_show_all_slots) {
|
||||
/* Show all slots of the Action. */
|
||||
for (animrig::Slot *slot : action.slots()) {
|
||||
add_element(&legacy_te_.subtree,
|
||||
reinterpret_cast<ID *>(&action_),
|
||||
slot,
|
||||
&legacy_te_,
|
||||
TSE_ACTION_SLOT,
|
||||
0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Only show a single slot. */
|
||||
animrig::Slot *slot = action.slot_for_handle(this->slot_handle_);
|
||||
if (!slot) {
|
||||
return;
|
||||
}
|
||||
add_element(&legacy_te_.subtree,
|
||||
reinterpret_cast<ID *>(&action_),
|
||||
slot,
|
||||
&legacy_te_,
|
||||
TSE_ACTION_SLOT,
|
||||
0);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::outliner
|
||||
|
||||
@@ -10,16 +10,25 @@
|
||||
|
||||
#include "tree_element_id.hh"
|
||||
|
||||
#include "ANIM_action.hh"
|
||||
|
||||
struct bAction;
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
class TreeElementIDAction final : public TreeElementID {
|
||||
bAction &action_;
|
||||
animrig::slot_handle_t slot_handle_ = animrig::Slot::unassigned;
|
||||
|
||||
public:
|
||||
TreeElementIDAction(TreeElement &legacy_te, bAction &action);
|
||||
|
||||
/**
|
||||
* When displaying this tree element in a "flat" tree view (so each Action is
|
||||
* only listed once, like in the Blender File outliner mode), this expands to
|
||||
* show all the Action's slots Otherwise, when using a data-hierarchical tree
|
||||
* view (like Scene or View Layer), only the assigned slot is shown.
|
||||
*/
|
||||
void expand(SpaceOutliner &space_outliner) const override;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user