Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh9
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc16
-rw-r--r--source/blender/editors/space_node/node_draw.cc83
-rw-r--r--source/blender/functions/FN_field.hh59
-rw-r--r--source/blender/functions/FN_multi_function_builder.hh7
-rw-r--r--source/blender/functions/intern/field.cc72
-rw-r--r--source/blender/functions/intern/multi_function_builder.cc21
-rw-r--r--source/blender/functions/tests/FN_multi_function_test.cc2
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc10
9 files changed, 237 insertions, 42 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index c3d594d7dcd..3da35cb4fe1 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -657,10 +657,17 @@ class AttributeFieldInput : public fn::FieldInput {
{
}
+ StringRefNull attribute_name() const
+ {
+ return name_;
+ }
+
const GVArray *get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
+ std::string socket_inspection_name() const override;
+
uint64_t hash() const override;
bool is_equal_to(const fn::FieldNode &other) const override;
};
@@ -683,6 +690,8 @@ class AnonymousAttributeFieldInput : public fn::FieldInput {
IndexMask mask,
ResourceScope &scope) const override;
+ std::string socket_inspection_name() const override;
+
uint64_t hash() const override;
bool is_equal_to(const fn::FieldNode &other) const override;
};
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index 08bd5dc5981..2a5bb99a18b 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -32,6 +32,8 @@
#include "BLI_float2.hh"
#include "BLI_span.hh"
+#include "BLT_translation.h"
+
#include "CLG_log.h"
#include "NOD_type_conversions.hh"
@@ -1318,6 +1320,13 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex
return nullptr;
}
+std::string AttributeFieldInput::socket_inspection_name() const
+{
+ std::stringstream ss;
+ ss << TIP_("Attribute: ") << name_;
+ return ss.str();
+}
+
uint64_t AttributeFieldInput::hash() const
{
return get_default_hash_2(name_, type_);
@@ -1346,6 +1355,13 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context(
return nullptr;
}
+std::string AnonymousAttributeFieldInput::socket_inspection_name() const
+{
+ std::stringstream ss;
+ ss << TIP_("Anonymous Attribute: ") << debug_name_;
+ return ss.str();
+}
+
uint64_t AnonymousAttributeFieldInput::hash() const
{
return get_default_hash_2(anonymous_id_.get(), type_);
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 5b4e3b3b6f5..aa241071425 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -41,10 +41,12 @@
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_geometry_set.hh"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -78,6 +80,8 @@
#include "NOD_geometry_nodes_eval_log.hh"
+#include "FN_field_cpp_type.hh"
+
#include "node_intern.h" /* own include */
#ifdef WITH_COMPOSITOR
@@ -88,7 +92,11 @@ using blender::Map;
using blender::Set;
using blender::Span;
using blender::Vector;
+using blender::VectorSet;
using blender::fn::CPPType;
+using blender::fn::FieldCPPType;
+using blender::fn::FieldInput;
+using blender::fn::GField;
using blender::fn::GPointer;
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
@@ -850,31 +858,70 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal
};
const GPointer value = value_log.value();
- if (value.is_type<int>()) {
- ss << *value.get<int>() << TIP_(" (Integer)");
- }
- else if (value.is_type<float>()) {
- ss << *value.get<float>() << TIP_(" (Float)");
- }
- else if (value.is_type<blender::float3>()) {
- ss << *value.get<blender::float3>() << TIP_(" (Vector)");
- }
- else if (value.is_type<bool>()) {
- ss << (*value.get<bool>() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
- }
- else if (value.is_type<std::string>()) {
- ss << *value.get<std::string>() << TIP_(" (String)");
+ const CPPType &type = *value.type();
+ if (const FieldCPPType *field_type = dynamic_cast<const FieldCPPType *>(&type)) {
+ const CPPType &base_type = field_type->field_type();
+ BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer);
+ const GField &field = field_type->get_gfield(value.get());
+ if (field.node().depends_on_input()) {
+ if (base_type.is<int>()) {
+ ss << TIP_("Integer Field");
+ }
+ else if (base_type.is<float>()) {
+ ss << TIP_("Float Field");
+ }
+ else if (base_type.is<blender::float3>()) {
+ ss << TIP_("Vector Field");
+ }
+ else if (base_type.is<bool>()) {
+ ss << TIP_("Boolean Field");
+ }
+ else if (base_type.is<std::string>()) {
+ ss << TIP_("String Field");
+ }
+ ss << TIP_(" based on:\n");
+
+ /* Use vector set to deduplicate inputs. */
+ VectorSet<std::reference_wrapper<const FieldInput>> field_inputs;
+ field.node().foreach_field_input(
+ [&](const FieldInput &field_input) { field_inputs.add(field_input); });
+ for (const FieldInput &field_input : field_inputs) {
+ ss << "\u2022 " << field_input.socket_inspection_name();
+ if (field_input != field_inputs.as_span().last().get()) {
+ ss << ".\n";
+ }
+ }
+ }
+ else {
+ blender::fn::evaluate_constant_field(field, buffer);
+ if (base_type.is<int>()) {
+ ss << *(int *)buffer << TIP_(" (Integer)");
+ }
+ else if (base_type.is<float>()) {
+ ss << *(float *)buffer << TIP_(" (Float)");
+ }
+ else if (base_type.is<blender::float3>()) {
+ ss << *(blender::float3 *)buffer << TIP_(" (Vector)");
+ }
+ else if (base_type.is<bool>()) {
+ ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
+ }
+ else if (base_type.is<std::string>()) {
+ ss << *(std::string *)buffer << TIP_(" (String)");
+ }
+ base_type.destruct(buffer);
+ }
}
- else if (value.is_type<Object *>()) {
+ else if (type.is<Object *>()) {
id_to_inspection_string((ID *)*value.get<Object *>(), ID_OB);
}
- else if (value.is_type<Material *>()) {
+ else if (type.is<Material *>()) {
id_to_inspection_string((ID *)*value.get<Material *>(), ID_MA);
}
- else if (value.is_type<Tex *>()) {
+ else if (type.is<Tex *>()) {
id_to_inspection_string((ID *)*value.get<Tex *>(), ID_TE);
}
- else if (value.is_type<Collection *>()) {
+ else if (type.is<Collection *>()) {
id_to_inspection_string((ID *)*value.get<Collection *>(), ID_GR);
}
}
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index 79a6faf499b..976d260d91b 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -46,6 +46,7 @@
* they share common sub-fields and a common context.
*/
+#include "BLI_function_ref.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
@@ -57,15 +58,27 @@
namespace blender::fn {
+class FieldInput;
+
/**
* A node in a field-tree. It has at least one output that can be referenced by fields.
*/
class FieldNode {
private:
bool is_input_;
+ /**
+ * True when this node is a #FieldInput or (potentially indirectly) depends on one. This could
+ * always be derived again later by traversing the field-tree, but keeping track of it while the
+ * field is built is cheaper.
+ *
+ * If this is false, the field is constant. Note that even when this is true, the field may be
+ * constant when all inputs are constant.
+ */
+ bool depends_on_input_;
public:
- FieldNode(bool is_input) : is_input_(is_input)
+ FieldNode(bool is_input, bool depends_on_input)
+ : is_input_(is_input), depends_on_input_(depends_on_input)
{
}
@@ -83,6 +96,17 @@ class FieldNode {
return !is_input_;
}
+ bool depends_on_input() const
+ {
+ return depends_on_input_;
+ }
+
+ /**
+ * Invoke callback for every field input. It might be called multiple times for the same input.
+ * The caller is responsible for deduplication if required.
+ */
+ virtual void foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const = 0;
+
virtual uint64_t hash() const
{
return get_default_hash(this);
@@ -93,6 +117,11 @@ class FieldNode {
return a.is_equal_to(b);
}
+ friend bool operator!=(const FieldNode &a, const FieldNode &b)
+ {
+ return !(a == b);
+ }
+
virtual bool is_equal_to(const FieldNode &other) const
{
return this == &other;
@@ -211,16 +240,8 @@ class FieldOperation : public FieldNode {
blender::Vector<GField> inputs_;
public:
- FieldOperation(std::unique_ptr<const MultiFunction> function, Vector<GField> inputs = {})
- : FieldNode(false), owned_function_(std::move(function)), inputs_(std::move(inputs))
- {
- function_ = owned_function_.get();
- }
-
- FieldOperation(const MultiFunction &function, Vector<GField> inputs = {})
- : FieldNode(false), function_(&function), inputs_(std::move(inputs))
- {
- }
+ FieldOperation(std::unique_ptr<const MultiFunction> function, Vector<GField> inputs = {});
+ FieldOperation(const MultiFunction &function, Vector<GField> inputs = {});
Span<GField> inputs() const
{
@@ -247,6 +268,8 @@ class FieldOperation : public FieldNode {
BLI_assert_unreachable();
return CPPType::get<float>();
}
+
+ void foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const override;
};
class FieldContext;
@@ -260,10 +283,7 @@ class FieldInput : public FieldNode {
std::string debug_name_;
public:
- FieldInput(const CPPType &type, std::string debug_name = "")
- : FieldNode(true), type_(&type), debug_name_(std::move(debug_name))
- {
- }
+ FieldInput(const CPPType &type, std::string debug_name = "");
/**
* Get the value of this specific input based on the given context. The returned virtual array,
@@ -273,6 +293,11 @@ class FieldInput : public FieldNode {
IndexMask mask,
ResourceScope &scope) const = 0;
+ virtual std::string socket_inspection_name() const
+ {
+ return debug_name_;
+ }
+
blender::StringRef debug_name() const
{
return debug_name_;
@@ -289,6 +314,8 @@ class FieldInput : public FieldNode {
UNUSED_VARS_NDEBUG(output_index);
return *type_;
}
+
+ void foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const override;
};
/**
@@ -453,4 +480,6 @@ template<typename T> Field<T> make_constant_field(T value)
return Field<T>{GField{std::move(operation), 0}};
}
+GField make_field_constant_if_possible(GField field);
+
} // namespace blender::fn
diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh
index d13615ced07..0ce05cbca30 100644
--- a/source/blender/functions/FN_multi_function_builder.hh
+++ b/source/blender/functions/FN_multi_function_builder.hh
@@ -326,18 +326,21 @@ template<typename From, typename To> class CustomMF_Convert : public MultiFuncti
/**
* A multi-function that outputs the same value every time. The value is not owned by an instance
- * of this function. The caller is responsible for destructing and freeing the value.
+ * of this function. If #make_value_copy is false, the caller is responsible for destructing and
+ * freeing the value.
*/
class CustomMF_GenericConstant : public MultiFunction {
private:
const CPPType &type_;
const void *value_;
MFSignature signature_;
+ bool owns_value_;
template<typename T> friend class CustomMF_Constant;
public:
- CustomMF_GenericConstant(const CPPType &type, const void *value);
+ CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy);
+ ~CustomMF_GenericConstant();
void call(IndexMask mask, MFParams params, MFContext context) const override;
uint64_t hash() const override;
bool equals(const MultiFunction &other) const override;
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index fa7dea97b7f..6133658d8e3 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -462,6 +462,29 @@ void evaluate_constant_field(const GField &field, void *r_value)
varrays[0]->get_to_uninitialized(0, r_value);
}
+/**
+ * If the field depends on some input, the same field is returned.
+ * Otherwise the field is evaluated and a new field is created that just computes this constant.
+ *
+ * Making the field constant has two benefits:
+ * - The field-tree becomes a single node, which is more efficient when the field is evaluated many
+ * times.
+ * - Memory of the input fields may be freed.
+ */
+GField make_field_constant_if_possible(GField field)
+{
+ if (field.node().depends_on_input()) {
+ return field;
+ }
+ const CPPType &type = field.cpp_type();
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+ evaluate_constant_field(field, buffer);
+ auto constant_fn = std::make_unique<CustomMF_GenericConstant>(type, buffer, true);
+ type.destruct(buffer);
+ auto operation = std::make_shared<FieldOperation>(std::move(constant_fn));
+ return GField{operation, 0};
+}
+
const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input,
IndexMask mask,
ResourceScope &scope) const
@@ -472,6 +495,55 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input,
}
/* --------------------------------------------------------------------
+ * FieldOperation.
+ */
+
+FieldOperation::FieldOperation(std::unique_ptr<const MultiFunction> function,
+ Vector<GField> inputs)
+ : FieldOperation(*function, std::move(inputs))
+{
+ owned_function_ = std::move(function);
+}
+
+static bool any_field_depends_on_input(Span<GField> fields)
+{
+ for (const GField &field : fields) {
+ if (field.node().depends_on_input()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+FieldOperation::FieldOperation(const MultiFunction &function, Vector<GField> inputs)
+ : FieldNode(false, any_field_depends_on_input(inputs)),
+ function_(&function),
+ inputs_(std::move(inputs))
+{
+}
+
+void FieldOperation::foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const
+{
+ for (const GField &field : inputs_) {
+ field.node().foreach_field_input(foreach_fn);
+ }
+}
+
+/* --------------------------------------------------------------------
+ * FieldInput.
+ */
+
+FieldInput::FieldInput(const CPPType &type, std::string debug_name)
+ : FieldNode(true, true), type_(&type), debug_name_(std::move(debug_name))
+{
+}
+
+void FieldInput::foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const
+{
+ foreach_fn(*this);
+}
+
+/* --------------------------------------------------------------------
* FieldEvaluator.
*/
diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc
index 180d1f17a54..f891f162820 100644
--- a/source/blender/functions/intern/multi_function_builder.cc
+++ b/source/blender/functions/intern/multi_function_builder.cc
@@ -20,9 +20,18 @@
namespace blender::fn {
-CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, const void *value)
- : type_(type), value_(value)
+CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type,
+ const void *value,
+ bool make_value_copy)
+ : type_(type), owns_value_(make_value_copy)
{
+ if (make_value_copy) {
+ void *copied_value = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
+ type.copy_construct(value, copied_value);
+ value = copied_value;
+ }
+ value_ = value;
+
MFSignatureBuilder signature{"Constant " + type.name()};
std::stringstream ss;
type.print_or_default(value, ss, type.name());
@@ -31,6 +40,14 @@ CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, const vo
this->set_signature(&signature_);
}
+CustomMF_GenericConstant::~CustomMF_GenericConstant()
+{
+ if (owns_value_) {
+ signature_.param_types[0].data_type().single_type().destruct((void *)value_);
+ MEM_freeN((void *)value_);
+ }
+}
+
void CustomMF_GenericConstant::call(IndexMask mask,
MFParams params,
MFContext UNUSED(context)) const
diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc
index 91c72a51dd6..204dbfab6aa 100644
--- a/source/blender/functions/tests/FN_multi_function_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_test.cc
@@ -263,7 +263,7 @@ TEST(multi_function, CustomMF_Constant)
TEST(multi_function, CustomMF_GenericConstant)
{
int value = 42;
- CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value};
+ CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value, false};
EXPECT_EQ(fn.param_name(0), "42");
Array<int> outputs(4, 0);
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index 4f21924619b..f67f7f967c9 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -892,8 +892,10 @@ class GeometryNodesEvaluator {
OutputState &output_state = node_state.outputs[i];
const DOutputSocket socket{node.context(), &socket_ref};
const CPPType *cpp_type = get_socket_cpp_type(socket_ref);
- GField &field = *allocator.construct<GField>(operation, output_index).release();
- this->forward_output(socket, {cpp_type, &field});
+ GField new_field{operation, output_index};
+ new_field = fn::make_field_constant_if_possible(std::move(new_field));
+ GField &field_to_forward = *allocator.construct<GField>(std::move(new_field)).release();
+ this->forward_output(socket, {cpp_type, &field_to_forward});
output_state.has_been_computed = true;
output_index++;
}
@@ -1411,8 +1413,8 @@ class GeometryNodesEvaluator {
{
if (const FieldCPPType *field_cpp_type = dynamic_cast<const FieldCPPType *>(&type)) {
const CPPType &base_type = field_cpp_type->field_type();
- auto constant_fn = std::make_unique<fn::CustomMF_GenericConstant>(base_type,
- base_type.default_value());
+ auto constant_fn = std::make_unique<fn::CustomMF_GenericConstant>(
+ base_type, base_type.default_value(), false);
auto operation = std::make_shared<fn::FieldOperation>(std::move(constant_fn));
new (r_value) GField(std::move(operation), 0);
return;