[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