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. @@ _______________________________________________ Bf-blender-cvs mailing list [email protected] List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs
