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:
authorJacques Lucke <jacques@blender.org>2021-09-11 14:05:20 +0300
committerJacques Lucke <jacques@blender.org>2021-09-11 14:05:20 +0300
commit4e78b89e487e9b9707d583c6b2578ad122c59d5e (patch)
tree620c9d0e0114488328cc55187dadfdb4f8a05754
parent166c8be7ac018c461514453b9947a23b7ac2ef26 (diff)
Geometry Nodes: add field support for socket inspection
Since fields were committed to master, socket inspection did not work correctly for all socket types anymore. Now the same functionality as before is back. Furthermore, fields that depend on some input will now show the inputs in the socket inspection. I added support for evaluating constant fields more immediately. This has the benefit that the same constant field is not evaluated more than once. It also helps with making the field independent of the multi-functions that it uses. We might still want to change the ownership handling for the multi-functions of nodes a bit, but that can be done separately. Differential Revision: https://developer.blender.org/D12444
-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;