[Bf-blender-cvs] [2b4cb893e7e] master: Fix T101214: hidden link can cause cycle in node tree

Jacques Lucke noreply at git.blender.org
Tue Sep 20 13:21:26 CEST 2022


Commit: 2b4cb893e7ebb33c24c7413ba0ff10eeb78bddfe
Author: Jacques Lucke
Date:   Tue Sep 20 13:21:03 2022 +0200
Branches: master
https://developer.blender.org/rB2b4cb893e7ebb33c24c7413ba0ff10eeb78bddfe

Fix T101214: hidden link can cause cycle in node tree

Links that are linked to unavailable sockets should be ignored.

===================================================================

M	source/blender/blenkernel/BKE_node_runtime.hh
M	source/blender/blenkernel/intern/node_runtime.cc
M	source/blender/blenkernel/intern/node_tree_update.cc
M	source/blender/makesdna/DNA_node_types.h
M	source/blender/nodes/intern/derived_node_tree.cc
M	source/blender/nodes/intern/geometry_nodes_lazy_function.cc

===================================================================

diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh
index c3e0460bdb1..65c801a087b 100644
--- a/source/blender/blenkernel/BKE_node_runtime.hh
+++ b/source/blender/blenkernel/BKE_node_runtime.hh
@@ -81,7 +81,7 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
   Vector<bNode *> toposort_left_to_right;
   Vector<bNode *> toposort_right_to_left;
   Vector<bNode *> group_nodes;
-  bool has_link_cycle = false;
+  bool has_available_link_cycle = false;
   bool has_undefined_nodes_or_sockets = false;
   bNode *group_output_node = nullptr;
 };
@@ -152,8 +152,8 @@ class bNodeRuntime : NonCopyable, NonMovable {
   Map<StringRefNull, bNodeSocket *> inputs_by_identifier;
   Map<StringRefNull, bNodeSocket *> outputs_by_identifier;
   int index_in_tree = -1;
-  bool has_linked_inputs = false;
-  bool has_linked_outputs = false;
+  bool has_available_linked_inputs = false;
+  bool has_available_linked_outputs = false;
   bNodeTree *owner_tree = nullptr;
 };
 
@@ -269,10 +269,10 @@ inline blender::Span<bNode *> bNodeTree::group_nodes()
   return this->runtime->group_nodes;
 }
 
-inline bool bNodeTree::has_link_cycle() const
+inline bool bNodeTree::has_available_link_cycle() const
 {
   BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
-  return this->runtime->has_link_cycle;
+  return this->runtime->has_available_link_cycle;
 }
 
 inline bool bNodeTree::has_undefined_nodes_or_sockets() const
@@ -455,6 +455,11 @@ inline bool bNodeLink::is_muted() const
   return this->flag & NODE_LINK_MUTED;
 }
 
+inline bool bNodeLink::is_available() const
+{
+  return this->fromsock->is_available() && this->tosock->is_available();
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc
index 8b5b8bbc8b7..4fb0e423a33 100644
--- a/source/blender/blenkernel/intern/node_runtime.cc
+++ b/source/blender/blenkernel/intern/node_runtime.cc
@@ -123,15 +123,17 @@ static void update_directly_linked_links_and_sockets(const bNodeTree &ntree)
       socket->runtime->directly_linked_links.clear();
       socket->runtime->directly_linked_sockets.clear();
     }
-    node->runtime->has_linked_inputs = false;
-    node->runtime->has_linked_outputs = false;
+    node->runtime->has_available_linked_inputs = false;
+    node->runtime->has_available_linked_outputs = false;
   }
   for (bNodeLink *link : tree_runtime.links) {
     link->fromsock->runtime->directly_linked_links.append(link);
     link->fromsock->runtime->directly_linked_sockets.append(link->tosock);
     link->tosock->runtime->directly_linked_links.append(link);
-    link->fromnode->runtime->has_linked_outputs = true;
-    link->tonode->runtime->has_linked_inputs = true;
+    if (link->is_available()) {
+      link->fromnode->runtime->has_available_linked_outputs = true;
+      link->tonode->runtime->has_available_linked_inputs = true;
+    }
   }
   for (bNodeSocket *socket : tree_runtime.input_sockets) {
     if (socket->flag & SOCK_MULTI_INPUT) {
@@ -168,7 +170,10 @@ static void find_logical_origins_for_socket_recursive(
     links_to_check = links_to_check.take_front(1);
   }
   for (bNodeLink *link : links_to_check) {
-    if (link->flag & NODE_LINK_MUTED) {
+    if (link->is_muted()) {
+      continue;
+    }
+    if (!link->is_available()) {
       continue;
     }
     bNodeSocket &origin_socket = *link->fromsock;
@@ -285,14 +290,20 @@ static void toposort_from_start_node(const ToposortDirection direction,
         break;
       }
       bNodeSocket &socket = *sockets[item.socket_index];
-      const Span<bNodeSocket *> linked_sockets = socket.runtime->directly_linked_sockets;
-      if (item.link_index == linked_sockets.size()) {
+      const Span<bNodeLink *> linked_links = socket.runtime->directly_linked_links;
+      if (item.link_index == linked_links.size()) {
         /* All links connected to this socket have already been visited. */
         item.socket_index++;
         item.link_index = 0;
         continue;
       }
-      bNodeSocket &linked_socket = *linked_sockets[item.link_index];
+      bNodeLink &link = *linked_links[item.link_index];
+      if (!link.is_available()) {
+        /* Ignore unavailable links. */
+        item.link_index++;
+        continue;
+      }
+      bNodeSocket &linked_socket = *socket.runtime->directly_linked_sockets[item.link_index];
       bNode &linked_node = *linked_socket.runtime->owner_node;
       ToposortNodeState &linked_node_state = node_states[linked_node.runtime->index_in_tree];
       if (linked_node_state.is_done) {
@@ -337,8 +348,9 @@ static void update_toposort(const bNodeTree &ntree,
       /* Ignore nodes that are done already. */
       continue;
     }
-    if ((direction == ToposortDirection::LeftToRight) ? node->runtime->has_linked_outputs :
-                                                        node->runtime->has_linked_inputs) {
+    if ((direction == ToposortDirection::LeftToRight) ?
+            node->runtime->has_available_linked_outputs :
+            node->runtime->has_available_linked_inputs) {
       /* Ignore non-start nodes. */
       continue;
     }
@@ -398,7 +410,7 @@ static void ensure_topology_cache(const bNodeTree &ntree)
                                      update_toposort(ntree,
                                                      ToposortDirection::LeftToRight,
                                                      tree_runtime.toposort_left_to_right,
-                                                     tree_runtime.has_link_cycle);
+                                                     tree_runtime.has_available_link_cycle);
                                    },
                                    [&]() {
                                      bool dummy;
diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc
index dcb6666317f..f9bab0959c9 100644
--- a/source/blender/blenkernel/intern/node_tree_update.cc
+++ b/source/blender/blenkernel/intern/node_tree_update.cc
@@ -1388,7 +1388,7 @@ class NodeTreeMainUpdater {
   uint32_t get_combined_socket_topology_hash(const bNodeTree &tree,
                                              Span<const bNodeSocket *> sockets)
   {
-    if (tree.has_link_cycle()) {
+    if (tree.has_available_link_cycle()) {
       /* Return dummy value when the link has any cycles. The algorithm below could be improved to
        * handle cycles more gracefully. */
       return 0;
@@ -1404,7 +1404,7 @@ class NodeTreeMainUpdater {
   Array<uint32_t> get_socket_topology_hashes(const bNodeTree &tree,
                                              Span<const bNodeSocket *> sockets)
   {
-    BLI_assert(!tree.has_link_cycle());
+    BLI_assert(!tree.has_available_link_cycle());
     Array<std::optional<uint32_t>> hash_by_socket_id(tree.all_sockets().size());
     Stack<const bNodeSocket *> sockets_to_check = sockets;
 
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 7832541e360..06e14c3134b 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -505,6 +505,7 @@ typedef struct bNodeLink {
 
 #ifdef __cplusplus
   bool is_muted() const;
+  bool is_available() const;
 #endif
 
 } bNodeLink;
@@ -655,12 +656,12 @@ typedef struct bNodeTree {
   /**
    * Cached toposort of all nodes. If there are cycles, the returned array is not actually a
    * toposort. However, if a connected component does not contain a cycle, this component is sorted
-   * correctly. Use #has_link_cycle to check for cycles.
+   * correctly. Use #has_available_link_cycle to check for cycles.
    */
   blender::Span<const bNode *> toposort_left_to_right() const;
   blender::Span<const bNode *> toposort_right_to_left() const;
   /** True when there are any cycles in the node tree. */
-  bool has_link_cycle() const;
+  bool has_available_link_cycle() const;
   /**
    * True when there are nodes or sockets in the node tree that don't use a known type. This can
    * happen when nodes don't exist in the current Blender version that existed in the version where
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index e8e0f0fa61c..2ea80008af8 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -58,7 +58,7 @@ void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
 bool DerivedNodeTree::has_link_cycles() const
 {
   for (const bNodeTree *btree : used_btrees_) {
-    if (btree->has_link_cycle()) {
+    if (btree->has_available_link_cycle()) {
       return true;
     }
   }
diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
index af6861a59c0..718aaef5680 100644
--- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
+++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
@@ -1044,10 +1044,10 @@ struct GeometryNodesLazyFunctionGraphBuilder {
       if (link->is_muted()) {
         continue;
       }
-      const bNodeSocket &to_bsocket = *link->tosock;
-      if (!to_bsocket.is_available()) {
+      if (!link->is_available()) {
         continue;
       }
+      const bNodeSocket &to_bsocket = *link->tosock;
       const CPPType *to_type = get_socket_cpp_type(to_bsocket);
       if (to_type == nullptr) {
         continue;
@@ -1258,7 +1258,7 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr
     const bNodeTree &btree)
 {
   btree.ensure_topology_cache();
-  if (btree.has_link_cycle()) {
+  if (btree.has_available_link_cycle()) {
     return nullptr;
   }



More information about the Bf-blender-cvs mailing list