[Bf-blender-cvs] [5f29552be79] temp-geometry-nodes-fields--fields: Complete handling of field network inputs
Hans Goudey
noreply at git.blender.org
Sat Aug 28 07:05:22 CEST 2021
Commit: 5f29552be79cdb46e8e36845ed6ce1caf0feabed
Author: Hans Goudey
Date: Sat Aug 28 00:05:14 2021 -0500
Branches: temp-geometry-nodes-fields--fields
https://developer.blender.org/rB5f29552be79cdb46e8e36845ed6ce1caf0feabed
Complete handling of field network inputs
===================================================================
M source/blender/functions/FN_field.hh
M source/blender/functions/intern/field.cc
M source/blender/functions/tests/FN_field_test.cc
===================================================================
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index 79535acc472..0416eb11d50 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -55,12 +55,13 @@ class FieldFunction {
*/
blender::Vector<Field *> inputs_;
- std::string name_ = "";
+ std::string name_;
public:
- FieldFunction() = default;
- FieldFunction(std::unique_ptr<MultiFunction> function, Span<Field *> inputs)
- : function_(std::move(function)), inputs_(inputs)
+ FieldFunction(std::unique_ptr<MultiFunction> function,
+ Span<Field *> inputs,
+ std::string &&name = "")
+ : function_(std::move(function)), inputs_(inputs), name_(std::move(name))
{
}
@@ -81,21 +82,14 @@ class FieldFunction {
};
class FieldInput {
-
- GVArrayPtr data_;
-
- std::string name_ = "";
+ std::string name_;
public:
- FieldInput(GVArrayPtr data) : data_(std::move(data))
+ FieldInput(std::string &&name = "") : name_(std::move(name))
{
}
- const GVArray &data() const
- {
- BLI_assert(data_);
- return *data_;
- }
+ virtual GVArrayPtr retrieve_data(IndexMask mask) const = 0;
blender::StringRef name() const
{
@@ -147,7 +141,7 @@ class Field {
{
return input_ != nullptr;
}
- const FieldInput &input()
+ const FieldInput &input() const
{
BLI_assert(function_ == nullptr);
BLI_assert(input_ != nullptr);
@@ -187,6 +181,6 @@ class Field {
*/
void evaluate_fields(blender::Span<Field> fields,
blender::IndexMask mask,
- blender::MutableSpan<GMutableSpan> outputs);
+ blender::Span<GMutableSpan> outputs);
} // namespace blender::fn
\ No newline at end of file
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index 29f5148c01a..62e7d8d31aa 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -15,20 +15,33 @@
*/
#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
+#include "BLI_set.hh"
#include "FN_field.hh"
namespace blender::fn {
-/* A map to hold the output variables for each function so they can be reused. */
-using OutputMap = MultiValueMap<const FieldFunction *, MFVariable *>;
+/**
+ * A map to hold the output variables for each function so they can be reused.
+ */
+// using VariableMap = Map<const FunctionOrInput *, Vector<MFVariable *>>;
+using VariableMap = Map<const void *, Vector<MFVariable *>>;
+
+/**
+ * A map of the computed inputs for all of a field system's inputs, to avoid creating duplicates.
+ * Usually virtual arrays are just references, but sometimes they can be heavier as well.
+ */
+using ComputedInputMap = Map<const MFVariable *, GVArrayPtr>;
-static MFVariable *get_field_variable(const Field &field, const OutputMap &output_map)
+static MFVariable *get_field_variable(const Field &field, const VariableMap &variable_map)
{
- const FieldFunction &input_field_function = field.function();
- const Span<MFVariable *> input_function_outputs = output_map.lookup(&input_field_function);
- return input_function_outputs[field.function_output_index()];
+ if (field.is_input()) {
+ const FieldInput &input = field.input();
+ return variable_map.lookup(&input).first();
+ }
+ const FieldFunction &function = field.function();
+ const Span<MFVariable *> function_outputs = variable_map.lookup(&function);
+ return function_outputs[field.function_output_index()];
}
/**
@@ -36,45 +49,54 @@ static MFVariable *get_field_variable(const Field &field, const OutputMap &outpu
* inputs. Start adding multi-function variables there. Those outputs are then used as inputs
* for the dependent functions, and the rest of the field tree is built up from there.
*/
-static void add_field_variables(const Field &field,
- MFProcedureBuilder &builder,
- OutputMap &output_map)
+static void add_field_variables_recursive(const Field &field,
+ MFProcedureBuilder &builder,
+ VariableMap &variable_map)
{
- const FieldFunction &function = field.function();
- for (const Field *input_field : function.inputs()) {
- add_field_variables(*input_field, builder, output_map);
+ if (field.is_input()) {
+ const FieldInput &input = field.input();
+ if (!variable_map.contains(&input)) {
+ variable_map.add(&input,
+ {&builder.add_input_parameter(MFDataType::ForSingle(field.type()))});
+ }
}
-
- /* Add the immediate inputs to this field, which were added before in the recursive call.
- * This will be skipped for functions with no inputs. */
- Vector<MFVariable *> inputs;
- for (const Field *input_field : function.inputs()) {
- MFVariable *input = get_field_variable(*input_field, output_map);
- builder.add_input_parameter(input->data_type());
- inputs.append(input);
+ else {
+ const FieldFunction &function = field.function();
+ for (const Field *input_field : function.inputs()) {
+ add_field_variables_recursive(*input_field, builder, variable_map);
+ }
+
+ /* Add the immediate inputs to this field, which were added earlier in the
+ * recursive call. This will be skipped for functions with no inputs. */
+ Vector<MFVariable *> inputs;
+ for (const Field *input_field : function.inputs()) {
+ MFVariable *input = get_field_variable(*input_field, variable_map);
+ builder.add_input_parameter(input->data_type(), input_field->name());
+ inputs.append(input);
+ }
+
+ Vector<MFVariable *> outputs = builder.add_call(function.multi_function(), inputs);
+
+ builder.add_destruct(inputs);
+
+ variable_map.add(&function, std::move(outputs));
}
-
- Vector<MFVariable *> outputs = builder.add_call(function.multi_function(), inputs);
-
- builder.add_destruct(inputs);
-
- output_map.add_multiple(&function, outputs);
}
-static void build_procedure(const Span<Field> fields, MFProcedure &procedure)
+static void build_procedure(const Span<Field> fields,
+ MFProcedure &procedure,
+ VariableMap &variable_map)
{
MFProcedureBuilder builder{procedure};
- OutputMap output_map;
-
for (const Field &field : fields) {
- add_field_variables(field, builder, output_map);
+ add_field_variables_recursive(field, builder, variable_map);
}
builder.add_return();
for (const Field &field : fields) {
- MFVariable *input = get_field_variable(field, output_map);
+ MFVariable *input = get_field_variable(field, variable_map);
builder.add_output_parameter(*input);
}
@@ -83,33 +105,98 @@ static void build_procedure(const Span<Field> fields, MFProcedure &procedure)
BLI_assert(procedure.validate());
}
-static void evaluate_procedure(MFProcedure &procedure,
- const IndexMask mask,
- const MutableSpan<GMutableSpan> outputs)
+/**
+ * \TODO: In the future this could remove from the input map instead of building a second map.
+ * Right now it's preferrable to keep this more understandable though.
+ */
+static void gather_inputs_recursive(const Field &field,
+ const VariableMap &variable_map,
+ const IndexMask mask,
+ MFParamsBuilder ¶ms,
+ Set<const MFVariable *> &computed_inputs,
+ Vector<GVArrayPtr> &r_inputs)
{
- MFProcedureExecutor executor{"Evaluate Field", procedure};
- MFParamsBuilder params{executor, mask.min_array_size()};
- MFContextBuilder context;
+ if (field.is_input()) {
+ const FieldInput &input = field.input();
+ const MFVariable *variable = variable_map.lookup(&input).first();
+ if (!computed_inputs.contains(variable)) {
+ GVArrayPtr data = input.retrieve_data(mask);
+ computed_inputs.add_new(variable);
+ params.add_readonly_single_input(*data, input.name());
+ r_inputs.append(std::move(data));
+ }
+ }
+ else {
+ const FieldFunction &function = field.function();
+ for (const Field *input_field : function.inputs()) {
+ gather_inputs_recursive(*input_field, variable_map, mask, params, computed_inputs, r_inputs);
+ }
+ }
+}
+
+static void gather_inputs(const Span<Field> fields,
+ const VariableMap &variable_map,
+ const IndexMask mask,
+ MFParamsBuilder ¶ms,
+ Vector<GVArrayPtr> &r_inputs)
+{
+ Set<const MFVariable *> computed_inputs;
+ for (const Field &field : fields) {
+ gather_inputs_recursive(field, variable_map, mask, params, computed_inputs, r_inputs);
+ }
+}
- /* Add the output arrays. */
+static void add_outputs(MFParamsBuilder ¶ms, Span<GMutableSpan> outputs)
+{
for (const int i : outputs.index_range()) {
params.add_uninitialized_single_output(outputs[i]);
}
+}
+
+static void evaluate_non_input_fields(const Span<Field> fields,
+ const IndexMask mask,
+ const Span<GMutableSpan> outputs)
+{
+ MFProcedure procedure;
+ VariableMap variable_map;
+ build_procedure(fields, procedure, variable_map);
+
+ MFProcedureExecutor executor{"Evaluate Field", procedure};
+ MFParamsBuilder params{executor, mask.min_array_size()};
+ MFContextBuilder context;
+
+ Vector<GVArrayPtr> inputs;
+ gather_inputs(fields, variable_map, mask, params, inputs);
+
+ add_outputs(params, outputs);
executor.call(mask, params, context);
}
/**
- * Evaluate more than one prodecure at a time
+ * Evaluate more than one prodecure at a time, since often intermediate results will be shared
+ * between multiple final results, and the procedure evaluator can optimize for this case.
*/
void evaluate_fields(const Span<Field> fields,
const IndexMask mask,
- const MutableSpan<GMutableSpan> outputs)
+ const Span<GMut
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list