[Bf-blender-cvs] [c227b4da17c] temp-geometry-nodes-fields: cleanup
Jacques Lucke
noreply at git.blender.org
Mon Sep 6 11:45:58 CEST 2021
Commit: c227b4da17ccc0d5aec5454ead8966d4204b49db
Author: Jacques Lucke
Date: Mon Sep 6 11:45:50 2021 +0200
Branches: temp-geometry-nodes-fields
https://developer.blender.org/rBc227b4da17ccc0d5aec5454ead8966d4204b49db
cleanup
===================================================================
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 309d871a56e..984a12e3f23 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -19,15 +19,31 @@
/** \file
* \ingroup fn
*
- * Field serve as an intermediate representation for calculation of a group of functions. Having
- * an intermediate representation is helpful mainly to separate the execution system from the
- * system that describes the necessary computations. Fields can be executed in different contexts,
- * and optimization might mean executing the fields differently based on some factors like the
- * number of elements.
+ * A #Field represents a function that outputs a value based on an arbitrary number of inputs. The
+ * inputs for a specific field evaluation are provided by a #FieldContext.
*
- * For now, fields are very tied to the multi-function system, but in the future #FieldOperation
- * could be extended to use different descriptions of its outputs and computation besides the
- * embedded multi-function.
+ * A typical example is a field that computes a displacement vector for every vertex on a mesh
+ * based on its position.
+ *
+ * Fields can be build, composed and evaluated at run-time. They are stored in a directed tree
+ * graph data structure, whereby each node is a #FieldNode and edges are dependencies. A #FieldNode
+ * has an arbitrary number of inputs and at least one output. A #Field references a specific output
+ * of a #FieldNode. The inputs of a #FieldNode are other fields.
+ *
+ * There are two different types of field nodes:
+ * - #FieldInput: Has no input and exactly one output. It represents an input to the entire field
+ * when it is evaluated. During evaluation, the value of this input is based on a #FieldContext.
+ * - #FieldOperation: Has an arbitrary number of field inputs and at least one output. Its main
+ * use is to compose multiple existing fields into new fields.
+ *
+ * When fields are evaluated, they are converted into a multi-function procedure which allows
+ * efficient compution. In the future, we might support different field evaluation mechanisms for
+ * e.g. the following scenarios:
+ * - Latency of a single evaluation is more important than throughput.
+ * - Evaluation should happen on other hardware like GPUs.
+ *
+ * Whenever possible, multiple fields should be evaluated together to avoid duplicate work when
+ * they share common sub-fields and a common context.
*/
#include "BLI_string_ref.hh"
@@ -41,6 +57,9 @@
namespace blender::fn {
+/**
+ * A node in a field-tree. It has one our more outputs that can be referenced by fields.
+ */
class FieldNode {
private:
bool is_input_;
@@ -80,7 +99,9 @@ class FieldNode {
}
};
-/** Common base class for fields to avoid declaring the same methods for #GField and #GFieldRef. */
+/**
+ * Common base class for fields to avoid declaring the same methods for #GField and #GFieldRef.
+ */
template<typename NodePtr> class GFieldBase {
protected:
NodePtr node_ = nullptr;
@@ -126,8 +147,7 @@ template<typename NodePtr> class GFieldBase {
};
/**
- * Describes the output of a function. Generally corresponds to the combination of an output socket
- * and link combination in a node graph.
+ * A field whose output type is only known at run-time.
*/
class GField : public GFieldBase<std::shared_ptr<FieldNode>> {
public:
@@ -158,6 +178,9 @@ class GFieldRef : public GFieldBase<const FieldNode *> {
}
};
+/**
+ * A typed version of #GField. It has the same memory layout as #GField.
+ */
template<typename T> class Field : public GField {
public:
Field() = default;
@@ -173,10 +196,18 @@ template<typename T> class Field : public GField {
}
};
+/**
+ * A #FieldNode that allows composing existing fields into new fields.
+ */
class FieldOperation : public FieldNode {
+ /**
+ * The multi-function used by this node. It is optionally owned.
+ * Multi-functions with mutable or vector parameters are not supported currently.
+ */
std::unique_ptr<const MultiFunction> owned_function_;
const MultiFunction *function_;
+ /** Inputs to the operation. */
blender::Vector<GField> inputs_;
public:
@@ -218,17 +249,11 @@ class FieldOperation : public FieldNode {
}
};
-class FieldInput;
-
-class FieldContext {
- public:
- ~FieldContext() = default;
-
- virtual const GVArray *try_get_varray_for_context(const FieldInput &field_input,
- IndexMask mask,
- ResourceScope &scope) const;
-};
+class FieldContext;
+/**
+ * A #FieldNode that represents an input to the entire field-tree.
+ */
class FieldInput : public FieldNode {
protected:
const CPPType *type_;
@@ -240,6 +265,10 @@ class FieldInput : public FieldNode {
{
}
+ /**
+ * Get the value of this specific input based on the given context. The returned virtual array,
+ * should live at least as long as the passed in #scope.
+ */
virtual const GVArray *try_get_varray_for_context(const FieldContext &context,
IndexMask mask,
ResourceScope &scope) const = 0;
@@ -262,36 +291,21 @@ class FieldInput : public FieldNode {
}
};
-Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
- Span<GFieldRef> fields_to_evaluate,
- IndexMask mask,
- const FieldContext &context,
- Span<GVMutableArray *> dst_hints = {});
-
-void evaluate_constant_field(const GField &field, void *r_value);
-
-void evaluate_fields_to_spans(Span<GFieldRef> fields_to_evaluate,
- IndexMask mask,
- const FieldContext &context,
- Span<GMutableSpan> out_spans);
-
-Vector<int64_t> indices_from_selection(const VArray<bool> &selection);
-
-template<typename T> T evaluate_constant_field(const Field<T> &field)
-{
- T value;
- value.~T();
- evaluate_constant_field(field, &value);
- return value;
-}
+/**
+ * Provides inputs for a specific field evaluation.
+ */
+class FieldContext {
+ public:
+ ~FieldContext() = default;
-template<typename T> Field<T> make_constant_field(T value)
-{
- auto constant_fn = std::make_unique<fn::CustomMF_Constant<T>>(std::forward<T>(value));
- auto operation = std::make_shared<FieldOperation>(std::move(constant_fn));
- return Field<T>{GField{std::move(operation), 0}};
-}
+ virtual const GVArray *try_get_varray_for_context(const FieldInput &field_input,
+ IndexMask mask,
+ ResourceScope &scope) const;
+};
+/**
+ * Utility class that makes it easier to evaluate fields.
+ */
class FieldEvaluator : NonMovable, NonCopyable {
private:
struct OutputPointerInfo {
@@ -316,6 +330,7 @@ class FieldEvaluator : NonMovable, NonCopyable {
: context_(context), mask_(*mask)
{
}
+
FieldEvaluator(const FieldContext &context, const int64_t size) : context_(context), mask_(size)
{
}
@@ -324,13 +339,7 @@ class FieldEvaluator : NonMovable, NonCopyable {
* \param field: Field to add to the evaluator.
* \param dst: Mutable virtual array that the evaluated result for this field is be written into.
*/
- int add_with_destination(GField field, GVMutableArray &dst)
- {
- const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
- dst_hints_.append(&dst);
- output_pointer_infos_.append({});
- return field_index;
- }
+ int add_with_destination(GField field, GVMutableArray &dst);
/** Same as #add_with_destination but typed. */
template<typename T> int add_with_destination(Field<T> field, VMutableArray<T> &dst)
@@ -346,13 +355,7 @@ class FieldEvaluator : NonMovable, NonCopyable {
* \note: When the output may only be used as a single value, the version of this function with
* a virtual array result array should be used.
*/
- int add_with_destination(GField field, GMutableSpan dst)
- {
- const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
- dst_hints_.append(&scope_.construct<GVMutableArray_For_GMutableSpan>(__func__, dst));
- output_pointer_infos_.append({});
- return field_index;
- }
+ int add_with_destination(GField field, GMutableSpan dst);
/**
* \param field: Field to add to the evaluator.
@@ -368,16 +371,7 @@ class FieldEvaluator : NonMovable, NonCopyable {
return field_index;
}
- int add(GField field, const GVArray **varray_ptr)
- {
- const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
- dst_hints_.append(nullptr);
- output_pointer_infos_.append(OutputPointerInfo{
- varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &UNUSED(scope)) {
- *(const GVArray **)dst = &varray;
- }});
- return field_index;
- }
+ int add(GField field, const GVArray **varray_ptr);
/**
* \param field: Field to add to the evaluator.
@@ -399,34 +393,12 @@ class FieldEvaluator : NonMovable, NonCopyable {
/**
* \return Index of the field in the evaluator which can be used in the #get_evaluated methods.
*/
- int add(GField field)
- {
- const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
- dst_hints_.append(nullptr);
- output_pointer_infos_.append({});
- return field_index;
- }
+ int add(GField field);
/**
* Evaluate all fields on the evaluator. This can only be called once.
*/
- void evaluate()
- {
- BLI_assert_msg(!is_evaluated_, "Cannot evaluate fields twice.");
- Array<GFieldRef> fields(fields_to_evaluate_.size());
- for (const int i : fields_to_evaluate_.index_range()) {
- fields[i] = fields_to_evaluate_[i];
- }
- evaluated_varrays_ = evaluate_fields(scope_, fields, mask_, context_, dst_hints_);
- BLI_assert(fields_to_evaluate_.size() == evaluated_varrays_
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list