diff options
author | Jacques Lucke <jacques@blender.org> | 2021-11-23 16:47:25 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2021-11-23 16:49:26 +0300 |
commit | 47276b84701727c2f187c77f1ec502b4ca4963bd (patch) | |
tree | ac6e6fced7908dbaa984cfe686b2e3a080b99fc7 /source/blender/modifiers/intern | |
parent | 0bedd5d14f6d61a6a4a96cc7f88484b0dc46f752 (diff) |
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
Diffstat (limited to 'source/blender/modifiers/intern')
-rw-r--r-- | source/blender/modifiers/intern/MOD_nodes.cc | 29 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 143 |
2 files changed, 135 insertions, 37 deletions
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index d027be0a7f6..05e9bd7d233 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -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; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index fd2fe2161c9..4d081450fd8 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -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); } |