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
This commit is contained in:
parent
0bedd5d14f
commit
47276b8470
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
* \{ */
|
||||
|
|
|
@ -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; \
|
||||
}
|
||||
|
|
|
@ -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, buffer, true);
|
||||
GField new_field = make_constant_field(type, buffer);
|
||||
type.destruct(buffer);
|
||||
return new_field;
|
||||
}
|
||||
|
||||
GField make_constant_field(const CPPType &type, const void *value)
|
||||
{
|
||||
auto constant_fn = std::make_unique<CustomMF_GenericConstant>(type, value, true);
|
||||
auto operation = std::make_shared<FieldOperation>(std::move(constant_fn));
|
||||
return GField{operation, 0};
|
||||
return GField{std::move(operation), 0};
|
||||
}
|
||||
|
||||
GVArray FieldContext::get_varray_for_input(const FieldInput &field_input,
|
||||
|
|
|
@ -98,6 +98,7 @@
|
|||
#include "NOD_node_declaration.hh"
|
||||
|
||||
#include "FN_field.hh"
|
||||
#include "FN_field_cpp_type.hh"
|
||||
#include "FN_multi_function.hh"
|
||||
|
||||
using blender::Array;
|
||||
|
@ -113,9 +114,11 @@ using blender::StringRef;
|
|||
using blender::StringRefNull;
|
||||
using blender::Vector;
|
||||
using blender::bke::OutputAttribute;
|
||||
using blender::fn::Field;
|
||||
using blender::fn::GField;
|
||||
using blender::fn::GMutablePointer;
|
||||
using blender::fn::GPointer;
|
||||
using blender::fn::ValueOrField;
|
||||
using blender::nodes::FieldInferencingInterface;
|
||||
using blender::nodes::GeoNodeExecParams;
|
||||
using blender::nodes::InputSocketFieldType;
|
||||
|
@ -491,35 +494,34 @@ static void init_socket_cpp_value_from_property(const IDProperty &property,
|
|||
else if (property.type == IDP_DOUBLE) {
|
||||
value = (float)IDP_Double(&property);
|
||||
}
|
||||
new (r_value) blender::fn::Field<float>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<float>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_INT: {
|
||||
int value = IDP_Int(&property);
|
||||
new (r_value) blender::fn::Field<int>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<int>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
float3 value;
|
||||
copy_v3_v3(value, (const float *)IDP_Array(&property));
|
||||
new (r_value) blender::fn::Field<float3>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<float3>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_RGBA: {
|
||||
blender::ColorGeometry4f value;
|
||||
copy_v4_v4((float *)value, (const float *)IDP_Array(&property));
|
||||
new (r_value) blender::fn::Field<ColorGeometry4f>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<ColorGeometry4f>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_BOOLEAN: {
|
||||
bool value = IDP_Int(&property) != 0;
|
||||
new (r_value) blender::fn::Field<bool>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<bool>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
std::string value = IDP_String(&property);
|
||||
new (r_value)
|
||||
blender::fn::Field<std::string>(blender::fn::make_constant_field(std::move(value)));
|
||||
new (r_value) ValueOrField<std::string>(std::move(value));
|
||||
break;
|
||||
}
|
||||
case SOCK_OBJECT: {
|
||||
|
@ -740,7 +742,12 @@ static void initialize_group_input(NodesModifierData &nmd,
|
|||
const StringRef attribute_name{IDP_String(property_attribute_name)};
|
||||
auto attribute_input = std::make_shared<blender::bke::AttributeFieldInput>(
|
||||
attribute_name, *socket_type.base_cpp_type);
|
||||
new (r_value) blender::fn::GField(std::move(attribute_input), 0);
|
||||
GField attribute_field{std::move(attribute_input), 0};
|
||||
const blender::fn::ValueOrFieldCPPType *cpp_type =
|
||||
dynamic_cast<const blender::fn::ValueOrFieldCPPType *>(
|
||||
socket_type.geometry_nodes_cpp_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
cpp_type->construct_from_field(r_value, std::move(attribute_field));
|
||||
}
|
||||
else {
|
||||
init_socket_cpp_value_from_property(
|
||||
|
@ -904,7 +911,11 @@ static void store_output_value_in_geometry(GeometrySet &geometry_set,
|
|||
if (attribute_name.is_empty()) {
|
||||
return;
|
||||
}
|
||||
const GField &field = *(const GField *)value.get();
|
||||
const blender::fn::ValueOrFieldCPPType *cpp_type =
|
||||
dynamic_cast<const blender::fn::ValueOrFieldCPPType *>(value.type());
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
|
||||
const GField field = cpp_type->as_field(value.get());
|
||||
const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&nmd->node_group->outputs,
|
||||
socket.index());
|
||||
const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain;
|
||||
|
|
|
@ -39,9 +39,11 @@ namespace blender::modifiers::geometry_nodes {
|
|||
|
||||
using fn::CPPType;
|
||||
using fn::Field;
|
||||
using fn::FieldCPPType;
|
||||
using fn::GField;
|
||||
using fn::GValueMap;
|
||||
using fn::GVArray;
|
||||
using fn::ValueOrField;
|
||||
using fn::ValueOrFieldCPPType;
|
||||
using nodes::GeoNodeExecParams;
|
||||
using namespace fn::multi_function_types;
|
||||
|
||||
|
@ -348,18 +350,19 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value)
|
|||
GEO_NODE_CURVE_HANDLE_LEFT ?
|
||||
"handle_left" :
|
||||
"handle_right";
|
||||
new (r_value) Field<float3>(bke::AttributeFieldInput::Create<float3>(side));
|
||||
new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side));
|
||||
return true;
|
||||
}
|
||||
new (r_value) Field<float3>(bke::AttributeFieldInput::Create<float3>("position"));
|
||||
new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position"));
|
||||
return true;
|
||||
}
|
||||
if (socket.typeinfo()->type == SOCK_INT) {
|
||||
if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) {
|
||||
new (r_value) Field<int>(std::make_shared<bke::IDAttributeFieldInput>());
|
||||
new (r_value)
|
||||
ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>()));
|
||||
return true;
|
||||
}
|
||||
new (r_value) Field<int>(std::make_shared<fn::IndexFieldInput>());
|
||||
new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -943,8 +946,9 @@ class GeometryNodesEvaluator {
|
|||
|
||||
LinearAllocator<> &allocator = local_allocators_.local();
|
||||
|
||||
/* Prepare the inputs for the multi function. */
|
||||
Vector<GField> input_fields;
|
||||
bool any_input_is_field = false;
|
||||
Vector<const void *, 16> input_values;
|
||||
Vector<const ValueOrFieldCPPType *, 16> input_types;
|
||||
for (const int i : node->inputs().index_range()) {
|
||||
const InputSocketRef &socket_ref = node->input(i);
|
||||
if (!socket_ref.is_available()) {
|
||||
|
@ -955,7 +959,37 @@ class GeometryNodesEvaluator {
|
|||
BLI_assert(input_state.was_ready_for_execution);
|
||||
SingleInputValue &single_value = *input_state.value.single;
|
||||
BLI_assert(single_value.value != nullptr);
|
||||
input_fields.append(std::move(*(GField *)single_value.value));
|
||||
const ValueOrFieldCPPType &field_cpp_type = static_cast<const ValueOrFieldCPPType &>(
|
||||
*input_state.type);
|
||||
input_values.append(single_value.value);
|
||||
input_types.append(&field_cpp_type);
|
||||
if (field_cpp_type.is_field(single_value.value)) {
|
||||
any_input_is_field = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (any_input_is_field) {
|
||||
this->execute_multi_function_node__field(
|
||||
node, fn_item, node_state, allocator, input_values, input_types);
|
||||
}
|
||||
else {
|
||||
this->execute_multi_function_node__value(
|
||||
node, *fn_item.fn, node_state, allocator, input_values, input_types);
|
||||
}
|
||||
}
|
||||
|
||||
void execute_multi_function_node__field(const DNode node,
|
||||
const nodes::NodeMultiFunctions::Item &fn_item,
|
||||
NodeState &node_state,
|
||||
LinearAllocator<> &allocator,
|
||||
Span<const void *> input_values,
|
||||
Span<const ValueOrFieldCPPType *> input_types)
|
||||
{
|
||||
Vector<GField> input_fields;
|
||||
for (const int i : input_values.index_range()) {
|
||||
const void *input_value_or_field = input_values[i];
|
||||
const ValueOrFieldCPPType &field_cpp_type = *input_types[i];
|
||||
input_fields.append(field_cpp_type.as_field(input_value_or_field));
|
||||
}
|
||||
|
||||
std::shared_ptr<fn::FieldOperation> operation;
|
||||
|
@ -966,7 +1000,6 @@ class GeometryNodesEvaluator {
|
|||
operation = std::make_shared<fn::FieldOperation>(*fn_item.fn, std::move(input_fields));
|
||||
}
|
||||
|
||||
/* Forward outputs. */
|
||||
int output_index = 0;
|
||||
for (const int i : node->outputs().index_range()) {
|
||||
const OutputSocketRef &socket_ref = node->output(i);
|
||||
|
@ -975,16 +1008,68 @@ 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);
|
||||
const ValueOrFieldCPPType *cpp_type = static_cast<const ValueOrFieldCPPType *>(
|
||||
get_socket_cpp_type(socket_ref));
|
||||
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});
|
||||
void *buffer = allocator.allocate(cpp_type->size(), cpp_type->alignment());
|
||||
cpp_type->construct_from_field(buffer, std::move(new_field));
|
||||
this->forward_output(socket, {cpp_type, buffer});
|
||||
output_state.has_been_computed = true;
|
||||
output_index++;
|
||||
}
|
||||
}
|
||||
|
||||
void execute_multi_function_node__value(const DNode node,
|
||||
const MultiFunction &fn,
|
||||
NodeState &node_state,
|
||||
LinearAllocator<> &allocator,
|
||||
Span<const void *> input_values,
|
||||
Span<const ValueOrFieldCPPType *> input_types)
|
||||
{
|
||||
MFParamsBuilder params{fn, 1};
|
||||
for (const int i : input_values.index_range()) {
|
||||
const void *input_value_or_field = input_values[i];
|
||||
const ValueOrFieldCPPType &field_cpp_type = *input_types[i];
|
||||
const CPPType &base_type = field_cpp_type.base_type();
|
||||
const void *input_value = field_cpp_type.get_value_ptr(input_value_or_field);
|
||||
params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, input_value));
|
||||
}
|
||||
|
||||
Vector<GMutablePointer, 16> output_buffers;
|
||||
for (const int i : node->outputs().index_range()) {
|
||||
const DOutputSocket socket = node.output(i);
|
||||
if (!socket->is_available()) {
|
||||
output_buffers.append({});
|
||||
continue;
|
||||
}
|
||||
const ValueOrFieldCPPType *value_or_field_type = static_cast<const ValueOrFieldCPPType *>(
|
||||
get_socket_cpp_type(socket));
|
||||
const CPPType &base_type = value_or_field_type->base_type();
|
||||
void *value_or_field_buffer = allocator.allocate(value_or_field_type->size(),
|
||||
value_or_field_type->alignment());
|
||||
value_or_field_type->default_construct(value_or_field_buffer);
|
||||
void *value_buffer = value_or_field_type->get_value_ptr(value_or_field_buffer);
|
||||
base_type.destruct(value_buffer);
|
||||
params.add_uninitialized_single_output(GMutableSpan{base_type, value_buffer, 1});
|
||||
output_buffers.append({value_or_field_type, value_or_field_buffer});
|
||||
}
|
||||
|
||||
MFContextBuilder context;
|
||||
fn.call(IndexRange(1), params, context);
|
||||
|
||||
for (const int i : output_buffers.index_range()) {
|
||||
GMutablePointer buffer = output_buffers[i];
|
||||
if (buffer.get() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const DOutputSocket socket = node.output(i);
|
||||
this->forward_output(socket, buffer);
|
||||
|
||||
OutputState &output_state = node_state.outputs[i];
|
||||
output_state.has_been_computed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void execute_unknown_node(const DNode node, NodeState &node_state)
|
||||
{
|
||||
LinearAllocator<> &allocator = local_allocators_.local();
|
||||
|
@ -1466,18 +1551,28 @@ class GeometryNodesEvaluator {
|
|||
from_type.copy_construct(from_value, to_value);
|
||||
return;
|
||||
}
|
||||
const FieldCPPType *from_field_type = dynamic_cast<const FieldCPPType *>(&from_type);
|
||||
const FieldCPPType *to_field_type = dynamic_cast<const FieldCPPType *>(&to_type);
|
||||
const ValueOrFieldCPPType *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(
|
||||
&from_type);
|
||||
const ValueOrFieldCPPType *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type);
|
||||
|
||||
if (from_field_type != nullptr && to_field_type != nullptr) {
|
||||
const CPPType &from_base_type = from_field_type->base_type();
|
||||
const CPPType &to_base_type = to_field_type->base_type();
|
||||
if (conversions_.is_convertible(from_base_type, to_base_type)) {
|
||||
const MultiFunction &fn = *conversions_.get_conversion_multi_function(
|
||||
MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type));
|
||||
const GField &from_field = *(const GField *)from_value;
|
||||
auto operation = std::make_shared<fn::FieldOperation>(fn, Vector<GField>{from_field});
|
||||
new (to_value) GField(std::move(operation), 0);
|
||||
if (from_field_type->is_field(from_value)) {
|
||||
const GField &from_field = *from_field_type->get_field_ptr(from_value);
|
||||
const MultiFunction &fn = *conversions_.get_conversion_multi_function(
|
||||
MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type));
|
||||
auto operation = std::make_shared<fn::FieldOperation>(fn, Vector<GField>{from_field});
|
||||
to_field_type->construct_from_field(to_value, GField(std::move(operation), 0));
|
||||
}
|
||||
else {
|
||||
to_field_type->default_construct(to_value);
|
||||
const void *from_value_ptr = from_field_type->get_value_ptr(from_value);
|
||||
void *to_value_ptr = to_field_type->get_value_ptr(to_value);
|
||||
conversions_.get_conversion_functions(from_base_type, to_base_type)
|
||||
->convert_single_to_initialized(from_value_ptr, to_value_ptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1493,14 +1588,6 @@ class GeometryNodesEvaluator {
|
|||
|
||||
void construct_default_value(const CPPType &type, void *r_value)
|
||||
{
|
||||
if (const FieldCPPType *field_cpp_type = dynamic_cast<const FieldCPPType *>(&type)) {
|
||||
const CPPType &base_type = field_cpp_type->base_type();
|
||||
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;
|
||||
}
|
||||
type.copy_construct(type.default_value(), r_value);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ using fn::GVArray;
|
|||
using fn::GVArray_GSpan;
|
||||
using fn::GVMutableArray;
|
||||
using fn::GVMutableArray_GSpan;
|
||||
using fn::ValueOrField;
|
||||
using geometry_nodes_eval_log::NodeWarningType;
|
||||
|
||||
/**
|
||||
|
@ -129,7 +130,7 @@ class GeoNodeExecParams {
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
static inline constexpr bool is_stored_as_field_v = std::is_same_v<T, float> ||
|
||||
static inline constexpr bool is_field_base_type_v = std::is_same_v<T, float> ||
|
||||
std::is_same_v<T, int> ||
|
||||
std::is_same_v<T, bool> ||
|
||||
std::is_same_v<T, ColorGeometry4f> ||
|
||||
|
@ -157,9 +158,15 @@ class GeoNodeExecParams {
|
|||
*/
|
||||
template<typename T> T extract_input(StringRef identifier)
|
||||
{
|
||||
if constexpr (is_stored_as_field_v<T>) {
|
||||
Field<T> field = this->extract_input<Field<T>>(identifier);
|
||||
return fn::evaluate_constant_field(field);
|
||||
if constexpr (is_field_base_type_v<T>) {
|
||||
ValueOrField<T> value_or_field = this->extract_input<ValueOrField<T>>(identifier);
|
||||
return value_or_field.as_value();
|
||||
}
|
||||
else if constexpr (fn::is_field_v<T>) {
|
||||
using BaseType = typename T::base_type;
|
||||
ValueOrField<BaseType> value_or_field = this->extract_input<ValueOrField<BaseType>>(
|
||||
identifier);
|
||||
return value_or_field.as_field();
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
|
@ -186,9 +193,9 @@ class GeoNodeExecParams {
|
|||
Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier);
|
||||
Vector<T> values;
|
||||
for (GMutablePointer gvalue : gvalues) {
|
||||
if constexpr (is_stored_as_field_v<T>) {
|
||||
const Field<T> field = gvalue.relocate_out<Field<T>>();
|
||||
values.append(fn::evaluate_constant_field(field));
|
||||
if constexpr (is_field_base_type_v<T>) {
|
||||
const ValueOrField<T> value_or_field = gvalue.relocate_out<ValueOrField<T>>();
|
||||
values.append(value_or_field.as_value());
|
||||
}
|
||||
else {
|
||||
values.append(gvalue.relocate_out<T>());
|
||||
|
@ -200,11 +207,16 @@ class GeoNodeExecParams {
|
|||
/**
|
||||
* Get the input value for the input socket with the given identifier.
|
||||
*/
|
||||
template<typename T> const T get_input(StringRef identifier) const
|
||||
template<typename T> T get_input(StringRef identifier) const
|
||||
{
|
||||
if constexpr (is_stored_as_field_v<T>) {
|
||||
const Field<T> &field = this->get_input<Field<T>>(identifier);
|
||||
return fn::evaluate_constant_field(field);
|
||||
if constexpr (is_field_base_type_v<T>) {
|
||||
ValueOrField<T> value_or_field = this->get_input<ValueOrField<T>>(identifier);
|
||||
return value_or_field.as_value();
|
||||
}
|
||||
else if constexpr (fn::is_field_v<T>) {
|
||||
using BaseType = typename T::base_type;
|
||||
ValueOrField<BaseType> value_or_field = this->get_input<ValueOrField<BaseType>>(identifier);
|
||||
return value_or_field.as_field();
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
|
@ -226,9 +238,12 @@ class GeoNodeExecParams {
|
|||
template<typename T> void set_output(StringRef identifier, T &&value)
|
||||
{
|
||||
using StoredT = std::decay_t<T>;
|
||||
if constexpr (is_stored_as_field_v<StoredT>) {
|
||||
this->set_output<Field<StoredT>>(identifier,
|
||||
fn::make_constant_field<StoredT>(std::forward<T>(value)));
|
||||
if constexpr (is_field_base_type_v<StoredT>) {
|
||||
this->set_output(identifier, ValueOrField<StoredT>(std::forward<T>(value)));
|
||||
}
|
||||
else if constexpr (fn::is_field_v<StoredT>) {
|
||||
using BaseType = typename StoredT::base_type;
|
||||
this->set_output(identifier, ValueOrField<BaseType>(std::forward<T>(value)));
|
||||
}
|
||||
else {
|
||||
const CPPType &type = CPPType::get<StoredT>();
|
||||
|
|
|
@ -31,6 +31,7 @@ using fn::CPPType;
|
|||
using fn::FieldCPPType;
|
||||
using fn::FieldInput;
|
||||
using fn::GField;
|
||||
using fn::ValueOrFieldCPPType;
|
||||
|
||||
ModifierLog::ModifierLog(GeoLogger &logger)
|
||||
: input_geometry_log_(std::move(logger.input_geometry_log_)),
|
||||
|
@ -417,25 +418,38 @@ void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value
|
|||
geometry_set, log_full_geometry);
|
||||
values_.append({copied_sockets, std::move(value_log)});
|
||||
}
|
||||
else if (const FieldCPPType *field_type = dynamic_cast<const FieldCPPType *>(&type)) {
|
||||
GField field = field_type->get_gfield(value.get());
|
||||
bool log_full_field = false;
|
||||
if (!field.node().depends_on_input()) {
|
||||
/* Always log constant fields so that their value can be shown in socket inspection.
|
||||
* In the future we can also evaluate the field here and only store the value. */
|
||||
log_full_field = true;
|
||||
}
|
||||
if (!log_full_field) {
|
||||
for (const DSocket &socket : sockets) {
|
||||
if (main_logger_->log_full_sockets_.contains(socket)) {
|
||||
log_full_field = true;
|
||||
break;
|
||||
else if (const ValueOrFieldCPPType *value_or_field_type =
|
||||
dynamic_cast<const ValueOrFieldCPPType *>(&type)) {
|
||||
const void *value_or_field = value.get();
|
||||
if (value_or_field_type->is_field(value_or_field)) {
|
||||
GField field = *value_or_field_type->get_field_ptr(value_or_field);
|
||||
bool log_full_field = false;
|
||||
if (!field.node().depends_on_input()) {
|
||||
/* Always log constant fields so that their value can be shown in socket inspection.
|
||||
* In the future we can also evaluate the field here and only store the value. */
|
||||
log_full_field = true;
|
||||
}
|
||||
if (!log_full_field) {
|
||||
for (const DSocket &socket : sockets) {
|
||||
if (main_logger_->log_full_sockets_.contains(socket)) {
|
||||
log_full_field = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>(
|
||||
std::move(field), log_full_field);
|
||||
values_.append({copied_sockets, std::move(value_log)});
|
||||
}
|
||||
else {
|
||||
const CPPType &base_type = value_or_field_type->base_type();
|
||||
const void *value = value_or_field_type->get_value_ptr(value_or_field);
|
||||
void *buffer = allocator_->allocate(base_type.size(), base_type.alignment());
|
||||
base_type.copy_construct(value, buffer);
|
||||
destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>(
|
||||
GMutablePointer{base_type, buffer});
|
||||
values_.append({copied_sockets, std::move(value_log)});
|
||||
}
|
||||
destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>(
|
||||
std::move(field), log_full_field);
|
||||
values_.append({copied_sockets, std::move(value_log)});
|
||||
}
|
||||
else {
|
||||
void *buffer = allocator_->allocate(type.size(), type.alignment());
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "FN_field.hh"
|
||||
|
||||
using namespace blender;
|
||||
using blender::fn::ValueOrField;
|
||||
using blender::nodes::SocketDeclarationPtr;
|
||||
|
||||
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
|
||||
|
@ -701,11 +702,11 @@ static bNodeSocketType *make_socket_type_bool()
|
|||
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
*(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value;
|
||||
};
|
||||
socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<blender::fn::Field<bool>>();
|
||||
socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<bool>>();
|
||||
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
bool value;
|
||||
socket.typeinfo->get_base_cpp_value(socket, &value);
|
||||
new (r_value) blender::fn::Field<bool>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<bool>(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
@ -717,11 +718,11 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
|
|||
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
*(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value;
|
||||
};
|
||||
socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<blender::fn::Field<float>>();
|
||||
socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<float>>();
|
||||
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
float value;
|
||||
socket.typeinfo->get_base_cpp_value(socket, &value);
|
||||
new (r_value) blender::fn::Field<float>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<float>(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
@ -733,11 +734,11 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
|
|||
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
*(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value;
|
||||
};
|
||||
socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<blender::fn::Field<int>>();
|
||||
socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<int>>();
|
||||
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
int value;
|
||||
socket.typeinfo->get_base_cpp_value(socket, &value);
|
||||
new (r_value) blender::fn::Field<int>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<int>(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
@ -749,12 +750,11 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
|
|||
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
*(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value;
|
||||
};
|
||||
socktype->geometry_nodes_cpp_type =
|
||||
&blender::fn::CPPType::get<blender::fn::Field<blender::float3>>();
|
||||
socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<blender::float3>>();
|
||||
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
blender::float3 value;
|
||||
socket.typeinfo->get_base_cpp_value(socket, &value);
|
||||
new (r_value) blender::fn::Field<blender::float3>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<blender::float3>(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
@ -767,12 +767,11 @@ static bNodeSocketType *make_socket_type_rgba()
|
|||
*(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
|
||||
};
|
||||
socktype->geometry_nodes_cpp_type =
|
||||
&blender::fn::CPPType::get<blender::fn::Field<blender::ColorGeometry4f>>();
|
||||
&blender::fn::CPPType::get<ValueOrField<blender::ColorGeometry4f>>();
|
||||
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
blender::ColorGeometry4f value;
|
||||
socket.typeinfo->get_base_cpp_value(socket, &value);
|
||||
new (r_value)
|
||||
blender::fn::Field<blender::ColorGeometry4f>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<blender::ColorGeometry4f>(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
@ -784,13 +783,12 @@ static bNodeSocketType *make_socket_type_string()
|
|||
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value);
|
||||
};
|
||||
socktype->geometry_nodes_cpp_type =
|
||||
&blender::fn::CPPType::get<blender::fn::Field<std::string>>();
|
||||
socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<std::string>>();
|
||||
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
std::string value;
|
||||
value.~basic_string();
|
||||
socket.typeinfo->get_base_cpp_value(socket, &value);
|
||||
new (r_value) blender::fn::Field<std::string>(blender::fn::make_constant_field(value));
|
||||
new (r_value) ValueOrField<std::string>(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue