[Bf-blender-cvs] [eac8e820f25] master: Cleanup: move node tree field inferencing to separate file

Jacques Lucke noreply at git.blender.org
Sat Dec 10 11:32:15 CET 2022


Commit: eac8e820f2508cc19403df782227a37998479fe0
Author: Jacques Lucke
Date:   Sat Dec 10 11:32:04 2022 +0100
Branches: master
https://developer.blender.org/rBeac8e820f2508cc19403df782227a37998479fe0

Cleanup: move node tree field inferencing to separate file

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

M	source/blender/blenkernel/BKE_node_runtime.hh
M	source/blender/blenkernel/CMakeLists.txt
A	source/blender/blenkernel/intern/node_tree_field_inferencing.cc
M	source/blender/blenkernel/intern/node_tree_update.cc

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

diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh
index 09722b46fc1..88b68082ab7 100644
--- a/source/blender/blenkernel/BKE_node_runtime.hh
+++ b/source/blender/blenkernel/BKE_node_runtime.hh
@@ -320,6 +320,10 @@ inline bool topology_cache_is_available(const bNodeSocket &socket)
 
 }  // namespace node_tree_runtime
 
+namespace node_field_inferencing {
+bool update_field_inferencing(const bNodeTree &tree);
+}
+
 }  // namespace blender::bke
 
 /* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index f56acc43582..227c9bd8c66 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -229,6 +229,7 @@ set(SRC
   intern/nla.c
   intern/node.cc
   intern/node_runtime.cc
+  intern/node_tree_field_inferencing.cc
   intern/node_tree_update.cc
   intern/object.cc
   intern/object_deform.c
diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc
new file mode 100644
index 00000000000..0413e28a297
--- /dev/null
+++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc
@@ -0,0 +1,531 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
+
+#include "NOD_node_declaration.hh"
+
+#include "BLI_set.hh"
+#include "BLI_stack.hh"
+
+namespace blender::bke::node_field_inferencing {
+
+using nodes::FieldInferencingInterface;
+using nodes::InputSocketFieldType;
+using nodes::NodeDeclaration;
+using nodes::OutputFieldDependency;
+using nodes::OutputSocketFieldType;
+using nodes::SocketDeclaration;
+
+static bool is_field_socket_type(eNodeSocketDatatype type)
+{
+  return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
+}
+
+static bool is_field_socket_type(const bNodeSocket &socket)
+{
+  return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo->type);
+}
+
+static InputSocketFieldType get_interface_input_field_type(const bNode &node,
+                                                           const bNodeSocket &socket)
+{
+  if (!is_field_socket_type(socket)) {
+    return InputSocketFieldType::None;
+  }
+  if (node.type == NODE_REROUTE) {
+    return InputSocketFieldType::IsSupported;
+  }
+  if (node.type == NODE_GROUP_OUTPUT) {
+    /* Outputs always support fields when the data type is correct. */
+    return InputSocketFieldType::IsSupported;
+  }
+  if (node.typeinfo == &NodeTypeUndefined) {
+    return InputSocketFieldType::None;
+  }
+  if (node.type == NODE_CUSTOM) {
+    return InputSocketFieldType::None;
+  }
+
+  /* TODO: Ensure declaration exists. */
+  const NodeDeclaration *node_decl = node.declaration();
+
+  /* Node declarations should be implemented for nodes involved here. */
+  BLI_assert(node_decl != nullptr);
+
+  /* Get the field type from the declaration. */
+  const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()];
+  const InputSocketFieldType field_type = socket_decl.input_field_type();
+  if (field_type == InputSocketFieldType::Implicit) {
+    return field_type;
+  }
+  if (node_decl->is_function_node()) {
+    /* In a function node, every socket supports fields. */
+    return InputSocketFieldType::IsSupported;
+  }
+  return field_type;
+}
+
+static OutputFieldDependency get_interface_output_field_dependency(const bNode &node,
+                                                                   const bNodeSocket &socket)
+{
+  if (!is_field_socket_type(socket)) {
+    /* Non-field sockets always output data. */
+    return OutputFieldDependency::ForDataSource();
+  }
+  if (node.type == NODE_REROUTE) {
+    /* The reroute just forwards what is passed in. */
+    return OutputFieldDependency::ForDependentField();
+  }
+  if (node.type == NODE_GROUP_INPUT) {
+    /* Input nodes get special treatment in #determine_group_input_states. */
+    return OutputFieldDependency::ForDependentField();
+  }
+  if (node.typeinfo == &NodeTypeUndefined) {
+    return OutputFieldDependency::ForDataSource();
+  }
+  if (node.type == NODE_CUSTOM) {
+    return OutputFieldDependency::ForDataSource();
+  }
+
+  const NodeDeclaration *node_decl = node.declaration();
+
+  /* Node declarations should be implemented for nodes involved here. */
+  BLI_assert(node_decl != nullptr);
+
+  if (node_decl->is_function_node()) {
+    /* In a generic function node, all outputs depend on all inputs. */
+    return OutputFieldDependency::ForDependentField();
+  }
+
+  /* Use the socket declaration. */
+  const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()];
+  return socket_decl.output_field_dependency();
+}
+
+static FieldInferencingInterface get_dummy_field_inferencing_interface(const bNode &node)
+{
+  FieldInferencingInterface inferencing_interface;
+  inferencing_interface.inputs.append_n_times(InputSocketFieldType::None,
+                                              node.input_sockets().size());
+  inferencing_interface.outputs.append_n_times(OutputFieldDependency::ForDataSource(),
+                                               node.output_sockets().size());
+  return inferencing_interface;
+}
+
+/**
+ * Retrieves information about how the node interacts with fields.
+ * In the future, this information can be stored in the node declaration. This would allow this
+ * function to return a reference, making it more efficient.
+ */
+static FieldInferencingInterface get_node_field_inferencing_interface(const bNode &node)
+{
+  /* Node groups already reference all required information, so just return that. */
+  if (node.is_group()) {
+    bNodeTree *group = (bNodeTree *)node.id;
+    if (group == nullptr) {
+      return FieldInferencingInterface();
+    }
+    if (!ntreeIsRegistered(group)) {
+      /* This can happen when there is a linked node group that was not found (see T92799). */
+      return get_dummy_field_inferencing_interface(node);
+    }
+    if (!group->runtime->field_inferencing_interface) {
+      /* This shouldn't happen because referenced node groups should always be updated first. */
+      BLI_assert_unreachable();
+    }
+    return *group->runtime->field_inferencing_interface;
+  }
+
+  FieldInferencingInterface inferencing_interface;
+  for (const bNodeSocket *input_socket : node.input_sockets()) {
+    inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket));
+  }
+
+  for (const bNodeSocket *output_socket : node.output_sockets()) {
+    inferencing_interface.outputs.append(
+        get_interface_output_field_dependency(node, *output_socket));
+  }
+  return inferencing_interface;
+}
+
+/**
+ * This struct contains information for every socket. The values are propagated through the
+ * network.
+ */
+struct SocketFieldState {
+  /* This socket starts a new field. */
+  bool is_field_source = false;
+  /* This socket can never become a field, because the node itself does not support it. */
+  bool is_always_single = false;
+  /* This socket is currently a single value. It could become a field though. */
+  bool is_single = true;
+  /* This socket is required to be a single value. This can be because the node itself only
+   * supports this socket to be a single value, or because a node afterwards requires this to be a
+   * single value. */
+  bool requires_single = false;
+};
+
+static Vector<const bNodeSocket *> gather_input_socket_dependencies(
+    const OutputFieldDependency &field_dependency, const bNode &node)
+{
+  const OutputSocketFieldType type = field_dependency.field_type();
+  Vector<const bNodeSocket *> input_sockets;
+  switch (type) {
+    case OutputSocketFieldType::FieldSource:
+    case OutputSocketFieldType::None: {
+      break;
+    }
+    case OutputSocketFieldType::DependentField: {
+      /* This output depends on all inputs. */
+      input_sockets.extend(node.input_sockets());
+      break;
+    }
+    case OutputSocketFieldType::PartiallyDependent: {
+      /* This output depends only on a few inputs. */
+      for (const int i : field_dependency.linked_input_indices()) {
+        input_sockets.append(&node.input_socket(i));
+      }
+      break;
+    }
+  }
+  return input_sockets;
+}
+
+/**
+ * Check what the group output socket depends on. Potentially traverses the node tree
+ * to figure out if it is always a field or if it depends on any group inputs.
+ */
+static OutputFieldDependency find_group_output_dependencies(
+    const bNodeSocket &group_output_socket, const Span<SocketFieldState> field_state_by_socket_id)
+{
+  if (!is_field_socket_type(group_output_socket)) {
+    return OutputFieldDependency::ForDataSource();
+  }
+
+  /* Use a Set here instead of an array indexed by socket id, because we my only need to look at
+   * very few sockets. */
+  Set<const bNodeSocket *> handled_sockets;
+  Stack<const bNodeSocket *> sockets_to_check;
+
+  handled_sockets.add(&group_output_socket);
+  sockets_to_check.push(&group_output_socket);
+
+  /* Keeps track of group input indices that are (indirectly) connected to the output. */
+  Vector<int> linked_input_indices;
+
+  while (!sockets_to_check.is_empty()) {
+    const bNodeSocket *input_socket = sockets_to_check.pop();
+
+    if (!input_socket->is_directly_linked() &&
+        !field_state_by_socket_id[input_socket->index_in_tree()].is_single) {
+      /* This socket uses a field as input by default. */
+      return OutputFieldDependency::ForFieldSource();
+    }
+
+    for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
+      const bNode &origin_node = origin_socket->owner_node();
+      const SocketFieldState &origin_state =
+          field_state_by_socket_id[origin_socket->index_in_tree()];
+
+      if (origin_state.is_field_source) {
+        if (origin_node.type == NODE_GROUP_INPUT) {
+          /* Found a group input that the group output depends on. */
+          linked_input_indices.append_non_duplicates(origin_socket->index());
+        }
+        else {
+          /* Found a field source that is not the group input. So the output is always a

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list