[Bf-blender-cvs] [5617cb5500f] temp-geometry-nodes-fields: add some comments

Jacques Lucke noreply at git.blender.org
Fri Sep 3 17:12:10 CEST 2021


Commit: 5617cb5500fb077282d4d464be4763f4bf46ca1c
Author: Jacques Lucke
Date:   Fri Sep 3 17:09:14 2021 +0200
Branches: temp-geometry-nodes-fields
https://developer.blender.org/rB5617cb5500fb077282d4d464be4763f4bf46ca1c

add some comments

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

M	source/blender/functions/FN_field.hh
M	source/blender/functions/intern/field.cc

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

diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index 34ff83fe2b9..2262bfdb641 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -107,7 +107,7 @@ template<typename NodePtr> class GFieldBase {
     return node_->cpp_type_of_output_index(node_output_index_);
   }
 
-  bool has_context_node() const
+  bool has_input_node() const
   {
     return node_->is_input_node();
   }
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index e7a05a53c48..f10a154dcd6 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -24,14 +24,26 @@
 
 namespace blender::fn {
 
-struct FieldGraphInfo {
+struct FieldTreeInfo {
+  /**
+   * When fields are build, they only have references to the fields that they depend on. This map
+   * allows traversal of fields in the opposite direction. So for every field it stores what other
+   * fields directly depend on it.
+   */
   MultiValueMap<GFieldRef, GFieldRef> field_users;
+  /**
+   * The same field input may exist in the field tree as as separate nodes due to the way the tree
+   * is constructed. This set contains every input only once.
+   */
   VectorSet<std::reference_wrapper<const FieldInput>> deduplicated_field_inputs;
 };
 
-static FieldGraphInfo preprocess_field_graph(Span<GFieldRef> entry_fields)
+/**
+ * Collects some information from the field tree that is required by later steps.
+ */
+static FieldTreeInfo preprocess_field_tree(Span<GFieldRef> entry_fields)
 {
-  FieldGraphInfo graph_info;
+  FieldTreeInfo field_tree_info;
 
   Stack<GFieldRef> fields_to_check;
   Set<GFieldRef> handled_fields;
@@ -44,30 +56,34 @@ static FieldGraphInfo preprocess_field_graph(Span<GFieldRef> entry_fields)
 
   while (!fields_to_check.is_empty()) {
     GFieldRef field = fields_to_check.pop();
-    if (field.has_context_node()) {
+    if (field.has_input_node()) {
       const FieldInput &field_input = static_cast<const FieldInput &>(field.node());
-      graph_info.deduplicated_field_inputs.add(field_input);
+      field_tree_info.deduplicated_field_inputs.add(field_input);
       continue;
     }
     BLI_assert(field.has_operation_node());
     const FieldOperation &operation = static_cast<const FieldOperation &>(field.node());
     for (const GFieldRef operation_input : operation.inputs()) {
-      graph_info.field_users.add(operation_input, field);
+      field_tree_info.field_users.add(operation_input, field);
       if (handled_fields.add(operation_input)) {
         fields_to_check.push(operation_input);
       }
     }
   }
-  return graph_info;
+  return field_tree_info;
 }
 
-static Vector<const GVArray *> get_field_context_inputs(ResourceScope &scope,
-                                                        const IndexMask mask,
-                                                        const FieldContext &context,
-                                                        const FieldGraphInfo &graph_info)
+/**
+ * Retrieves the data from the context that is passed as input into the field.
+ */
+static Vector<const GVArray *> get_field_context_inputs(
+    ResourceScope &scope,
+    const IndexMask mask,
+    const FieldContext &context,
+    const Span<std::reference_wrapper<const FieldInput>> field_inputs)
 {
   Vector<const GVArray *> field_context_inputs;
-  for (const FieldInput &field_input : graph_info.deduplicated_field_inputs) {
+  for (const FieldInput &field_input : field_inputs) {
     const GVArray *varray = context.try_get_varray_for_context(field_input, mask, scope);
     if (varray == nullptr) {
       const CPPType &type = field_input.cpp_type();
@@ -79,19 +95,27 @@ static Vector<const GVArray *> get_field_context_inputs(ResourceScope &scope,
   return field_context_inputs;
 }
 
-static Set<GFieldRef> find_varying_fields(const FieldGraphInfo &graph_info,
+/**
+ * \return A set that contains all fields from the field tree that depend on an input that varies
+ * for different indices.
+ */
+static Set<GFieldRef> find_varying_fields(const FieldTreeInfo &field_tree_info,
                                           Span<const GVArray *> field_context_inputs)
 {
   Set<GFieldRef> found_fields;
   Stack<GFieldRef> fields_to_check;
+
+  /* The varying fields are the ones that depend on inputs that are not constant. Therefore we
+   * start the tree search at the non-constant input fields and traverse through all fields that
+   * depend on those. */
   for (const int i : field_context_inputs.index_range()) {
     const GVArray *varray = field_context_inputs[i];
     if (varray->is_single()) {
       continue;
     }
-    const FieldInput &field_input = graph_info.deduplicated_field_inputs[i];
+    const FieldInput &field_input = field_tree_info.deduplicated_field_inputs[i];
     const GFieldRef field_input_field{field_input, 0};
-    const Span<GFieldRef> users = graph_info.field_users.lookup(field_input_field);
+    const Span<GFieldRef> users = field_tree_info.field_users.lookup(field_input_field);
     for (const GFieldRef &field : users) {
       if (found_fields.add(field)) {
         fields_to_check.push(field);
@@ -100,7 +124,7 @@ static Set<GFieldRef> find_varying_fields(const FieldGraphInfo &graph_info,
   }
   while (!fields_to_check.is_empty()) {
     GFieldRef field = fields_to_check.pop();
-    const Span<GFieldRef> users = graph_info.field_users.lookup(field);
+    const Span<GFieldRef> users = field_tree_info.field_users.lookup(field);
     for (GFieldRef field : users) {
       if (found_fields.add(field)) {
         fields_to_check.push(field);
@@ -110,40 +134,53 @@ static Set<GFieldRef> find_varying_fields(const FieldGraphInfo &graph_info,
   return found_fields;
 }
 
+/**
+ * Builds the #procedure so that it computes the the fields.
+ */
 static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
                                                       ResourceScope &scope,
-                                                      const FieldGraphInfo &graph_info,
+                                                      const FieldTreeInfo &field_tree_info,
                                                       Span<GFieldRef> output_fields)
 {
   MFProcedureBuilder builder{procedure};
+  /* Every input, intermediate and output field corresponds to a variable in the procedure. */
   Map<GFieldRef, MFVariable *> variable_by_field;
-  for (const FieldInput &field_input : graph_info.deduplicated_field_inputs) {
+
+  /* Start by adding the field inputs as parameters to the procedure. */
+  for (const FieldInput &field_input : field_tree_info.deduplicated_field_inputs) {
     MFVariable &variable = builder.add_input_parameter(
         MFDataType::ForSingle(field_input.cpp_type()), field_input.debug_name());
     variable_by_field.add_new({field_input, 0}, &variable);
   }
 
+  /* Utility struct that is used to do proper depth first search traversal of the tree below. */
   struct FieldWithIndex {
     GFieldRef field;
     int current_input_index = 0;
   };
 
   for (GFieldRef field : output_fields) {
+    /* We start a new stack for each output field to make sure that a field pushed later to the
+     * stack does never depend on a field that was pushed before. */
     Stack<FieldWithIndex> fields_to_check;
     fields_to_check.push({field, 0});
     while (!fields_to_check.is_empty()) {
       FieldWithIndex &field_with_index = fields_to_check.peek();
       const GFieldRef &field = field_with_index.field;
       if (variable_by_field.contains(field)) {
+        /* The field has been handled already. */
         fields_to_check.pop();
         continue;
       }
       /* Field inputs should already be handled above. */
       BLI_assert(field.has_operation_node());
+
       const FieldOperation &operation = static_cast<const FieldOperation &>(field.node());
       const Span<GField> operation_inputs = operation.inputs();
+
       if (field_with_index.current_input_index < operation_inputs.size()) {
-        /* Push next input. */
+        /* Not all inputs are handled yet. Push the next input field to the stack and increment the
+         * input index. */
         fields_to_check.push({operation_inputs[field_with_index.current_input_index]});
         field_with_index.current_input_index++;
       }
@@ -155,6 +192,7 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
         }
         const MultiFunction &multi_function = operation.multi_function();
         Vector<MFVariable *> output_variables = builder.add_call(multi_function, input_variables);
+        /* Add newly created variables to the map. */
         for (const int i : output_variables.index_range()) {
           variable_by_field.add_new({operation, i}, output_variables[i]);
         }
@@ -162,11 +200,13 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
     }
   }
 
+  /* Add output parameters to the procedure. */
   Set<MFVariable *> already_output_variables;
   for (const GFieldRef &field : output_fields) {
     MFVariable *variable = variable_by_field.lookup(field);
     if (!already_output_variables.add(variable)) {
-      /* The same variable is output twice. Create a copy to make it work. */
+      /* One variable can be output at most once. To output the same value twice, we have to make a
+       * copy first. */
       const MultiFunction &copy_fn = scope.construct<CustomMF_GenericCopy>(
           __func__, "copy", variable->data_type());
       variable = builder.add_call<1>(copy_fn, {variable})[0];
@@ -178,6 +218,7 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
   for (const GFieldRef &field : output_fields) {
     variable_by_field.remove(field);
   }
+  /* Add destructor calls for the remaining variables. */
   for (MFVariable *variable : variable_by_field.values()) {
     builder.add_destruct(*variable);
   }
@@ -188,6 +229,9 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
   BLI_assert(procedure.validate());
 }
 
+/**
+ * Utility class that destructs elements from a partially initialized array.
+ */


@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list