[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 ©_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