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-04-27 14:03:40 +0300
committerJacques Lucke <jacques@blender.org>2021-04-27 14:03:40 +0300
commit908bb0363062168e16c608e13b9340724510e2cd (patch)
treec1215044d03af2f9cbdac32325582810666e5ebc /source/blender/modifiers
parenta022cffb72f6f8b791c8f2ac9d7b689caa3e8839 (diff)
Geometry Nodes: improve geometry nodes evaluator internal api
This is a first step towards T87620. It should not have any functional changes. Goals of this refactor: * Move the evaluator out of `MOD_nodes.cc`. That makes it easier to improve it in isolation. * Extract core input/out parameter management out of `GeoNodeExecParams`. Managing this is the responsibility of the evaluator. This separation of concerns will be useful once we have lazy evaluation of certain inputs/outputs. Differential Revision: https://developer.blender.org/D11085
Diffstat (limited to 'source/blender/modifiers')
-rw-r--r--source/blender/modifiers/CMakeLists.txt2
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc395
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc452
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.hh56
4 files changed, 524 insertions, 381 deletions
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 54caaed9231..6ac2629c006 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
intern/MOD_mirror.c
intern/MOD_multires.c
intern/MOD_nodes.cc
+ intern/MOD_nodes_evaluator.cc
intern/MOD_none.c
intern/MOD_normal_edit.c
intern/MOD_ocean.c
@@ -118,6 +119,7 @@ set(SRC
MOD_modifiertypes.h
MOD_nodes.h
intern/MOD_meshcache_util.h
+ intern/MOD_nodes_evaluator.hh
intern/MOD_solidify_util.h
intern/MOD_ui_common.h
intern/MOD_util.h
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index cc8f4a544c0..bc045b39904 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -75,16 +75,14 @@
#include "MOD_modifiertypes.h"
#include "MOD_nodes.h"
+#include "MOD_nodes_evaluator.hh"
#include "MOD_ui_common.h"
#include "ED_spreadsheet.h"
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
-#include "NOD_geometry_exec.hh"
#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_callbacks.hh"
-#include "NOD_type_conversions.hh"
using blender::float3;
using blender::FunctionRef;
@@ -100,7 +98,6 @@ using blender::bke::PersistentDataHandleMap;
using blender::bke::PersistentObjectHandle;
using blender::fn::GMutablePointer;
using blender::fn::GPointer;
-using blender::fn::GValueMap;
using blender::nodes::GeoNodeExecParams;
using namespace blender::fn::multi_function_types;
using namespace blender::nodes::derived_node_tree_types;
@@ -268,368 +265,6 @@ static bool logging_enabled(const ModifierEvalContext *ctx)
return true;
}
-class GeometryNodesEvaluator {
- public:
- using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>;
-
- private:
- blender::LinearAllocator<> allocator_;
- Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_;
- Vector<DInputSocket> group_outputs_;
- blender::nodes::MultiFunctionByNode &mf_by_node_;
- const blender::nodes::DataTypeConversions &conversions_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
- LogSocketValueFn log_socket_value_fn_;
-
- public:
- GeometryNodesEvaluator(const Map<DOutputSocket, GMutablePointer> &group_input_data,
- Vector<DInputSocket> group_outputs,
- blender::nodes::MultiFunctionByNode &mf_by_node,
- const PersistentDataHandleMap &handle_map,
- const Object *self_object,
- const ModifierData *modifier,
- Depsgraph *depsgraph,
- LogSocketValueFn log_socket_value_fn)
- : group_outputs_(std::move(group_outputs)),
- mf_by_node_(mf_by_node),
- conversions_(blender::nodes::get_implicit_type_conversions()),
- handle_map_(handle_map),
- self_object_(self_object),
- modifier_(modifier),
- depsgraph_(depsgraph),
- log_socket_value_fn_(std::move(log_socket_value_fn))
- {
- for (auto item : group_input_data.items()) {
- this->log_socket_value(item.key, item.value);
- this->forward_to_inputs(item.key, item.value);
- }
- }
-
- Vector<GMutablePointer> execute()
- {
- Vector<GMutablePointer> results;
- for (const DInputSocket &group_output : group_outputs_) {
- Vector<GMutablePointer> result = this->get_input_values(group_output);
- this->log_socket_value(group_output, result);
- results.append(result[0]);
- }
- for (GMutablePointer value : value_by_input_.values()) {
- value.destruct();
- }
- return results;
- }
-
- private:
- Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute)
- {
- Vector<DSocket> from_sockets;
- socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); });
-
- if (from_sockets.is_empty()) {
- /* The input is not connected, use the value from the socket itself. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- return {get_unlinked_input_value(socket_to_compute, type)};
- }
-
- /* Multi-input sockets contain a vector of inputs. */
- if (socket_to_compute->is_multi_input_socket()) {
- return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets);
- }
-
- const DSocket from_socket = from_sockets[0];
- GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket);
- return {value};
- }
-
- Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute,
- const Span<DSocket> from_sockets)
- {
- Vector<GMutablePointer> values;
- for (const int i : from_sockets.index_range()) {
- const DSocket from_socket = from_sockets[i];
- const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket);
- if (first_occurence == -1) {
- values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket));
- }
- else {
- /* If the same from-socket occurs more than once, we make a copy of the first value. This
- * can happen when a node linked to a multi-input-socket is muted. */
- GMutablePointer value = values[first_occurence];
- const CPPType *type = value.type();
- void *copy_buffer = allocator_.allocate(type->size(), type->alignment());
- type->copy_to_uninitialized(value.get(), copy_buffer);
- values.append({type, copy_buffer});
- }
- }
- return values;
- }
-
- GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute,
- const DSocket from_socket)
- {
- if (from_socket->is_output()) {
- const DOutputSocket from_output_socket{from_socket};
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute,
- from_output_socket);
- std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
- if (value.has_value()) {
- /* This input has been computed before, return it directly. */
- return {*value};
- }
-
- /* Compute the socket now. */
- this->compute_output_and_forward(from_output_socket);
- return {value_by_input_.pop(key)};
- }
-
- /* Get value from an unlinked input socket. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- const DInputSocket from_input_socket{from_socket};
- return {get_unlinked_input_value(from_input_socket, type)};
- }
-
- void compute_output_and_forward(const DOutputSocket socket_to_compute)
- {
- const DNode node{socket_to_compute.context(), &socket_to_compute->node()};
-
- if (!socket_to_compute->is_available()) {
- /* If the output is not available, use a default value. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(type.default_value(), buffer);
- this->forward_to_inputs(socket_to_compute, {type, buffer});
- return;
- }
-
- /* Prepare inputs required to execute the node. */
- GValueMap<StringRef> node_inputs_map{allocator_};
- for (const InputSocketRef *input_socket : node->inputs()) {
- if (input_socket->is_available()) {
- DInputSocket dsocket{node.context(), input_socket};
- Vector<GMutablePointer> values = this->get_input_values(dsocket);
- this->log_socket_value(dsocket, values);
- for (int i = 0; i < values.size(); ++i) {
- /* Values from Multi Input Sockets are stored in input map with the format
- * <identifier>[<index>]. */
- blender::StringRefNull key = allocator_.copy_string(
- input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : ""));
- node_inputs_map.add_new_direct(key, std::move(values[i]));
- }
- }
- }
-
- /* Execute the node. */
- GValueMap<StringRef> node_outputs_map{allocator_};
- GeoNodeExecParams params{
- node, node_inputs_map, node_outputs_map, handle_map_, self_object_, modifier_, depsgraph_};
- this->execute_node(node, params);
-
- /* Forward computed outputs to linked input sockets. */
- for (const OutputSocketRef *output_socket : node->outputs()) {
- if (output_socket->is_available()) {
- const DOutputSocket dsocket{node.context(), output_socket};
- GMutablePointer value = node_outputs_map.extract(output_socket->identifier());
- this->log_socket_value(dsocket, value);
- this->forward_to_inputs(dsocket, value);
- }
- }
- }
-
- void log_socket_value(const DSocket socket, Span<GPointer> values)
- {
- if (log_socket_value_fn_) {
- log_socket_value_fn_(socket, values);
- }
- }
-
- void log_socket_value(const DSocket socket, Span<GMutablePointer> values)
- {
- this->log_socket_value(socket, values.cast<GPointer>());
- }
-
- void log_socket_value(const DSocket socket, GPointer value)
- {
- this->log_socket_value(socket, Span<GPointer>(&value, 1));
- }
-
- void execute_node(const DNode node, GeoNodeExecParams params)
- {
- const bNode &bnode = params.node();
-
- /* Use the geometry-node-execute callback if it exists. */
- if (bnode.typeinfo->geometry_node_execute != nullptr) {
- bnode.typeinfo->geometry_node_execute(params);
- return;
- }
-
- /* Use the multi-function implementation if it exists. */
- const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr);
- if (multi_function != nullptr) {
- this->execute_multi_function_node(node, params, *multi_function);
- return;
- }
-
- /* Just output default values if no implementation exists. */
- this->execute_unknown_node(node, params);
- }
-
- void execute_multi_function_node(const DNode node,
- GeoNodeExecParams params,
- const MultiFunction &fn)
- {
- MFContextBuilder fn_context;
- MFParamsBuilder fn_params{fn, 1};
- Vector<GMutablePointer> input_data;
- for (const InputSocketRef *socket_ref : node->inputs()) {
- if (socket_ref->is_available()) {
- GMutablePointer data = params.extract_input(socket_ref->identifier());
- fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1));
- input_data.append(data);
- }
- }
- Vector<GMutablePointer> output_data;
- for (const OutputSocketRef *socket_ref : node->outputs()) {
- if (socket_ref->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1));
- output_data.append(GMutablePointer(type, buffer));
- }
- }
- fn.call(IndexRange(1), fn_params, fn_context);
- for (GMutablePointer value : input_data) {
- value.destruct();
- }
- int output_index = 0;
- for (const int i : node->outputs().index_range()) {
- if (node->output(i).is_available()) {
- GMutablePointer value = output_data[output_index];
- params.set_output_by_move(node->output(i).identifier(), value);
- value.destruct();
- output_index++;
- }
- }
- }
-
- void execute_unknown_node(const DNode node, GeoNodeExecParams params)
- {
- for (const OutputSocketRef *socket : node->outputs()) {
- if (socket->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- params.set_output_by_copy(socket->identifier(), {type, type.default_value()});
- }
- }
- }
-
- void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward)
- {
- /* For all sockets that are linked with the from_socket push the value to their node. */
- Vector<DInputSocket> to_sockets_all;
-
- auto handle_target_socket_fn = [&](DInputSocket to_socket) {
- to_sockets_all.append_non_duplicates(to_socket);
- };
- auto handle_skipped_socket_fn = [&, this](DSocket socket) {
- this->log_socket_value(socket, value_to_forward);
- };
-
- from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn);
-
- const CPPType &from_type = *value_to_forward.type();
- Vector<DInputSocket> to_sockets_same_type;
- for (const DInputSocket &to_socket : to_sockets_all) {
- const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- if (from_type == to_type) {
- to_sockets_same_type.append(to_socket);
- }
- else {
- void *buffer = allocator_.allocate(to_type.size(), to_type.alignment());
- if (conversions_.is_convertible(from_type, to_type)) {
- conversions_.convert_to_uninitialized(
- from_type, to_type, value_to_forward.get(), buffer);
- }
- else {
- to_type.copy_to_uninitialized(to_type.default_value(), buffer);
- }
- add_value_to_input_socket(key, GMutablePointer{to_type, buffer});
- }
- }
-
- if (to_sockets_same_type.size() == 0) {
- /* This value is not further used, so destruct it. */
- value_to_forward.destruct();
- }
- else if (to_sockets_same_type.size() == 1) {
- /* This value is only used on one input socket, no need to copy it. */
- const DInputSocket to_socket = to_sockets_same_type[0];
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
-
- add_value_to_input_socket(key, value_to_forward);
- }
- else {
- /* Multiple inputs use the value, make a copy for every input except for one. */
- const DInputSocket first_to_socket = to_sockets_same_type[0];
- Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
- const CPPType &type = *value_to_forward.type();
- const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket,
- from_socket);
- add_value_to_input_socket(first_key, value_to_forward);
- for (const DInputSocket &to_socket : other_to_sockets) {
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(value_to_forward.get(), buffer);
- add_value_to_input_socket(key, GMutablePointer{type, buffer});
- }
- }
- }
-
- void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key,
- GMutablePointer value)
- {
- value_by_input_.add_new(key, value);
- }
-
- GMutablePointer get_unlinked_input_value(const DInputSocket &socket,
- const CPPType &required_type)
- {
- bNodeSocket *bsocket = socket->bsocket();
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
-
- if (bsocket->type == SOCK_OBJECT) {
- Object *object = socket->default_value<bNodeSocketValueObject>()->value;
- PersistentObjectHandle object_handle = handle_map_.lookup(object);
- new (buffer) PersistentObjectHandle(object_handle);
- }
- else if (bsocket->type == SOCK_COLLECTION) {
- Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value;
- PersistentCollectionHandle collection_handle = handle_map_.lookup(collection);
- new (buffer) PersistentCollectionHandle(collection_handle);
- }
- else {
- blender::nodes::socket_cpp_value_get(*bsocket, buffer);
- }
-
- if (type == required_type) {
- return {type, buffer};
- }
- if (conversions_.is_convertible(type, required_type)) {
- void *converted_buffer = allocator_.allocate(required_type.size(),
- required_type.alignment());
- conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer);
- type.destruct(buffer);
- return {required_type, converted_buffer};
- }
- void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment());
- required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
- return {required_type, default_buffer};
- }
-};
-
/**
* This code is responsible for creating the new property and also creating the group of
* properties in the prop_ui_container group for the UI info, the mapping for which is
@@ -1297,21 +932,19 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
log_ui_hints(socket, values, ctx->object, nmd);
};
- GeometryNodesEvaluator evaluator{group_inputs,
- group_outputs,
- mf_by_node,
- handle_map,
- ctx->object,
- (ModifierData *)nmd,
- ctx->depsgraph,
- log_socket_value};
-
- Vector<GMutablePointer> results = evaluator.execute();
- BLI_assert(results.size() == 1);
- GMutablePointer result = results[0];
-
- GeometrySet output_geometry = std::move(*(GeometrySet *)result.get());
- return output_geometry;
+ blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params;
+ eval_params.input_values = group_inputs;
+ eval_params.output_sockets = group_outputs;
+ eval_params.mf_by_node = &mf_by_node;
+ eval_params.handle_map = &handle_map;
+ eval_params.modifier_ = nmd;
+ eval_params.depsgraph = ctx->depsgraph;
+ eval_params.log_socket_value_fn = log_socket_value;
+ blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params);
+
+ BLI_assert(eval_params.r_output_values.size() == 1);
+ GMutablePointer result = eval_params.r_output_values[0];
+ return result.relocate_out<GeometrySet>();
}
/**
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
new file mode 100644
index 00000000000..aa35f5c540f
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -0,0 +1,452 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "MOD_nodes_evaluator.hh"
+
+#include "NOD_geometry_exec.hh"
+#include "NOD_type_conversions.hh"
+
+#include "DEG_depsgraph_query.h"
+
+#include "FN_generic_value_map.hh"
+#include "FN_multi_function.hh"
+
+namespace blender::modifiers::geometry_nodes {
+
+using bke::PersistentCollectionHandle;
+using bke::PersistentObjectHandle;
+using fn::CPPType;
+using fn::GValueMap;
+using nodes::GeoNodeExecParams;
+using namespace fn::multi_function_types;
+
+class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider {
+ public:
+ LinearAllocator<> *allocator;
+ GValueMap<StringRef> *input_values;
+ GValueMap<StringRef> *output_values;
+
+ bool can_get_input(StringRef identifier) const override
+ {
+ return input_values->contains(identifier);
+ }
+
+ bool can_set_output(StringRef identifier) const override
+ {
+ return !output_values->contains(identifier);
+ }
+
+ GMutablePointer extract_input(StringRef identifier) override
+ {
+ return this->input_values->extract(identifier);
+ }
+
+ Vector<GMutablePointer> extract_multi_input(StringRef identifier) override
+ {
+ Vector<GMutablePointer> values;
+ int index = 0;
+ while (true) {
+ std::string sub_identifier = identifier;
+ if (index > 0) {
+ sub_identifier += "[" + std::to_string(index) + "]";
+ }
+ if (!this->input_values->contains(sub_identifier)) {
+ break;
+ }
+ values.append(input_values->extract(sub_identifier));
+ index++;
+ }
+ return values;
+ }
+
+ GPointer get_input(StringRef identifier) const override
+ {
+ return this->input_values->lookup(identifier);
+ }
+
+ GMutablePointer alloc_output_value(StringRef identifier, const CPPType &type) override
+ {
+ void *buffer = this->allocator->allocate(type.size(), type.alignment());
+ GMutablePointer ptr{&type, buffer};
+ this->output_values->add_new_direct(identifier, ptr);
+ return ptr;
+ }
+};
+
+class GeometryNodesEvaluator {
+ public:
+ using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>;
+
+ private:
+ blender::LinearAllocator<> &allocator_;
+ Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_;
+ Vector<DInputSocket> group_outputs_;
+ blender::nodes::MultiFunctionByNode &mf_by_node_;
+ const blender::nodes::DataTypeConversions &conversions_;
+ const PersistentDataHandleMap &handle_map_;
+ const Object *self_object_;
+ const ModifierData *modifier_;
+ Depsgraph *depsgraph_;
+ LogSocketValueFn log_socket_value_fn_;
+
+ public:
+ GeometryNodesEvaluator(GeometryNodesEvaluationParams &params)
+ : allocator_(params.allocator),
+ group_outputs_(std::move(params.output_sockets)),
+ mf_by_node_(*params.mf_by_node),
+ conversions_(blender::nodes::get_implicit_type_conversions()),
+ handle_map_(*params.handle_map),
+ self_object_(params.self_object),
+ modifier_(&params.modifier_->modifier),
+ depsgraph_(params.depsgraph),
+ log_socket_value_fn_(std::move(params.log_socket_value_fn))
+ {
+ for (auto item : params.input_values.items()) {
+ this->log_socket_value(item.key, item.value);
+ this->forward_to_inputs(item.key, item.value);
+ }
+ }
+
+ Vector<GMutablePointer> execute()
+ {
+ Vector<GMutablePointer> results;
+ for (const DInputSocket &group_output : group_outputs_) {
+ Vector<GMutablePointer> result = this->get_input_values(group_output);
+ this->log_socket_value(group_output, result);
+ results.append(result[0]);
+ }
+ for (GMutablePointer value : value_by_input_.values()) {
+ value.destruct();
+ }
+ return results;
+ }
+
+ private:
+ Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute)
+ {
+ Vector<DSocket> from_sockets;
+ socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); });
+
+ if (from_sockets.is_empty()) {
+ /* The input is not connected, use the value from the socket itself. */
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
+ return {get_unlinked_input_value(socket_to_compute, type)};
+ }
+
+ /* Multi-input sockets contain a vector of inputs. */
+ if (socket_to_compute->is_multi_input_socket()) {
+ return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets);
+ }
+
+ const DSocket from_socket = from_sockets[0];
+ GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket);
+ return {value};
+ }
+
+ Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute,
+ const Span<DSocket> from_sockets)
+ {
+ Vector<GMutablePointer> values;
+ for (const int i : from_sockets.index_range()) {
+ const DSocket from_socket = from_sockets[i];
+ const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket);
+ if (first_occurence == -1) {
+ values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket));
+ }
+ else {
+ /* If the same from-socket occurs more than once, we make a copy of the first value. This
+ * can happen when a node linked to a multi-input-socket is muted. */
+ GMutablePointer value = values[first_occurence];
+ const CPPType *type = value.type();
+ void *copy_buffer = allocator_.allocate(type->size(), type->alignment());
+ type->copy_to_uninitialized(value.get(), copy_buffer);
+ values.append({type, copy_buffer});
+ }
+ }
+ return values;
+ }
+
+ GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute,
+ const DSocket from_socket)
+ {
+ if (from_socket->is_output()) {
+ const DOutputSocket from_output_socket{from_socket};
+ const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute,
+ from_output_socket);
+ std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
+ if (value.has_value()) {
+ /* This input has been computed before, return it directly. */
+ return {*value};
+ }
+
+ /* Compute the socket now. */
+ this->compute_output_and_forward(from_output_socket);
+ return {value_by_input_.pop(key)};
+ }
+
+ /* Get value from an unlinked input socket. */
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
+ const DInputSocket from_input_socket{from_socket};
+ return {get_unlinked_input_value(from_input_socket, type)};
+ }
+
+ void compute_output_and_forward(const DOutputSocket socket_to_compute)
+ {
+ const DNode node{socket_to_compute.context(), &socket_to_compute->node()};
+
+ if (!socket_to_compute->is_available()) {
+ /* If the output is not available, use a default value. */
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(type.default_value(), buffer);
+ this->forward_to_inputs(socket_to_compute, {type, buffer});
+ return;
+ }
+
+ /* Prepare inputs required to execute the node. */
+ GValueMap<StringRef> node_inputs_map{allocator_};
+ for (const InputSocketRef *input_socket : node->inputs()) {
+ if (input_socket->is_available()) {
+ DInputSocket dsocket{node.context(), input_socket};
+ Vector<GMutablePointer> values = this->get_input_values(dsocket);
+ this->log_socket_value(dsocket, values);
+ for (int i = 0; i < values.size(); ++i) {
+ /* Values from Multi Input Sockets are stored in input map with the format
+ * <identifier>[<index>]. */
+ blender::StringRefNull key = allocator_.copy_string(
+ input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : ""));
+ node_inputs_map.add_new_direct(key, std::move(values[i]));
+ }
+ }
+ }
+
+ /* Execute the node. */
+ GValueMap<StringRef> node_outputs_map{allocator_};
+ NodeParamsProvider params_provider;
+ params_provider.dnode = node;
+ params_provider.handle_map = &handle_map_;
+ params_provider.self_object = self_object_;
+ params_provider.depsgraph = depsgraph_;
+ params_provider.allocator = &allocator_;
+ params_provider.input_values = &node_inputs_map;
+ params_provider.output_values = &node_outputs_map;
+ params_provider.modifier = modifier_;
+ this->execute_node(node, params_provider);
+
+ /* Forward computed outputs to linked input sockets. */
+ for (const OutputSocketRef *output_socket : node->outputs()) {
+ if (output_socket->is_available()) {
+ const DOutputSocket dsocket{node.context(), output_socket};
+ GMutablePointer value = node_outputs_map.extract(output_socket->identifier());
+ this->log_socket_value(dsocket, value);
+ this->forward_to_inputs(dsocket, value);
+ }
+ }
+ }
+
+ void log_socket_value(const DSocket socket, Span<GPointer> values)
+ {
+ if (log_socket_value_fn_) {
+ log_socket_value_fn_(socket, values);
+ }
+ }
+
+ void log_socket_value(const DSocket socket, Span<GMutablePointer> values)
+ {
+ this->log_socket_value(socket, values.cast<GPointer>());
+ }
+
+ void log_socket_value(const DSocket socket, GPointer value)
+ {
+ this->log_socket_value(socket, Span<GPointer>(&value, 1));
+ }
+
+ void execute_node(const DNode node, NodeParamsProvider &params_provider)
+ {
+ const bNode &bnode = *params_provider.dnode->bnode();
+
+ /* Use the geometry-node-execute callback if it exists. */
+ if (bnode.typeinfo->geometry_node_execute != nullptr) {
+ GeoNodeExecParams params{params_provider};
+ bnode.typeinfo->geometry_node_execute(params);
+ return;
+ }
+
+ /* Use the multi-function implementation if it exists. */
+ const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr);
+ if (multi_function != nullptr) {
+ this->execute_multi_function_node(node, params_provider, *multi_function);
+ return;
+ }
+
+ /* Just output default values if no implementation exists. */
+ this->execute_unknown_node(node, params_provider);
+ }
+
+ void execute_multi_function_node(const DNode node,
+ NodeParamsProvider &params_provider,
+ const MultiFunction &fn)
+ {
+ MFContextBuilder fn_context;
+ MFParamsBuilder fn_params{fn, 1};
+ Vector<GMutablePointer> input_data;
+ for (const InputSocketRef *socket_ref : node->inputs()) {
+ if (socket_ref->is_available()) {
+ GMutablePointer data = params_provider.extract_input(socket_ref->identifier());
+ fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1));
+ input_data.append(data);
+ }
+ }
+ Vector<GMutablePointer> output_data;
+ for (const OutputSocketRef *socket_ref : node->outputs()) {
+ if (socket_ref->is_available()) {
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo());
+ GMutablePointer output_value = params_provider.alloc_output_value(socket_ref->identifier(),
+ type);
+ fn_params.add_uninitialized_single_output(GMutableSpan{type, output_value.get(), 1});
+ output_data.append(output_value);
+ }
+ }
+ fn.call(IndexRange(1), fn_params, fn_context);
+ for (GMutablePointer value : input_data) {
+ value.destruct();
+ }
+ }
+
+ void execute_unknown_node(const DNode node, NodeParamsProvider &params_provider)
+ {
+ for (const OutputSocketRef *socket : node->outputs()) {
+ if (socket->is_available()) {
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
+ params_provider.output_values->add_new_by_copy(socket->identifier(),
+ {type, type.default_value()});
+ }
+ }
+ }
+
+ void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward)
+ {
+ /* For all sockets that are linked with the from_socket push the value to their node. */
+ Vector<DInputSocket> to_sockets_all;
+
+ auto handle_target_socket_fn = [&](DInputSocket to_socket) {
+ to_sockets_all.append_non_duplicates(to_socket);
+ };
+ auto handle_skipped_socket_fn = [&, this](DSocket socket) {
+ this->log_socket_value(socket, value_to_forward);
+ };
+
+ from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn);
+
+ const CPPType &from_type = *value_to_forward.type();
+ Vector<DInputSocket> to_sockets_same_type;
+ for (const DInputSocket &to_socket : to_sockets_all) {
+ const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
+ const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
+ if (from_type == to_type) {
+ to_sockets_same_type.append(to_socket);
+ }
+ else {
+ void *buffer = allocator_.allocate(to_type.size(), to_type.alignment());
+ if (conversions_.is_convertible(from_type, to_type)) {
+ conversions_.convert_to_uninitialized(
+ from_type, to_type, value_to_forward.get(), buffer);
+ }
+ else {
+ to_type.copy_to_uninitialized(to_type.default_value(), buffer);
+ }
+ add_value_to_input_socket(key, GMutablePointer{to_type, buffer});
+ }
+ }
+
+ if (to_sockets_same_type.size() == 0) {
+ /* This value is not further used, so destruct it. */
+ value_to_forward.destruct();
+ }
+ else if (to_sockets_same_type.size() == 1) {
+ /* This value is only used on one input socket, no need to copy it. */
+ const DInputSocket to_socket = to_sockets_same_type[0];
+ const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
+
+ add_value_to_input_socket(key, value_to_forward);
+ }
+ else {
+ /* Multiple inputs use the value, make a copy for every input except for one. */
+ const DInputSocket first_to_socket = to_sockets_same_type[0];
+ Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
+ const CPPType &type = *value_to_forward.type();
+ const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket,
+ from_socket);
+ add_value_to_input_socket(first_key, value_to_forward);
+ for (const DInputSocket &to_socket : other_to_sockets) {
+ const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(value_to_forward.get(), buffer);
+ add_value_to_input_socket(key, GMutablePointer{type, buffer});
+ }
+ }
+ }
+
+ void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key,
+ GMutablePointer value)
+ {
+ value_by_input_.add_new(key, value);
+ }
+
+ GMutablePointer get_unlinked_input_value(const DInputSocket &socket,
+ const CPPType &required_type)
+ {
+ bNodeSocket *bsocket = socket->bsocket();
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+
+ if (bsocket->type == SOCK_OBJECT) {
+ Object *object = socket->default_value<bNodeSocketValueObject>()->value;
+ PersistentObjectHandle object_handle = handle_map_.lookup(object);
+ new (buffer) PersistentObjectHandle(object_handle);
+ }
+ else if (bsocket->type == SOCK_COLLECTION) {
+ Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value;
+ PersistentCollectionHandle collection_handle = handle_map_.lookup(collection);
+ new (buffer) PersistentCollectionHandle(collection_handle);
+ }
+ else {
+ blender::nodes::socket_cpp_value_get(*bsocket, buffer);
+ }
+
+ if (type == required_type) {
+ return {type, buffer};
+ }
+ if (conversions_.is_convertible(type, required_type)) {
+ void *converted_buffer = allocator_.allocate(required_type.size(),
+ required_type.alignment());
+ conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer);
+ type.destruct(buffer);
+ return {required_type, converted_buffer};
+ }
+ void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment());
+ required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
+ return {required_type, default_buffer};
+ }
+};
+
+void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params)
+{
+ GeometryNodesEvaluator evaluator{params};
+ params.r_output_values = evaluator.execute();
+}
+
+} // namespace blender::modifiers::geometry_nodes
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
new file mode 100644
index 00000000000..3d9a03350f7
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
@@ -0,0 +1,56 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_tree_multi_function.hh"
+
+#include "FN_generic_pointer.hh"
+
+#include "BKE_persistent_data_handle.hh"
+
+#include "DNA_modifier_types.h"
+
+namespace blender::modifiers::geometry_nodes {
+
+using namespace nodes::derived_node_tree_types;
+using bke::PersistentDataHandleMap;
+using fn::GMutablePointer;
+using fn::GPointer;
+
+using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>;
+
+struct GeometryNodesEvaluationParams {
+ blender::LinearAllocator<> allocator;
+
+ Map<DOutputSocket, GMutablePointer> input_values;
+ Vector<DInputSocket> output_sockets;
+ nodes::MultiFunctionByNode *mf_by_node;
+ const PersistentDataHandleMap *handle_map;
+ const NodesModifierData *modifier_;
+ Depsgraph *depsgraph;
+ Object *self_object;
+ LogSocketValueFn log_socket_value_fn;
+
+ Vector<GMutablePointer> r_output_values;
+};
+
+void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params);
+
+} // namespace blender::modifiers::geometry_nodes