Fix #120071: quadratic complexity for dangling reroute check
To know if link is connected to dangling reroute and can be skipped as value-less, we need to know if reroute is dangling. This requires graph traversal. Currently this is done by non-recursive iteration. But this can lead quadratic complexity for some of the cases. Other way is to make this linear while cache building. Pull Request: https://projects.blender.org/blender/blender/pulls/120375
This commit is contained in:
committed by
Jacques Lucke
parent
2b595e7bcc
commit
942ca6be6d
@@ -300,6 +300,10 @@ class bNodeRuntime : NonCopyable, NonMovable {
|
||||
/** Used to avoid running forward compatibility code more often than necessary. */
|
||||
bool forward_compatible_versioning_done = false;
|
||||
|
||||
/** If this node is reroute and this reroute is not logically linked with any source except other
|
||||
* reroute, this will be true. */
|
||||
bool is_dangling_reroute = false;
|
||||
|
||||
/** Only valid if #topology_cache_is_dirty is false. */
|
||||
Vector<bNodeSocket *> inputs;
|
||||
Vector<bNodeSocket *> outputs;
|
||||
@@ -731,6 +735,12 @@ inline blender::Span<bNodeLink> bNode::internal_links() const
|
||||
return this->runtime->internal_links;
|
||||
}
|
||||
|
||||
inline bool bNode::is_dangling_reroute() const
|
||||
{
|
||||
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
|
||||
return this->runtime->is_dangling_reroute;
|
||||
}
|
||||
|
||||
inline bool bNode::is_socket_drawn(const bNodeSocket &socket) const
|
||||
{
|
||||
return socket.is_visible();
|
||||
|
||||
@@ -2528,37 +2528,6 @@ void nodeParentsIter(bNode *node, bool (*callback)(bNode *, void *), void *userd
|
||||
}
|
||||
}
|
||||
|
||||
bool nodeIsDanglingReroute(const bNodeTree *ntree, const bNode *node)
|
||||
{
|
||||
ntree->ensure_topology_cache();
|
||||
BLI_assert(node_tree_runtime::topology_cache_is_available(*ntree));
|
||||
|
||||
const bNode *iter_node = node;
|
||||
Set<const bNode *> visited_nodes;
|
||||
while (true) {
|
||||
if (!iter_node->is_reroute()) {
|
||||
return false;
|
||||
}
|
||||
if (!visited_nodes.add(iter_node)) {
|
||||
/* Treat cycle of reroute as dangling reroute branch. */
|
||||
return true;
|
||||
}
|
||||
const Span<const bNodeLink *> links = iter_node->input_socket(0).directly_linked_links();
|
||||
BLI_assert(links.size() <= 1);
|
||||
if (links.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
const bNodeLink &link = *links[0];
|
||||
if (!link.is_available()) {
|
||||
return false;
|
||||
}
|
||||
if (link.is_muted()) {
|
||||
return false;
|
||||
}
|
||||
iter_node = link.fromnode;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
void nodeUniqueName(bNodeTree *ntree, bNode *node)
|
||||
|
||||
@@ -509,6 +509,25 @@ static void update_group_output_node(const bNodeTree &ntree)
|
||||
}
|
||||
}
|
||||
|
||||
static void update_dangling_reroute_nodes(const bNodeTree &ntree)
|
||||
{
|
||||
for (const bNode *node : ntree.runtime->toposort_left_to_right) {
|
||||
bNodeRuntime &node_runtime = *node->runtime;
|
||||
if (!node->is_reroute()) {
|
||||
node_runtime.is_dangling_reroute = false;
|
||||
continue;
|
||||
}
|
||||
const Span<const bNodeLink *> links = node_runtime.inputs[0]->runtime->directly_linked_links;
|
||||
if (links.is_empty()) {
|
||||
node_runtime.is_dangling_reroute = true;
|
||||
continue;
|
||||
}
|
||||
BLI_assert(links.size() == 1);
|
||||
const bNode &source_node = *links.first()->fromnode;
|
||||
node_runtime.is_dangling_reroute = source_node.runtime->is_dangling_reroute;
|
||||
}
|
||||
}
|
||||
|
||||
static void ensure_topology_cache(const bNodeTree &ntree)
|
||||
{
|
||||
bNodeTreeRuntime &tree_runtime = *ntree.runtime;
|
||||
@@ -546,6 +565,7 @@ static void ensure_topology_cache(const bNodeTree &ntree)
|
||||
[&]() { update_root_frames(ntree); },
|
||||
[&]() { update_direct_frames_childrens(ntree); });
|
||||
update_group_output_node(ntree);
|
||||
update_dangling_reroute_nodes(ntree);
|
||||
tree_runtime.topology_cache_exists = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -625,7 +625,7 @@ class NodeTreeMainUpdater {
|
||||
const Span<const bNodeLink *> links) const
|
||||
{
|
||||
for (const bNodeLink *link : links) {
|
||||
if (!bke::nodeIsDanglingReroute(&ntree, link->fromnode)) {
|
||||
if (!link->fromnode->is_dangling_reroute()) {
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,7 @@ static bool update_zone_per_node(const Span<const bNode *> all_nodes,
|
||||
|
||||
static void update_zone_border_links(const bNodeTree &tree, bNodeTreeZones &tree_zones)
|
||||
{
|
||||
tree.ensure_topology_cache();
|
||||
for (const bNodeLink *link : tree.all_links()) {
|
||||
if (!link->is_available()) {
|
||||
continue;
|
||||
@@ -196,7 +197,7 @@ static void update_zone_border_links(const bNodeTree &tree, bNodeTreeZones &tree
|
||||
if (link->is_muted()) {
|
||||
continue;
|
||||
}
|
||||
if (bke::nodeIsDanglingReroute(&tree, link->fromnode)) {
|
||||
if (link->fromnode->is_dangling_reroute()) {
|
||||
continue;
|
||||
}
|
||||
bNodeTreeZone *from_zone = const_cast<bNodeTreeZone *>(
|
||||
|
||||
@@ -439,6 +439,9 @@ typedef struct bNode {
|
||||
/** A span containing all internal links when the node is muted. */
|
||||
blender::Span<bNodeLink> internal_links() const;
|
||||
|
||||
/* This node is reroute which is not logically connected to any source of value. */
|
||||
bool is_dangling_reroute() const;
|
||||
|
||||
/* True if the socket is visible and has a valid location. The icon may not be visible. */
|
||||
bool is_socket_drawn(const bNodeSocket &socket) const;
|
||||
/* True if the socket is drawn and the icon is visible. */
|
||||
|
||||
@@ -396,10 +396,9 @@ class LazyFunctionForMultiInput : public LazyFunction {
|
||||
base_type_ = get_socket_cpp_type(socket);
|
||||
BLI_assert(base_type_ != nullptr);
|
||||
BLI_assert(socket.is_multi_input());
|
||||
const bNodeTree &btree = socket.owner_tree();
|
||||
for (const bNodeLink *link : socket.directly_linked_links()) {
|
||||
if (link->is_muted() || !link->fromsock->is_available() ||
|
||||
bke::nodeIsDanglingReroute(&btree, link->fromnode))
|
||||
link->fromnode->is_dangling_reroute())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -777,9 +776,7 @@ class LazyFunctionForViewerNode : public LazyFunction {
|
||||
continue;
|
||||
}
|
||||
const Span<const bNodeLink *> links = bsocket->directly_linked_links();
|
||||
if (links.is_empty() ||
|
||||
bke::nodeIsDanglingReroute(&bnode.owner_tree(), links.first()->fromnode))
|
||||
{
|
||||
if (links.is_empty() || links.first()->fromnode->is_dangling_reroute()) {
|
||||
use_field_input_ = false;
|
||||
inputs_.pop_last();
|
||||
r_lf_index_by_bsocket[bsocket->index_in_tree()] = -1;
|
||||
@@ -3784,7 +3781,7 @@ struct GeometryNodesLazyFunctionBuilder {
|
||||
lf::OutputSocket &from_lf_socket,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
if (bke::nodeIsDanglingReroute(&btree_, &from_bsocket.owner_node())) {
|
||||
if (from_bsocket.owner_node().is_dangling_reroute()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3871,7 +3868,7 @@ struct GeometryNodesLazyFunctionBuilder {
|
||||
break;
|
||||
}
|
||||
if (multi_input_link->is_muted() || !multi_input_link->fromsock->is_available() ||
|
||||
bke::nodeIsDanglingReroute(&btree_, multi_input_link->fromnode))
|
||||
multi_input_link->fromnode->is_dangling_reroute())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user