[Bf-blender-cvs] [47276b84701] master: Geometry Nodes: reduce overhead when processing single values

Jacques Lucke noreply at git.blender.org
Tue Nov 23 14:50:00 CET 2021


Commit: 47276b84701727c2f187c77f1ec502b4ca4963bd
Author: Jacques Lucke
Date:   Tue Nov 23 14:47:25 2021 +0100
Branches: master
https://developer.blender.org/rB47276b84701727c2f187c77f1ec502b4ca4963bd

Geometry Nodes: reduce overhead when processing single values

Currently the geometry nodes evaluator always stores a field for every
type that supports it, even if it is just a single value. This results in a lot
of overhead when there are many sockets that just contain a single
value, which is often the case.

This introduces a new `ValueOrField<T>` type that is used by the geometry
nodes evaluator. Now a field will only be created when it is actually
necessary. See D13307 for more details. In extrem cases this can speed
up the evaluation 2-3x (those cases are probably never hit in practice
though, but it's good to get rid of unnecessary overhead nevertheless).

Differential Revision: https://developer.blender.org/D13307

===================================================================

M	source/blender/editors/space_node/node_draw.cc
M	source/blender/functions/FN_field.hh
M	source/blender/functions/FN_field_cpp_type.hh
M	source/blender/functions/intern/field.cc
M	source/blender/modifiers/intern/MOD_nodes.cc
M	source/blender/modifiers/intern/MOD_nodes_evaluator.cc
M	source/blender/nodes/NOD_geometry_exec.hh
M	source/blender/nodes/intern/geometry_nodes_eval_log.cc
M	source/blender/nodes/intern/node_socket.cc

===================================================================

diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 867544d0805..f9a9e491403 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -843,29 +843,43 @@ struct SocketTooltipData {
   bNodeSocket *socket;
 };
 
-static void create_inspection_string_for_generic_value(const geo_log::GenericValueLog &value_log,
-                                                       std::stringstream &ss)
+static void create_inspection_string_for_generic_value(const GPointer value, std::stringstream &ss)
 {
   auto id_to_inspection_string = [&](ID *id, short idcode) {
     ss << (id ? id->name + 2 : TIP_("None")) << " (" << BKE_idtype_idcode_to_name(idcode) << ")";
   };
 
-  const GPointer value = value_log.value();
   const CPPType &type = *value.type();
+  const void *buffer = value.get();
   if (type.is<Object *>()) {
-    id_to_inspection_string((ID *)*value.get<Object *>(), ID_OB);
+    id_to_inspection_string((ID *)buffer, ID_OB);
   }
   else if (type.is<Material *>()) {
-    id_to_inspection_string((ID *)*value.get<Material *>(), ID_MA);
+    id_to_inspection_string((ID *)buffer, ID_MA);
   }
   else if (type.is<Tex *>()) {
-    id_to_inspection_string((ID *)*value.get<Tex *>(), ID_TE);
+    id_to_inspection_string((ID *)buffer, ID_TE);
   }
   else if (type.is<Image *>()) {
-    id_to_inspection_string((ID *)*value.get<Image *>(), ID_IM);
+    id_to_inspection_string((ID *)buffer, ID_IM);
   }
   else if (type.is<Collection *>()) {
-    id_to_inspection_string((ID *)*value.get<Collection *>(), ID_GR);
+    id_to_inspection_string((ID *)buffer, ID_GR);
+  }
+  else if (type.is<int>()) {
+    ss << *(int *)buffer << TIP_(" (Integer)");
+  }
+  else if (type.is<float>()) {
+    ss << *(float *)buffer << TIP_(" (Float)");
+  }
+  else if (type.is<blender::float3>()) {
+    ss << *(blender::float3 *)buffer << TIP_(" (Vector)");
+  }
+  else if (type.is<bool>()) {
+    ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
+  }
+  else if (type.is<std::string>()) {
+    ss << *(std::string *)buffer << TIP_(" (String)");
   }
 }
 
@@ -880,21 +894,7 @@ static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &v
     if (field) {
       BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
       blender::fn::evaluate_constant_field(field, buffer);
-      if (type.is<int>()) {
-        ss << *(int *)buffer << TIP_(" (Integer)");
-      }
-      else if (type.is<float>()) {
-        ss << *(float *)buffer << TIP_(" (Float)");
-      }
-      else if (type.is<blender::float3>()) {
-        ss << *(blender::float3 *)buffer << TIP_(" (Vector)");
-      }
-      else if (type.is<bool>()) {
-        ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
-      }
-      else if (type.is<std::string>()) {
-        ss << *(std::string *)buffer << TIP_(" (String)");
-      }
+      create_inspection_string_for_generic_value({type, buffer}, ss);
       type.destruct(buffer);
     }
     else {
@@ -1023,7 +1023,7 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C,
   std::stringstream ss;
   if (const geo_log::GenericValueLog *generic_value_log =
           dynamic_cast<const geo_log::GenericValueLog *>(value_log)) {
-    create_inspection_string_for_generic_value(*generic_value_log, ss);
+    create_inspection_string_for_generic_value(generic_value_log->value(), ss);
   }
   if (const geo_log::GFieldValueLog *gfield_value_log =
           dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) {
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index fb488fdbfa9..28bb120bb17 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -178,11 +178,19 @@ class GFieldRef : public GFieldBase<const FieldNode *> {
   }
 };
 
+namespace detail {
+/* Utility class to make #is_field_v work. */
+struct TypedFieldBase {
+};
+}  // namespace detail
+
 /**
  * A typed version of #GField. It has the same memory layout as #GField.
  */
-template<typename T> class Field : public GField {
+template<typename T> class Field : public GField, detail::TypedFieldBase {
  public:
+  using base_type = T;
+
   Field() = default;
 
   Field(GField field) : GField(std::move(field))
@@ -196,6 +204,11 @@ template<typename T> class Field : public GField {
   }
 };
 
+/** True when T is any Field<...> type. */
+template<typename T>
+static constexpr bool is_field_v = std::is_base_of_v<detail::TypedFieldBase, T> &&
+                                   !std::is_same_v<detail::TypedFieldBase, T>;
+
 /**
  * A #FieldNode that allows composing existing fields into new fields.
  */
@@ -419,6 +432,8 @@ template<typename T> Field<T> make_constant_field(T value)
   return Field<T>{GField{std::move(operation), 0}};
 }
 
+GField make_constant_field(const CPPType &type, const void *value);
+
 GField make_field_constant_if_possible(GField field);
 
 class IndexFieldInput final : public FieldInput {
@@ -437,6 +452,52 @@ class IndexFieldInput final : public FieldInput {
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name Value or Field Class
+ *
+ * Utility class that wraps a single value and a field, to simplify accessing both of the types.
+ * \{ */
+
+template<typename T> struct ValueOrField {
+  /** Value that is used when the field is empty. */
+  T value{};
+  Field<T> field;
+
+  ValueOrField() = default;
+
+  ValueOrField(T value) : value(std::move(value))
+  {
+  }
+
+  ValueOrField(Field<T> field) : field(std::move(field))
+  {
+  }
+
+  bool is_field() const
+  {
+    return (bool)this->field;
+  }
+
+  Field<T> as_field() const
+  {
+    if (this->field) {
+      return this->field;
+    }
+    return make_constant_field(this->value);
+  }
+
+  T as_value() const
+  {
+    if (this->field) {
+      /* This returns a default value when the field is not constant. */
+      return evaluate_constant_field(this->field);
+    }
+    return this->value;
+  }
+};
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name #FieldNode Inline Methods
  * \{ */
diff --git a/source/blender/functions/FN_field_cpp_type.hh b/source/blender/functions/FN_field_cpp_type.hh
index c3636fb3c7b..940faba6d70 100644
--- a/source/blender/functions/FN_field_cpp_type.hh
+++ b/source/blender/functions/FN_field_cpp_type.hh
@@ -60,6 +60,84 @@ class FieldCPPType : public CPPType {
   }
 };
 
+class ValueOrFieldCPPType : public CPPType {
+ private:
+  const CPPType &base_type_;
+  void (*construct_from_value_)(void *dst, const void *value);
+  void (*construct_from_field_)(void *dst, GField field);
+  const void *(*get_value_ptr_)(const void *value_or_field);
+  const GField *(*get_field_ptr_)(const void *value_or_field);
+  bool (*is_field_)(const void *value_or_field);
+  GField (*as_field_)(const void *value_or_field);
+
+ public:
+  template<typename T>
+  ValueOrFieldCPPType(FieldCPPTypeParam<ValueOrField<T>> /* unused */, StringRef debug_name)
+      : CPPType(CPPTypeParam<ValueOrField<T>, CPPTypeFlags::None>(), debug_name),
+        base_type_(CPPType::get<T>())
+  {
+    construct_from_value_ = [](void *dst, const void *value_or_field) {
+      new (dst) ValueOrField<T>(*(const T *)value_or_field);
+    };
+    construct_from_field_ = [](void *dst, GField field) {
+      new (dst) ValueOrField<T>(Field<T>(std::move(field)));
+    };
+    get_value_ptr_ = [](const void *value_or_field) {
+      return (const void *)&((ValueOrField<T> *)value_or_field)->value;
+    };
+    get_field_ptr_ = [](const void *value_or_field) -> const GField * {
+      return &((ValueOrField<T> *)value_or_field)->field;
+    };
+    is_field_ = [](const void *value_or_field) {
+      return ((ValueOrField<T> *)value_or_field)->is_field();
+    };
+    as_field_ = [](const void *value_or_field) -> GField {
+      return ((ValueOrField<T> *)value_or_field)->as_field();
+    };
+  }
+
+  const CPPType &base_type() const
+  {
+    return base_type_;
+  }
+
+  void construct_from_value(void *dst, const void *value) const
+  {
+    construct_from_value_(dst, value);
+  }
+
+  void construct_from_field(void *dst, GField field) const
+  {
+    construct_from_field_(dst, field);
+  }
+
+  const void *get_value_ptr(const void *value_or_field) const
+  {
+    return get_value_ptr_(value_or_field);
+  }
+
+  void *get_value_ptr(void *value_or_field) const
+  {
+    /* Use `const_cast` to avoid duplicating the callback for the non-const case. */
+    return const_cast<void *>(get_value_ptr_(value_or_field));
+  }
+
+  const GField *get_field_ptr(const void *value_or_field) const
+  {
+    return get_field_ptr_(value_or_field);
+  }
+
+  bool is_field(const void *value_or_field) const
+  {
+    return is_field_(value_or_field);
+  }
+
+  GField as_field(const void *value_or_field) const
+  {
+    return as_field_(value_or_field);
+  }
+};
+
 }  // namespace blender::fn
 
 #define MAKE_FIELD_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \
@@ -69,4 +147,13 @@ class FieldCPPType : public CPPType {
     static blender::fn::FieldCPPType cpp_type{ \
         blender::fn::FieldCPPTypeParam<blender::fn::Field<FIELD_TYPE>>(), STRINGIFY(DEBUG_NAME)}; \
     return cpp_type; \
+  } \
+  template<> \
+  const blender::fn::CPPType & \
+  blender::fn::CPPType::get_impl<blender::fn::ValueOrField<FIELD_TYPE>>() \
+  { \
+    static blender::fn::ValueOrFieldCPPType cpp_type{ \
+        blender::fn::FieldCPPTypeParam<blender::fn::ValueOrField<FIELD_TYPE>>(), \
+        STRINGIFY(DEBUG_NAME##OrValue)}; \
+    return cpp_type; \
   }
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index 91b1bdfd8f0..7934490a6d9 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -511,10 +511,16 @@ GField make_field_constant_if_possible(GField 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, buff

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list