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-08-20 14:14:39 +0300
committerJacques Lucke <jacques@blender.org>2021-08-20 14:14:39 +0300
commit00812008129bdab48d328775b09332af12118867 (patch)
tree344723847c164ce75f9f4cff22991ce69678b57c /source/blender/functions
parent7d8c71e8003ecb4d3a7fe2483a328d3f2d184faa (diff)
Functions: remove multi-function network
The multi-function network system was able to compose multiple multi-functions into a new one and to evaluate that efficiently. This functionality was heavily used by the particle nodes prototype a year ago. However, since then we only used multi-functions without the need to compose them in geometry nodes. The upcoming "fields" in geometry nodes will need a way to compose multi-functions again. Unfortunately, the code removed in this commit was not ideal for this different kind of function composition. I've been working on an alternative that will be added separately when it becomes needed. I've had to update all the function nodes, because their interface depended on the multi-function network data structure a bit. The actual multi-function implementations are still the same though.
Diffstat (limited to 'source/blender/functions')
-rw-r--r--source/blender/functions/CMakeLists.txt7
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh2
-rw-r--r--source/blender/functions/FN_multi_function_network.hh536
-rw-r--r--source/blender/functions/FN_multi_function_network_evaluation.hh62
-rw-r--r--source/blender/functions/FN_multi_function_network_optimization.hh29
-rw-r--r--source/blender/functions/FN_multi_function_signature.hh15
-rw-r--r--source/blender/functions/intern/multi_function_network.cc330
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc1083
-rw-r--r--source/blender/functions/intern/multi_function_network_optimization.cc501
-rw-r--r--source/blender/functions/tests/FN_multi_function_network_test.cc280
-rw-r--r--source/blender/functions/tests/FN_multi_function_test.cc92
-rw-r--r--source/blender/functions/tests/FN_multi_function_test_common.hh174
12 files changed, 194 insertions, 2917 deletions
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt
index 809294ad274..f8d2acc74a8 100644
--- a/source/blender/functions/CMakeLists.txt
+++ b/source/blender/functions/CMakeLists.txt
@@ -33,9 +33,6 @@ set(SRC
intern/generic_virtual_vector_array.cc
intern/multi_function.cc
intern/multi_function_builder.cc
- intern/multi_function_network.cc
- intern/multi_function_network_evaluation.cc
- intern/multi_function_network_optimization.cc
FN_cpp_type.hh
FN_cpp_type_make.hh
@@ -49,9 +46,6 @@ set(SRC
FN_multi_function_builder.hh
FN_multi_function_context.hh
FN_multi_function_data_type.hh
- FN_multi_function_network.hh
- FN_multi_function_network_evaluation.hh
- FN_multi_function_network_optimization.hh
FN_multi_function_param_type.hh
FN_multi_function_params.hh
FN_multi_function_signature.hh
@@ -68,7 +62,6 @@ if(WITH_GTESTS)
tests/FN_cpp_type_test.cc
tests/FN_generic_span_test.cc
tests/FN_generic_vector_array_test.cc
- tests/FN_multi_function_network_test.cc
tests/FN_multi_function_test.cc
)
set(TEST_LIB
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index c9398ceb547..f429243e2de 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -129,7 +129,7 @@ class GVArray {
}
/* Same as `get_internal_single`, but `r_value` points to initialized memory. */
- void get_single_to_uninitialized(void *r_value) const
+ void get_internal_single_to_uninitialized(void *r_value) const
{
type_->default_construct(r_value);
this->get_internal_single(r_value);
diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh
deleted file mode 100644
index b303589106a..00000000000
--- a/source/blender/functions/FN_multi_function_network.hh
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * 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
-
-/** \file
- * \ingroup fn
- *
- * A multi-function network (`MFNetwork`) allows you to connect multiple multi-functions. The
- * `MFNetworkEvaluator` is a multi-function that wraps an entire network into a new multi-function
- * (which can be used in another network and so on).
- *
- * A MFNetwork is a graph data structure with two kinds of nodes:
- * - MFFunctionNode: Represents a multi-function. Its input and output sockets correspond to
- * parameters of the referenced multi-function.
- * - MFDummyNode: Does not reference a multi-function. Instead it just has sockets that can be
- * used to represent node group inputs and outputs.
- *
- * Links represent data flow. Unlinked input sockets have no value. In order to execute a function
- * node, all its inputs have to be connected to something.
- *
- * Links are only allowed between sockets with the exact same MFDataType. There are no implicit
- * conversions.
- *
- * Every input and output parameter of a multi-function corresponds to exactly one input or output
- * socket respectively. A multiple parameter belongs to exactly one input AND one output socket.
- *
- * There is an .to_dot() method that generates a graph in dot format for debugging purposes.
- */
-
-#include "FN_multi_function.hh"
-
-#include "BLI_vector_set.hh"
-
-namespace blender::fn {
-
-class MFNode;
-class MFFunctionNode;
-class MFDummyNode;
-class MFSocket;
-class MFInputSocket;
-class MFOutputSocket;
-class MFNetwork;
-
-class MFNode : NonCopyable, NonMovable {
- protected:
- MFNetwork *network_;
- Span<MFInputSocket *> inputs_;
- Span<MFOutputSocket *> outputs_;
- bool is_dummy_;
- int id_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- int id() const;
-
- MFNetwork &network();
- const MFNetwork &network() const;
-
- bool is_dummy() const;
- bool is_function() const;
-
- MFDummyNode &as_dummy();
- const MFDummyNode &as_dummy() const;
-
- MFFunctionNode &as_function();
- const MFFunctionNode &as_function() const;
-
- MFInputSocket &input(int index);
- const MFInputSocket &input(int index) const;
-
- MFOutputSocket &output(int index);
- const MFOutputSocket &output(int index) const;
-
- Span<MFInputSocket *> inputs();
- Span<const MFInputSocket *> inputs() const;
-
- Span<MFOutputSocket *> outputs();
- Span<const MFOutputSocket *> outputs() const;
-
- bool has_unlinked_inputs() const;
-
- private:
- void destruct_sockets();
-};
-
-class MFFunctionNode : public MFNode {
- private:
- const MultiFunction *function_;
- Span<int> input_param_indices_;
- Span<int> output_param_indices_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- const MultiFunction &function() const;
-
- const MFInputSocket &input_for_param(int param_index) const;
- const MFOutputSocket &output_for_param(int param_index) const;
-};
-
-class MFDummyNode : public MFNode {
- protected:
- StringRefNull name_;
- MutableSpan<StringRefNull> input_names_;
- MutableSpan<StringRefNull> output_names_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- Span<StringRefNull> input_names() const;
- Span<StringRefNull> output_names() const;
-};
-
-class MFSocket : NonCopyable, NonMovable {
- protected:
- MFNode *node_;
- bool is_output_;
- int index_;
- MFDataType data_type_;
- int id_;
- StringRefNull name_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- int id() const;
- int index() const;
-
- const MFDataType &data_type() const;
-
- MFNode &node();
- const MFNode &node() const;
-
- bool is_input() const;
- bool is_output() const;
-
- MFInputSocket &as_input();
- const MFInputSocket &as_input() const;
-
- MFOutputSocket &as_output();
- const MFOutputSocket &as_output() const;
-};
-
-class MFInputSocket : public MFSocket {
- private:
- MFOutputSocket *origin_;
-
- friend MFNetwork;
-
- public:
- MFOutputSocket *origin();
- const MFOutputSocket *origin() const;
-};
-
-class MFOutputSocket : public MFSocket {
- private:
- Vector<MFInputSocket *, 1> targets_;
-
- friend MFNetwork;
-
- public:
- Span<MFInputSocket *> targets();
- Span<const MFInputSocket *> targets() const;
-};
-
-class MFNetwork : NonCopyable, NonMovable {
- private:
- LinearAllocator<> allocator_;
-
- VectorSet<MFFunctionNode *> function_nodes_;
- VectorSet<MFDummyNode *> dummy_nodes_;
-
- Vector<MFNode *> node_or_null_by_id_;
- Vector<MFSocket *> socket_or_null_by_id_;
-
- public:
- MFNetwork() = default;
- ~MFNetwork();
-
- MFFunctionNode &add_function(const MultiFunction &function);
- MFDummyNode &add_dummy(StringRef name,
- Span<MFDataType> input_types,
- Span<MFDataType> output_types,
- Span<StringRef> input_names,
- Span<StringRef> output_names);
- void add_link(MFOutputSocket &from, MFInputSocket &to);
-
- MFOutputSocket &add_input(StringRef name, MFDataType data_type);
- MFInputSocket &add_output(StringRef name, MFDataType data_type);
-
- void relink(MFOutputSocket &old_output, MFOutputSocket &new_output);
-
- void remove(MFNode &node);
- void remove(Span<MFNode *> nodes);
-
- int socket_id_amount() const;
- int node_id_amount() const;
-
- Span<MFDummyNode *> dummy_nodes();
- Span<MFFunctionNode *> function_nodes();
-
- MFNode *node_or_null_by_id(int id);
- const MFNode *node_or_null_by_id(int id) const;
-
- MFSocket *socket_or_null_by_id(int id);
- const MFSocket *socket_or_null_by_id(int id) const;
-
- void find_dependencies(Span<const MFInputSocket *> sockets,
- VectorSet<const MFOutputSocket *> &r_dummy_sockets,
- VectorSet<const MFInputSocket *> &r_unlinked_inputs) const;
-
- bool have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const;
-
- std::string to_dot(Span<const MFNode *> marked_nodes = {}) const;
-};
-
-/* --------------------------------------------------------------------
- * MFNode inline methods.
- */
-
-inline StringRefNull MFNode::name() const
-{
- if (is_dummy_) {
- return this->as_dummy().name();
- }
- else {
- return this->as_function().name();
- }
-}
-
-inline int MFNode::id() const
-{
- return id_;
-}
-
-inline MFNetwork &MFNode::network()
-{
- return *network_;
-}
-
-inline const MFNetwork &MFNode::network() const
-{
- return *network_;
-}
-
-inline bool MFNode::is_dummy() const
-{
- return is_dummy_;
-}
-
-inline bool MFNode::is_function() const
-{
- return !is_dummy_;
-}
-
-inline MFDummyNode &MFNode::as_dummy()
-{
- BLI_assert(is_dummy_);
- return static_cast<MFDummyNode &>(*this);
-}
-
-inline const MFDummyNode &MFNode::as_dummy() const
-{
- BLI_assert(is_dummy_);
- return static_cast<const MFDummyNode &>(*this);
-}
-
-inline MFFunctionNode &MFNode::as_function()
-{
- BLI_assert(!is_dummy_);
- return static_cast<MFFunctionNode &>(*this);
-}
-
-inline const MFFunctionNode &MFNode::as_function() const
-{
- BLI_assert(!is_dummy_);
- return static_cast<const MFFunctionNode &>(*this);
-}
-
-inline MFInputSocket &MFNode::input(int index)
-{
- return *inputs_[index];
-}
-
-inline const MFInputSocket &MFNode::input(int index) const
-{
- return *inputs_[index];
-}
-
-inline MFOutputSocket &MFNode::output(int index)
-{
- return *outputs_[index];
-}
-
-inline const MFOutputSocket &MFNode::output(int index) const
-{
- return *outputs_[index];
-}
-
-inline Span<MFInputSocket *> MFNode::inputs()
-{
- return inputs_;
-}
-
-inline Span<const MFInputSocket *> MFNode::inputs() const
-{
- return inputs_;
-}
-
-inline Span<MFOutputSocket *> MFNode::outputs()
-{
- return outputs_;
-}
-
-inline Span<const MFOutputSocket *> MFNode::outputs() const
-{
- return outputs_;
-}
-
-inline bool MFNode::has_unlinked_inputs() const
-{
- for (const MFInputSocket *socket : inputs_) {
- if (socket->origin() == nullptr) {
- return true;
- }
- }
- return false;
-}
-
-/* --------------------------------------------------------------------
- * MFFunctionNode inline methods.
- */
-
-inline StringRefNull MFFunctionNode::name() const
-{
- return function_->name();
-}
-
-inline const MultiFunction &MFFunctionNode::function() const
-{
- return *function_;
-}
-
-inline const MFInputSocket &MFFunctionNode::input_for_param(int param_index) const
-{
- return this->input(input_param_indices_.first_index(param_index));
-}
-
-inline const MFOutputSocket &MFFunctionNode::output_for_param(int param_index) const
-{
- return this->output(output_param_indices_.first_index(param_index));
-}
-
-/* --------------------------------------------------------------------
- * MFDummyNode inline methods.
- */
-
-inline StringRefNull MFDummyNode::name() const
-{
- return name_;
-}
-
-inline Span<StringRefNull> MFDummyNode::input_names() const
-{
- return input_names_;
-}
-
-inline Span<StringRefNull> MFDummyNode::output_names() const
-{
- return output_names_;
-}
-
-/* --------------------------------------------------------------------
- * MFSocket inline methods.
- */
-
-inline StringRefNull MFSocket::name() const
-{
- return name_;
-}
-
-inline int MFSocket::id() const
-{
- return id_;
-}
-
-inline int MFSocket::index() const
-{
- return index_;
-}
-
-inline const MFDataType &MFSocket::data_type() const
-{
- return data_type_;
-}
-
-inline MFNode &MFSocket::node()
-{
- return *node_;
-}
-
-inline const MFNode &MFSocket::node() const
-{
- return *node_;
-}
-
-inline bool MFSocket::is_input() const
-{
- return !is_output_;
-}
-
-inline bool MFSocket::is_output() const
-{
- return is_output_;
-}
-
-inline MFInputSocket &MFSocket::as_input()
-{
- BLI_assert(this->is_input());
- return static_cast<MFInputSocket &>(*this);
-}
-
-inline const MFInputSocket &MFSocket::as_input() const
-{
- BLI_assert(this->is_input());
- return static_cast<const MFInputSocket &>(*this);
-}
-
-inline MFOutputSocket &MFSocket::as_output()
-{
- BLI_assert(this->is_output());
- return static_cast<MFOutputSocket &>(*this);
-}
-
-inline const MFOutputSocket &MFSocket::as_output() const
-{
- BLI_assert(this->is_output());
- return static_cast<const MFOutputSocket &>(*this);
-}
-
-/* --------------------------------------------------------------------
- * MFInputSocket inline methods.
- */
-
-inline MFOutputSocket *MFInputSocket::origin()
-{
- return origin_;
-}
-
-inline const MFOutputSocket *MFInputSocket::origin() const
-{
- return origin_;
-}
-
-/* --------------------------------------------------------------------
- * MFOutputSocket inline methods.
- */
-
-inline Span<MFInputSocket *> MFOutputSocket::targets()
-{
- return targets_;
-}
-
-inline Span<const MFInputSocket *> MFOutputSocket::targets() const
-{
- return targets_;
-}
-
-/* --------------------------------------------------------------------
- * MFNetwork inline methods.
- */
-
-inline Span<MFDummyNode *> MFNetwork::dummy_nodes()
-{
- return dummy_nodes_;
-}
-
-inline Span<MFFunctionNode *> MFNetwork::function_nodes()
-{
- return function_nodes_;
-}
-
-inline MFNode *MFNetwork::node_or_null_by_id(int id)
-{
- return node_or_null_by_id_[id];
-}
-
-inline const MFNode *MFNetwork::node_or_null_by_id(int id) const
-{
- return node_or_null_by_id_[id];
-}
-
-inline MFSocket *MFNetwork::socket_or_null_by_id(int id)
-{
- return socket_or_null_by_id_[id];
-}
-
-inline const MFSocket *MFNetwork::socket_or_null_by_id(int id) const
-{
- return socket_or_null_by_id_[id];
-}
-
-inline int MFNetwork::socket_id_amount() const
-{
- return socket_or_null_by_id_.size();
-}
-
-inline int MFNetwork::node_id_amount() const
-{
- return node_or_null_by_id_.size();
-}
-
-} // namespace blender::fn
diff --git a/source/blender/functions/FN_multi_function_network_evaluation.hh b/source/blender/functions/FN_multi_function_network_evaluation.hh
deleted file mode 100644
index 17cffa406f7..00000000000
--- a/source/blender/functions/FN_multi_function_network_evaluation.hh
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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
-
-/** \file
- * \ingroup fn
- */
-
-#include "FN_multi_function_network.hh"
-
-namespace blender::fn {
-
-class MFNetworkEvaluationStorage;
-
-class MFNetworkEvaluator : public MultiFunction {
- private:
- MFSignature signature_;
- Vector<const MFOutputSocket *> inputs_;
- Vector<const MFInputSocket *> outputs_;
-
- public:
- MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, Vector<const MFInputSocket *> outputs);
-
- void call(IndexMask mask, MFParams params, MFContext context) const override;
-
- private:
- using Storage = MFNetworkEvaluationStorage;
-
- void copy_inputs_to_storage(MFParams params, Storage &storage) const;
- void copy_outputs_to_storage(
- MFParams params,
- Storage &storage,
- Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const;
-
- void evaluate_network_to_compute_outputs(MFContext &global_context, Storage &storage) const;
-
- void evaluate_function(MFContext &global_context,
- const MFFunctionNode &function_node,
- Storage &storage) const;
-
- bool can_do_single_value_evaluation(const MFFunctionNode &function_node, Storage &storage) const;
-
- void initialize_remaining_outputs(MFParams params,
- Storage &storage,
- Span<const MFInputSocket *> remaining_outputs) const;
-};
-
-} // namespace blender::fn
diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/functions/FN_multi_function_network_optimization.hh
deleted file mode 100644
index 96664fa368e..00000000000
--- a/source/blender/functions/FN_multi_function_network_optimization.hh
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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 "FN_multi_function_network.hh"
-
-#include "BLI_resource_scope.hh"
-
-namespace blender::fn::mf_network_optimization {
-
-void dead_node_removal(MFNetwork &network);
-void constant_folding(MFNetwork &network, ResourceScope &scope);
-void common_subnetwork_elimination(MFNetwork &network);
-
-} // namespace blender::fn::mf_network_optimization
diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh
index 23309c9a5e6..d05948cc645 100644
--- a/source/blender/functions/FN_multi_function_signature.hh
+++ b/source/blender/functions/FN_multi_function_signature.hh
@@ -160,6 +160,21 @@ class MFSignatureBuilder {
}
}
+ void add(StringRef name, const MFParamType &param_type)
+ {
+ switch (param_type.interface_type()) {
+ case MFParamType::Input:
+ this->input(name, param_type.data_type());
+ break;
+ case MFParamType::Mutable:
+ this->mutable_(name, param_type.data_type());
+ break;
+ case MFParamType::Output:
+ this->output(name, param_type.data_type());
+ break;
+ }
+ }
+
/* Context */
/** This indicates that the function accesses the context. This disables optimizations that
diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc
deleted file mode 100644
index b5c2c09a35a..00000000000
--- a/source/blender/functions/intern/multi_function_network.cc
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * 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 "BLI_dot_export.hh"
-#include "BLI_stack.hh"
-
-#include "FN_multi_function_network.hh"
-
-namespace blender::fn {
-
-MFNetwork::~MFNetwork()
-{
- for (MFFunctionNode *node : function_nodes_) {
- node->destruct_sockets();
- node->~MFFunctionNode();
- }
- for (MFDummyNode *node : dummy_nodes_) {
- node->destruct_sockets();
- node->~MFDummyNode();
- }
-}
-
-void MFNode::destruct_sockets()
-{
- for (MFInputSocket *socket : inputs_) {
- socket->~MFInputSocket();
- }
- for (MFOutputSocket *socket : outputs_) {
- socket->~MFOutputSocket();
- }
-}
-
-/**
- * Add a new function node to the network. The caller keeps the ownership of the function. The
- * function should not be freed before the network. A reference to the new node is returned. The
- * node is owned by the network.
- */
-MFFunctionNode &MFNetwork::add_function(const MultiFunction &function)
-{
- Vector<int, 16> input_param_indices, output_param_indices;
-
- for (int param_index : function.param_indices()) {
- switch (function.param_type(param_index).interface_type()) {
- case MFParamType::Input: {
- input_param_indices.append(param_index);
- break;
- }
- case MFParamType::Output: {
- output_param_indices.append(param_index);
- break;
- }
- case MFParamType::Mutable: {
- input_param_indices.append(param_index);
- output_param_indices.append(param_index);
- break;
- }
- }
- }
-
- MFFunctionNode &node = *allocator_.construct<MFFunctionNode>().release();
- function_nodes_.add_new(&node);
-
- node.network_ = this;
- node.is_dummy_ = false;
- node.id_ = node_or_null_by_id_.append_and_get_index(&node);
- node.function_ = &function;
- node.input_param_indices_ = allocator_.construct_array_copy<int>(input_param_indices);
- node.output_param_indices_ = allocator_.construct_array_copy<int>(output_param_indices);
-
- node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>(
- input_param_indices.size());
- node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>(
- output_param_indices.size());
-
- for (int i : input_param_indices.index_range()) {
- int param_index = input_param_indices[i];
- MFParamType param = function.param_type(param_index);
- BLI_assert(param.is_input_or_mutable());
-
- MFInputSocket &socket = *node.inputs_[i];
- socket.data_type_ = param.data_type();
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = false;
- socket.name_ = function.param_name(param_index);
- socket.origin_ = nullptr;
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- }
-
- for (int i : output_param_indices.index_range()) {
- int param_index = output_param_indices[i];
- MFParamType param = function.param_type(param_index);
- BLI_assert(param.is_output_or_mutable());
-
- MFOutputSocket &socket = *node.outputs_[i];
- socket.data_type_ = param.data_type();
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = true;
- socket.name_ = function.param_name(param_index);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- }
-
- return node;
-}
-
-/**
- * Add a dummy node with the given input and output sockets.
- */
-MFDummyNode &MFNetwork::add_dummy(StringRef name,
- Span<MFDataType> input_types,
- Span<MFDataType> output_types,
- Span<StringRef> input_names,
- Span<StringRef> output_names)
-{
- assert_same_size(input_types, input_names);
- assert_same_size(output_types, output_names);
-
- MFDummyNode &node = *allocator_.construct<MFDummyNode>().release();
- dummy_nodes_.add_new(&node);
-
- node.network_ = this;
- node.is_dummy_ = true;
- node.name_ = allocator_.copy_string(name);
- node.id_ = node_or_null_by_id_.append_and_get_index(&node);
-
- node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>(
- input_types.size());
- node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>(
- output_types.size());
-
- node.input_names_ = allocator_.allocate_array<StringRefNull>(input_types.size());
- node.output_names_ = allocator_.allocate_array<StringRefNull>(output_types.size());
-
- for (int i : input_types.index_range()) {
- MFInputSocket &socket = *node.inputs_[i];
- socket.data_type_ = input_types[i];
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = false;
- socket.name_ = allocator_.copy_string(input_names[i]);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- node.input_names_[i] = socket.name_;
- }
-
- for (int i : output_types.index_range()) {
- MFOutputSocket &socket = *node.outputs_[i];
- socket.data_type_ = output_types[i];
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = true;
- socket.name_ = allocator_.copy_string(output_names[i]);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- node.output_names_[i] = socket.name_;
- }
-
- return node;
-}
-
-/**
- * Connect two sockets. This invokes undefined behavior if the sockets belong to different
- * networks, the sockets have a different data type, or the `to` socket is connected to something
- * else already.
- */
-void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to)
-{
- BLI_assert(to.origin_ == nullptr);
- BLI_assert(from.node_->network_ == to.node_->network_);
- BLI_assert(from.data_type_ == to.data_type_);
- from.targets_.append(&to);
- to.origin_ = &from;
-}
-
-MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type)
-{
- return this->add_dummy(name, {}, {data_type}, {}, {"Value"}).output(0);
-}
-
-MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type)
-{
- return this->add_dummy(name, {data_type}, {}, {"Value"}, {}).input(0);
-}
-
-void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output)
-{
- BLI_assert(&old_output != &new_output);
- BLI_assert(old_output.data_type_ == new_output.data_type_);
- for (MFInputSocket *input : old_output.targets()) {
- input->origin_ = &new_output;
- }
- new_output.targets_.extend(old_output.targets_);
- old_output.targets_.clear();
-}
-
-void MFNetwork::remove(MFNode &node)
-{
- for (MFInputSocket *socket : node.inputs_) {
- if (socket->origin_ != nullptr) {
- socket->origin_->targets_.remove_first_occurrence_and_reorder(socket);
- }
- socket_or_null_by_id_[socket->id_] = nullptr;
- }
- for (MFOutputSocket *socket : node.outputs_) {
- for (MFInputSocket *other : socket->targets_) {
- other->origin_ = nullptr;
- }
- socket_or_null_by_id_[socket->id_] = nullptr;
- }
- node.destruct_sockets();
- if (node.is_dummy()) {
- MFDummyNode &dummy_node = node.as_dummy();
- dummy_node.~MFDummyNode();
- dummy_nodes_.remove_contained(&dummy_node);
- }
- else {
- MFFunctionNode &function_node = node.as_function();
- function_node.~MFFunctionNode();
- function_nodes_.remove_contained(&function_node);
- }
- node_or_null_by_id_[node.id_] = nullptr;
-}
-
-void MFNetwork::remove(Span<MFNode *> nodes)
-{
- for (MFNode *node : nodes) {
- this->remove(*node);
- }
-}
-
-void MFNetwork::find_dependencies(Span<const MFInputSocket *> sockets,
- VectorSet<const MFOutputSocket *> &r_dummy_sockets,
- VectorSet<const MFInputSocket *> &r_unlinked_inputs) const
-{
- Set<const MFNode *> visited_nodes;
- Stack<const MFInputSocket *> sockets_to_check;
- sockets_to_check.push_multiple(sockets);
-
- while (!sockets_to_check.is_empty()) {
- const MFInputSocket &socket = *sockets_to_check.pop();
- const MFOutputSocket *origin_socket = socket.origin();
- if (origin_socket == nullptr) {
- r_unlinked_inputs.add(&socket);
- continue;
- }
-
- const MFNode &origin_node = origin_socket->node();
-
- if (origin_node.is_dummy()) {
- r_dummy_sockets.add(origin_socket);
- continue;
- }
-
- if (visited_nodes.add(&origin_node)) {
- sockets_to_check.push_multiple(origin_node.inputs());
- }
- }
-}
-
-bool MFNetwork::have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const
-{
- VectorSet<const MFOutputSocket *> dummy_sockets;
- VectorSet<const MFInputSocket *> unlinked_inputs;
- this->find_dependencies(sockets, dummy_sockets, unlinked_inputs);
- return dummy_sockets.size() + unlinked_inputs.size() > 0;
-}
-
-std::string MFNetwork::to_dot(Span<const MFNode *> marked_nodes) const
-{
- dot::DirectedGraph digraph;
- digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
-
- Map<const MFNode *, dot::NodeWithSocketsRef> dot_nodes;
-
- Vector<const MFNode *> all_nodes;
- all_nodes.extend(function_nodes_.as_span().cast<const MFNode *>());
- all_nodes.extend(dummy_nodes_.as_span().cast<const MFNode *>());
-
- for (const MFNode *node : all_nodes) {
- dot::Node &dot_node = digraph.new_node("");
-
- Vector<std::string> input_names, output_names;
- for (const MFInputSocket *socket : node->inputs_) {
- input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")");
- }
- for (const MFOutputSocket *socket : node->outputs_) {
- output_names.append(socket->name() + " (" + socket->data_type().to_string() + ")");
- }
-
- dot::NodeWithSocketsRef dot_node_ref{dot_node, node->name(), input_names, output_names};
- dot_nodes.add_new(node, dot_node_ref);
- }
-
- for (const MFDummyNode *node : dummy_nodes_) {
- dot_nodes.lookup(node).node().set_background_color("#77EE77");
- }
- for (const MFNode *node : marked_nodes) {
- dot_nodes.lookup(node).node().set_background_color("#7777EE");
- }
-
- for (const MFNode *to_node : all_nodes) {
- dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node);
-
- for (const MFInputSocket *to_socket : to_node->inputs_) {
- const MFOutputSocket *from_socket = to_socket->origin_;
- if (from_socket != nullptr) {
- const MFNode *from_node = from_socket->node_;
- dot::NodeWithSocketsRef from_dot_node = dot_nodes.lookup(from_node);
- digraph.new_edge(from_dot_node.output(from_socket->index_),
- to_dot_node.input(to_socket->index_));
- }
- }
- }
-
- return digraph.to_dot_string();
-}
-
-} // namespace blender::fn
diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc
deleted file mode 100644
index 9a0cb0c35ce..00000000000
--- a/source/blender/functions/intern/multi_function_network_evaluation.cc
+++ /dev/null
@@ -1,1083 +0,0 @@
-/*
- * 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.
- */
-
-/** \file
- * \ingroup fn
- *
- * The `MFNetworkEvaluator` class is a multi-function that consists of potentially many smaller
- * multi-functions. When called, it traverses the underlying MFNetwork and executes the required
- * function nodes.
- *
- * There are many possible approaches to evaluate a function network. The approach implemented
- * below has the following features:
- * - It does not use recursion. Those could become problematic with long node chains.
- * - It can handle all existing parameter types (including mutable parameters).
- * - Avoids data copies in many cases.
- * - Every node is executed at most once.
- * - Can compute sub-functions on a single element, when the result is the same for all elements.
- *
- * Possible improvements:
- * - Cache and reuse buffers.
- * - Use "deepest depth first" heuristic to decide which order the inputs of a node should be
- * computed. This reduces the number of required temporary buffers when they are reused.
- */
-
-#include "FN_multi_function_network_evaluation.hh"
-
-#include "BLI_resource_scope.hh"
-#include "BLI_stack.hh"
-
-namespace blender::fn {
-
-struct Value;
-
-/**
- * This keeps track of all the values that flow through the multi-function network. Therefore it
- * maintains a mapping between output sockets and their corresponding values. Every `value`
- * references some memory, that is owned either by the caller or this storage.
- *
- * A value can be owned by different sockets over time to avoid unnecessary copies.
- */
-class MFNetworkEvaluationStorage {
- private:
- LinearAllocator<> allocator_;
- IndexMask mask_;
- Array<Value *> value_per_output_id_;
- int64_t min_array_size_;
-
- public:
- MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount);
- ~MFNetworkEvaluationStorage();
-
- /* Add the values that have been provided by the caller of the multi-function network. */
- void add_single_input_from_caller(const MFOutputSocket &socket, const GVArray &virtual_array);
- void add_vector_input_from_caller(const MFOutputSocket &socket,
- const GVVectorArray &virtual_vector_array);
- void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span);
- void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array);
-
- /* Get input buffers for function node evaluations. */
- const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope);
- const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope);
- const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope);
- const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope);
-
- /* Get output buffers for function node evaluations. */
- GMutableSpan get_single_output__full(const MFOutputSocket &socket);
- GMutableSpan get_single_output__single(const MFOutputSocket &socket);
- GVectorArray &get_vector_output__full(const MFOutputSocket &socket);
- GVectorArray &get_vector_output__single(const MFOutputSocket &socket);
-
- /* Get mutable buffers for function node evaluations. */
- GMutableSpan get_mutable_single__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GMutableSpan get_mutable_single__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GVectorArray &get_mutable_vector__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GVectorArray &get_mutable_vector__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
-
- /* Mark a node as being done with evaluation. This might free temporary buffers that are no
- * longer needed. */
- void finish_node(const MFFunctionNode &node);
- void finish_output_socket(const MFOutputSocket &socket);
- void finish_input_socket(const MFInputSocket &socket);
-
- IndexMask mask() const;
- bool socket_is_computed(const MFOutputSocket &socket);
- bool is_same_value_for_every_index(const MFOutputSocket &socket);
- bool socket_has_buffer_for_output(const MFOutputSocket &socket);
-};
-
-MFNetworkEvaluator::MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs,
- Vector<const MFInputSocket *> outputs)
- : inputs_(std::move(inputs)), outputs_(std::move(outputs))
-{
- BLI_assert(outputs_.size() > 0);
- MFSignatureBuilder signature{"Function Tree"};
-
- for (const MFOutputSocket *socket : inputs_) {
- BLI_assert(socket->node().is_dummy());
-
- MFDataType type = socket->data_type();
- switch (type.category()) {
- case MFDataType::Single:
- signature.single_input(socket->name(), type.single_type());
- break;
- case MFDataType::Vector:
- signature.vector_input(socket->name(), type.vector_base_type());
- break;
- }
- }
-
- for (const MFInputSocket *socket : outputs_) {
- BLI_assert(socket->node().is_dummy());
-
- MFDataType type = socket->data_type();
- switch (type.category()) {
- case MFDataType::Single:
- signature.single_output(socket->name(), type.single_type());
- break;
- case MFDataType::Vector:
- signature.vector_output(socket->name(), type.vector_base_type());
- break;
- }
- }
-
- signature_ = signature.build();
- this->set_signature(&signature_);
-}
-
-void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const
-{
- if (mask.size() == 0) {
- return;
- }
-
- const MFNetwork &network = outputs_[0]->node().network();
- Storage storage(mask, network.socket_id_amount());
-
- Vector<const MFInputSocket *> outputs_to_initialize_in_the_end;
-
- this->copy_inputs_to_storage(params, storage);
- this->copy_outputs_to_storage(params, storage, outputs_to_initialize_in_the_end);
- this->evaluate_network_to_compute_outputs(context, storage);
- this->initialize_remaining_outputs(params, storage, outputs_to_initialize_in_the_end);
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params,
- Storage &storage) const
-{
- for (int input_index : inputs_.index_range()) {
- int param_index = input_index + 0;
- const MFOutputSocket &socket = *inputs_[input_index];
- switch (socket.data_type().category()) {
- case MFDataType::Single: {
- const GVArray &input_list = params.readonly_single_input(param_index);
- storage.add_single_input_from_caller(socket, input_list);
- break;
- }
- case MFDataType::Vector: {
- const GVVectorArray &input_list_list = params.readonly_vector_input(param_index);
- storage.add_vector_input_from_caller(socket, input_list_list);
- break;
- }
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::copy_outputs_to_storage(
- MFParams params,
- Storage &storage,
- Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const
-{
- for (int output_index : outputs_.index_range()) {
- int param_index = output_index + inputs_.size();
- const MFInputSocket &socket = *outputs_[output_index];
- const MFOutputSocket &origin = *socket.origin();
-
- if (origin.node().is_dummy()) {
- BLI_assert(inputs_.contains(&origin));
- /* Don't overwrite input buffers. */
- outputs_to_initialize_in_the_end.append(&socket);
- continue;
- }
-
- if (storage.socket_has_buffer_for_output(origin)) {
- /* When two outputs will be initialized to the same values. */
- outputs_to_initialize_in_the_end.append(&socket);
- continue;
- }
-
- switch (socket.data_type().category()) {
- case MFDataType::Single: {
- GMutableSpan span = params.uninitialized_single_output(param_index);
- storage.add_single_output_from_caller(origin, span);
- break;
- }
- case MFDataType::Vector: {
- GVectorArray &vector_array = params.vector_output(param_index);
- storage.add_vector_output_from_caller(origin, vector_array);
- break;
- }
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs(
- MFContext &global_context, Storage &storage) const
-{
- Stack<const MFOutputSocket *, 32> sockets_to_compute;
- for (const MFInputSocket *socket : outputs_) {
- sockets_to_compute.push(socket->origin());
- }
-
- /* This is the main loop that traverses the MFNetwork. */
- while (!sockets_to_compute.is_empty()) {
- const MFOutputSocket &socket = *sockets_to_compute.peek();
- const MFNode &node = socket.node();
-
- if (storage.socket_is_computed(socket)) {
- sockets_to_compute.pop();
- continue;
- }
-
- BLI_assert(node.is_function());
- BLI_assert(!node.has_unlinked_inputs());
- const MFFunctionNode &function_node = node.as_function();
-
- bool all_origins_are_computed = true;
- for (const MFInputSocket *input_socket : function_node.inputs()) {
- const MFOutputSocket *origin = input_socket->origin();
- if (origin != nullptr) {
- if (!storage.socket_is_computed(*origin)) {
- sockets_to_compute.push(origin);
- all_origins_are_computed = false;
- }
- }
- }
-
- if (all_origins_are_computed) {
- this->evaluate_function(global_context, function_node, storage);
- sockets_to_compute.pop();
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_context,
- const MFFunctionNode &function_node,
- Storage &storage) const
-{
-
- const MultiFunction &function = function_node.function();
- // std::cout << "Function: " << function.name() << "\n";
-
- if (this->can_do_single_value_evaluation(function_node, storage)) {
- /* The function output would be the same for all elements. Therefore, it is enough to call the
- * function only on a single element. This can avoid many duplicate computations. */
- MFParamsBuilder params{function, 1};
- ResourceScope &scope = params.resource_scope();
-
- for (int param_index : function.param_indices()) {
- MFParamType param_type = function.param_type(param_index);
- switch (param_type.category()) {
- case MFParamType::SingleInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__single(socket, scope);
- params.add_readonly_single_input(values);
- break;
- }
- case MFParamType::VectorInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__single(socket, scope);
- params.add_readonly_vector_input(values);
- break;
- }
- case MFParamType::SingleOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_single_output__single(socket);
- params.add_uninitialized_single_output(values);
- break;
- }
- case MFParamType::VectorOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_vector_output__single(socket);
- params.add_vector_output(values);
- break;
- }
- case MFParamType::SingleMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__single(input, output, scope);
- params.add_single_mutable(values);
- break;
- }
- case MFParamType::VectorMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__single(input, output, scope);
- params.add_vector_mutable(values);
- break;
- }
- }
- }
-
- function.call(IndexRange(1), params, global_context);
- }
- else {
- MFParamsBuilder params{function, storage.mask().min_array_size()};
- ResourceScope &scope = params.resource_scope();
-
- for (int param_index : function.param_indices()) {
- MFParamType param_type = function.param_type(param_index);
- switch (param_type.category()) {
- case MFParamType::SingleInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__full(socket, scope);
- params.add_readonly_single_input(values);
- break;
- }
- case MFParamType::VectorInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__full(socket, scope);
- params.add_readonly_vector_input(values);
- break;
- }
- case MFParamType::SingleOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_single_output__full(socket);
- params.add_uninitialized_single_output(values);
- break;
- }
- case MFParamType::VectorOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_vector_output__full(socket);
- params.add_vector_output(values);
- break;
- }
- case MFParamType::SingleMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__full(input, output, scope);
- params.add_single_mutable(values);
- break;
- }
- case MFParamType::VectorMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__full(input, output, scope);
- params.add_vector_mutable(values);
- break;
- }
- }
- }
-
- function.call(storage.mask(), params, global_context);
- }
-
- storage.finish_node(function_node);
-}
-
-bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &function_node,
- Storage &storage) const
-{
- for (const MFInputSocket *socket : function_node.inputs()) {
- if (!storage.is_same_value_for_every_index(*socket->origin())) {
- return false;
- }
- }
- if (storage.mask().min_array_size() >= 1) {
- for (const MFOutputSocket *socket : function_node.outputs()) {
- if (storage.socket_has_buffer_for_output(*socket)) {
- return false;
- }
- }
- }
- return true;
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs(
- MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const
-{
- ResourceScope scope;
- for (const MFInputSocket *socket : remaining_outputs) {
- int param_index = inputs_.size() + outputs_.first_index_of(socket);
-
- switch (socket->data_type().category()) {
- case MFDataType::Single: {
- const GVArray &values = storage.get_single_input__full(*socket, scope);
- GMutableSpan output_values = params.uninitialized_single_output(param_index);
- values.materialize_to_uninitialized(storage.mask(), output_values.data());
- break;
- }
- case MFDataType::Vector: {
- const GVVectorArray &values = storage.get_vector_input__full(*socket, scope);
- GVectorArray &output_values = params.vector_output(param_index);
- output_values.extend(storage.mask(), values);
- break;
- }
- }
- }
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Value Types
- * \{ */
-
-enum class ValueType {
- InputSingle,
- InputVector,
- OutputSingle,
- OutputVector,
- OwnSingle,
- OwnVector,
-};
-
-struct Value {
- ValueType type;
-
- Value(ValueType type) : type(type)
- {
- }
-};
-
-struct InputSingleValue : public Value {
- /** This virtual array has been provided by the code that called the multi-function network. */
- const GVArray &virtual_array;
-
- InputSingleValue(const GVArray &virtual_array)
- : Value(ValueType::InputSingle), virtual_array(virtual_array)
- {
- }
-};
-
-struct InputVectorValue : public Value {
- /** This virtual vector has been provided by the code that called the multi-function network. */
- const GVVectorArray &virtual_vector_array;
-
- InputVectorValue(const GVVectorArray &virtual_vector_array)
- : Value(ValueType::InputVector), virtual_vector_array(virtual_vector_array)
- {
- }
-};
-
-struct OutputValue : public Value {
- bool is_computed = false;
-
- OutputValue(ValueType type) : Value(type)
- {
- }
-};
-
-struct OutputSingleValue : public OutputValue {
- /** This span has been provided by the code that called the multi-function network. */
- GMutableSpan span;
-
- OutputSingleValue(GMutableSpan span) : OutputValue(ValueType::OutputSingle), span(span)
- {
- }
-};
-
-struct OutputVectorValue : public OutputValue {
- /** This vector array has been provided by the code that called the multi-function network. */
- GVectorArray *vector_array;
-
- OutputVectorValue(GVectorArray &vector_array)
- : OutputValue(ValueType::OutputVector), vector_array(&vector_array)
- {
- }
-};
-
-struct OwnSingleValue : public Value {
- /** This span has been allocated during the evaluation of the multi-function network and contains
- * intermediate data. It has to be freed once the network evaluation is finished. */
- GMutableSpan span;
- int max_remaining_users;
- bool is_single_allocated;
-
- OwnSingleValue(GMutableSpan span, int max_remaining_users, bool is_single_allocated)
- : Value(ValueType::OwnSingle),
- span(span),
- max_remaining_users(max_remaining_users),
- is_single_allocated(is_single_allocated)
- {
- }
-};
-
-struct OwnVectorValue : public Value {
- /** This vector array has been allocated during the evaluation of the multi-function network and
- * contains intermediate data. It has to be freed once the network evaluation is finished. */
- GVectorArray *vector_array;
- int max_remaining_users;
-
- OwnVectorValue(GVectorArray &vector_array, int max_remaining_users)
- : Value(ValueType::OwnVector),
- vector_array(&vector_array),
- max_remaining_users(max_remaining_users)
- {
- }
-};
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Storage methods
- * \{ */
-
-MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount)
- : mask_(mask),
- value_per_output_id_(socket_id_amount, nullptr),
- min_array_size_(mask.min_array_size())
-{
-}
-
-MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage()
-{
- for (Value *any_value : value_per_output_id_) {
- if (any_value == nullptr) {
- continue;
- }
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- GMutableSpan span = value->span;
- const CPPType &type = span.type();
- if (value->is_single_allocated) {
- type.destruct(span.data());
- }
- else {
- type.destruct_indices(span.data(), mask_);
- MEM_freeN(span.data());
- }
- }
- else if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- delete value->vector_array;
- }
- }
-}
-
-IndexMask MFNetworkEvaluationStorage::mask() const
-{
- return mask_;
-}
-
-bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return false;
- }
- if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
- return static_cast<OutputValue *>(any_value)->is_computed;
- }
- return true;
-}
-
-bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- switch (any_value->type) {
- case ValueType::OwnSingle:
- return static_cast<OwnSingleValue *>(any_value)->span.size() == 1;
- case ValueType::OwnVector:
- return static_cast<OwnVectorValue *>(any_value)->vector_array->size() == 1;
- case ValueType::InputSingle:
- return static_cast<InputSingleValue *>(any_value)->virtual_array.is_single();
- case ValueType::InputVector:
- return static_cast<InputVectorValue *>(any_value)->virtual_vector_array.is_single_vector();
- case ValueType::OutputSingle:
- return static_cast<OutputSingleValue *>(any_value)->span.size() == 1;
- case ValueType::OutputVector:
- return static_cast<OutputVectorValue *>(any_value)->vector_array->size() == 1;
- }
- BLI_assert(false);
- return false;
-}
-
-bool MFNetworkEvaluationStorage::socket_has_buffer_for_output(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return false;
- }
-
- BLI_assert(ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector));
- return true;
-}
-
-void MFNetworkEvaluationStorage::finish_node(const MFFunctionNode &node)
-{
- for (const MFInputSocket *socket : node.inputs()) {
- this->finish_input_socket(*socket);
- }
- for (const MFOutputSocket *socket : node.outputs()) {
- this->finish_output_socket(*socket);
- }
-}
-
-void MFNetworkEvaluationStorage::finish_output_socket(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return;
- }
-
- if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
- static_cast<OutputValue *>(any_value)->is_computed = true;
- }
-}
-
-void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket)
-{
- const MFOutputSocket &origin = *socket.origin();
-
- Value *any_value = value_per_output_id_[origin.id()];
- if (any_value == nullptr) {
- /* Can happen when a value has been forward to the next node. */
- return;
- }
-
- switch (any_value->type) {
- case ValueType::InputSingle:
- case ValueType::OutputSingle:
- case ValueType::InputVector:
- case ValueType::OutputVector: {
- break;
- }
- case ValueType::OwnSingle: {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- BLI_assert(value->max_remaining_users >= 1);
- value->max_remaining_users--;
- if (value->max_remaining_users == 0) {
- GMutableSpan span = value->span;
- const CPPType &type = span.type();
- if (value->is_single_allocated) {
- type.destruct(span.data());
- }
- else {
- type.destruct_indices(span.data(), mask_);
- MEM_freeN(span.data());
- }
- value_per_output_id_[origin.id()] = nullptr;
- }
- break;
- }
- case ValueType::OwnVector: {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- BLI_assert(value->max_remaining_users >= 1);
- value->max_remaining_users--;
- if (value->max_remaining_users == 0) {
- delete value->vector_array;
- value_per_output_id_[origin.id()] = nullptr;
- }
- break;
- }
- }
-}
-
-void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket,
- const GVArray &virtual_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(virtual_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<InputSingleValue>(virtual_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_vector_input_from_caller(
- const MFOutputSocket &socket, const GVVectorArray &virtual_vector_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(virtual_vector_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<InputVectorValue>(virtual_vector_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket,
- GMutableSpan span)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(span.size() >= min_array_size_);
-
- auto *value = allocator_.construct<OutputSingleValue>(span).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket,
- GVectorArray &vector_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(vector_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<OutputVectorValue>(vector_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().single_type();
- void *buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
- GMutableSpan span(type, buffer, min_array_size_);
-
- auto *value =
- allocator_.construct<OwnSingleValue>(span, socket.targets().size(), false).release();
- value_per_output_id_[socket.id()] = value;
-
- return span;
- }
-
- BLI_assert(any_value->type == ValueType::OutputSingle);
- return static_cast<OutputSingleValue *>(any_value)->span;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_single_output__single(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().single_type();
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- GMutableSpan span(type, buffer, 1);
-
- auto *value =
- allocator_.construct<OwnSingleValue>(span, socket.targets().size(), true).release();
- value_per_output_id_[socket.id()] = value;
-
- return value->span;
- }
-
- BLI_assert(any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(any_value)->span;
- BLI_assert(span.size() == 1);
- return span;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_vector_output__full(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().vector_base_type();
- GVectorArray *vector_array = new GVectorArray(type, min_array_size_);
-
- auto *value =
- allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release();
- value_per_output_id_[socket.id()] = value;
-
- return *value->vector_array;
- }
-
- BLI_assert(any_value->type == ValueType::OutputVector);
- return *static_cast<OutputVectorValue *>(any_value)->vector_array;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().vector_base_type();
- GVectorArray *vector_array = new GVectorArray(type, 1);
-
- auto *value =
- allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release();
- value_per_output_id_[socket.id()] = value;
-
- return *value->vector_array;
- }
-
- BLI_assert(any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(any_value)->vector_array;
- BLI_assert(vector_array.size() == 1);
- return vector_array;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &type = from.data_type().single_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(type == to.data_type().single_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
- const GVArray &virtual_array = this->get_single_input__full(input, scope);
- virtual_array.materialize_to_uninitialized(mask_, span.data());
- return span;
- }
-
- if (from_any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value);
- if (value->max_remaining_users == 1 && !value->is_single_allocated) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return value->span;
- }
- }
-
- const GVArray &virtual_array = this->get_single_input__full(input, scope);
- void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
- GMutableSpan new_array_ref(type, new_buffer, min_array_size_);
- virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data());
-
- OwnSingleValue *new_value =
- allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), false).release();
- value_per_output_id_[to.id()] = new_value;
- return new_array_ref;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &type = from.data_type().single_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(type == to.data_type().single_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
- BLI_assert(span.size() == 1);
- const GVArray &virtual_array = this->get_single_input__single(input, scope);
- virtual_array.get_single_to_uninitialized(span[0]);
- return span;
- }
-
- if (from_any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- BLI_assert(value->span.size() == 1);
- return value->span;
- }
- }
-
- const GVArray &virtual_array = this->get_single_input__single(input, scope);
-
- void *new_buffer = allocator_.allocate(type.size(), type.alignment());
- virtual_array.get_single_to_uninitialized(new_buffer);
- GMutableSpan new_array_ref(type, new_buffer, 1);
-
- OwnSingleValue *new_value =
- allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), true).release();
- value_per_output_id_[to.id()] = new_value;
- return new_array_ref;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &base_type = from.data_type().vector_base_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(base_type == to.data_type().vector_base_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
- vector_array.extend(mask_, virtual_vector_array);
- return vector_array;
- }
-
- if (from_any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return *value->vector_array;
- }
- }
-
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
-
- GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_);
- new_vector_array->extend(mask_, virtual_vector_array);
-
- OwnVectorValue *new_value =
- allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release();
- value_per_output_id_[to.id()] = new_value;
-
- return *new_vector_array;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &base_type = from.data_type().vector_base_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(base_type == to.data_type().vector_base_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
- BLI_assert(vector_array.size() == 1);
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
- vector_array.extend({0}, virtual_vector_array);
- return vector_array;
- }
-
- if (from_any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return *value->vector_array;
- }
- }
-
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
-
- GVectorArray *new_vector_array = new GVectorArray(base_type, 1);
- new_vector_array->extend({0}, virtual_vector_array);
-
- OwnVectorValue *new_value =
- allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release();
- value_per_output_id_[to.id()] = new_value;
- return *new_vector_array;
-}
-
-const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket,
- ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- if (value->is_single_allocated) {
- return scope.construct<GVArray_For_SingleValueRef>(
- __func__, value->span.type(), min_array_size_, value->span.data());
- }
-
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
- if (any_value->type == ValueType::InputSingle) {
- InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
- return value->virtual_array;
- }
- if (any_value->type == ValueType::OutputSingle) {
- OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
- BLI_assert(value->is_computed);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
-
- BLI_assert(false);
- return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
-}
-
-const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
- ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- BLI_assert(value->span.size() == 1);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
- if (any_value->type == ValueType::InputSingle) {
- InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
- BLI_assert(value->virtual_array.is_single());
- return value->virtual_array;
- }
- if (any_value->type == ValueType::OutputSingle) {
- OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
- BLI_assert(value->is_computed);
- BLI_assert(value->span.size() == 1);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
-
- BLI_assert(false);
- return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
-}
-
-const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
- const MFInputSocket &socket, ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- if (value->vector_array->size() == 1) {
- GSpan span = (*value->vector_array)[0];
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_);
- }
-
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
- if (any_value->type == ValueType::InputVector) {
- InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
- return value->virtual_vector_array;
- }
- if (any_value->type == ValueType::OutputVector) {
- OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
-
- BLI_assert(false);
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
-}
-
-const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
- const MFInputSocket &socket, ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- BLI_assert(value->vector_array->size() == 1);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
- if (any_value->type == ValueType::InputVector) {
- InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
- BLI_assert(value->virtual_vector_array.is_single_vector());
- return value->virtual_vector_array;
- }
- if (any_value->type == ValueType::OutputVector) {
- OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- BLI_assert(value->vector_array->size() == 1);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
-
- BLI_assert(false);
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
-}
-
-/** \} */
-
-} // namespace blender::fn
diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc
deleted file mode 100644
index 75c3583c5e5..00000000000
--- a/source/blender/functions/intern/multi_function_network_optimization.cc
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * 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.
- */
-
-/** \file
- * \ingroup fn
- */
-
-/* Used to check if two multi-functions have the exact same type. */
-#include <typeinfo>
-
-#include "FN_multi_function_builder.hh"
-#include "FN_multi_function_network_evaluation.hh"
-#include "FN_multi_function_network_optimization.hh"
-
-#include "BLI_disjoint_set.hh"
-#include "BLI_ghash.h"
-#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
-#include "BLI_rand.h"
-#include "BLI_stack.hh"
-
-namespace blender::fn::mf_network_optimization {
-
-/* -------------------------------------------------------------------- */
-/** \name Utility functions to find nodes in a network.
- * \{ */
-
-static bool set_tag_and_check_if_modified(bool &tag, bool new_value)
-{
- if (tag != new_value) {
- tag = new_value;
- return true;
- }
-
- return false;
-}
-
-static Array<bool> mask_nodes_to_the_left(MFNetwork &network, Span<MFNode *> nodes)
-{
- Array<bool> is_to_the_left(network.node_id_amount(), false);
- Stack<MFNode *> nodes_to_check;
-
- for (MFNode *node : nodes) {
- is_to_the_left[node->id()] = true;
- nodes_to_check.push(node);
- }
-
- while (!nodes_to_check.is_empty()) {
- MFNode &node = *nodes_to_check.pop();
-
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin = input_socket->origin();
- if (origin != nullptr) {
- MFNode &origin_node = origin->node();
- if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) {
- nodes_to_check.push(&origin_node);
- }
- }
- }
- }
-
- return is_to_the_left;
-}
-
-static Array<bool> mask_nodes_to_the_right(MFNetwork &network, Span<MFNode *> nodes)
-{
- Array<bool> is_to_the_right(network.node_id_amount(), false);
- Stack<MFNode *> nodes_to_check;
-
- for (MFNode *node : nodes) {
- is_to_the_right[node->id()] = true;
- nodes_to_check.push(node);
- }
-
- while (!nodes_to_check.is_empty()) {
- MFNode &node = *nodes_to_check.pop();
-
- for (MFOutputSocket *output_socket : node.outputs()) {
- for (MFInputSocket *target_socket : output_socket->targets()) {
- MFNode &target_node = target_socket->node();
- if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) {
- nodes_to_check.push(&target_node);
- }
- }
- }
- }
-
- return is_to_the_right;
-}
-
-static Vector<MFNode *> find_nodes_based_on_mask(MFNetwork &network,
- Span<bool> id_mask,
- bool mask_value)
-{
- Vector<MFNode *> nodes;
- for (int id : id_mask.index_range()) {
- if (id_mask[id] == mask_value) {
- MFNode *node = network.node_or_null_by_id(id);
- if (node != nullptr) {
- nodes.append(node);
- }
- }
- }
- return nodes;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Dead Node Removal
- * \{ */
-
-/**
- * Unused nodes are all those nodes that no dummy node depends upon.
- */
-void dead_node_removal(MFNetwork &network)
-{
- Array<bool> node_is_used_mask = mask_nodes_to_the_left(network,
- network.dummy_nodes().cast<MFNode *>());
- Vector<MFNode *> nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false);
- network.remove(nodes_to_remove);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Constant Folding
- * \{ */
-
-static bool function_node_can_be_constant(MFFunctionNode *node)
-{
- if (node->has_unlinked_inputs()) {
- return false;
- }
- if (node->function().depends_on_context()) {
- return false;
- }
- return true;
-}
-
-static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network)
-{
- Vector<MFNode *> non_constant_nodes;
- non_constant_nodes.extend(network.dummy_nodes().cast<MFNode *>());
-
- for (MFFunctionNode *node : network.function_nodes()) {
- if (!function_node_can_be_constant(node)) {
- non_constant_nodes.append(node);
- }
- }
- return non_constant_nodes;
-}
-
-static bool output_has_non_constant_target_node(MFOutputSocket *output_socket,
- Span<bool> is_not_constant_mask)
-{
- for (MFInputSocket *target_socket : output_socket->targets()) {
- MFNode &target_node = target_socket->node();
- bool target_is_not_constant = is_not_constant_mask[target_node.id()];
- if (target_is_not_constant) {
- return true;
- }
- }
- return false;
-}
-
-static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket)
-{
- for (MFInputSocket *target_socket : output_socket->targets()) {
- if (target_socket->node().is_dummy()) {
- return target_socket;
- }
- }
- return nullptr;
-}
-
-static Vector<MFInputSocket *> find_constant_inputs_to_fold(
- MFNetwork &network, Vector<MFDummyNode *> &r_temporary_nodes)
-{
- Vector<MFNode *> non_constant_nodes = find_non_constant_nodes(network);
- Array<bool> is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes);
- Vector<MFNode *> constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false);
-
- Vector<MFInputSocket *> sockets_to_compute;
- for (MFNode *node : constant_nodes) {
- if (node->inputs().size() == 0) {
- continue;
- }
-
- for (MFOutputSocket *output_socket : node->outputs()) {
- MFDataType data_type = output_socket->data_type();
- if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) {
- MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket);
- if (dummy_target == nullptr) {
- dummy_target = &network.add_output("Dummy", data_type);
- network.add_link(*output_socket, *dummy_target);
- r_temporary_nodes.append(&dummy_target->node().as_dummy());
- }
-
- sockets_to_compute.append(dummy_target);
- }
- }
- }
- return sockets_to_compute;
-}
-
-static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
- MFParamsBuilder &params,
- ResourceScope &scope)
-{
- for (int param_index : network_fn.param_indices()) {
- MFParamType param_type = network_fn.param_type(param_index);
- MFDataType data_type = param_type.data_type();
-
- switch (data_type.category()) {
- case MFDataType::Single: {
- /* Allocates memory for a single constant folded value. */
- const CPPType &cpp_type = data_type.single_type();
- void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment());
- GMutableSpan array{cpp_type, buffer, 1};
- params.add_uninitialized_single_output(array);
- break;
- }
- case MFDataType::Vector: {
- /* Allocates memory for a constant folded vector. */
- const CPPType &cpp_type = data_type.vector_base_type();
- GVectorArray &vector_array = scope.construct<GVectorArray>(AT, cpp_type, 1);
- params.add_vector_output(vector_array);
- break;
- }
- }
- }
-}
-
-static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn,
- MFParamsBuilder &params,
- ResourceScope &scope,
- MFNetwork &network)
-{
- Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr};
-
- for (int param_index : network_fn.param_indices()) {
- MFParamType param_type = network_fn.param_type(param_index);
- MFDataType data_type = param_type.data_type();
-
- const MultiFunction *constant_fn = nullptr;
-
- switch (data_type.category()) {
- case MFDataType::Single: {
- const CPPType &cpp_type = data_type.single_type();
- GMutableSpan array = params.computed_array(param_index);
- void *buffer = array.data();
- scope.add(buffer, array.type().destruct_fn(), AT);
-
- constant_fn = &scope.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
- break;
- }
- case MFDataType::Vector: {
- GVectorArray &vector_array = params.computed_vector_array(param_index);
- GSpan array = vector_array[0];
- constant_fn = &scope.construct<CustomMF_GenericConstantArray>(AT, array);
- break;
- }
- }
-
- MFFunctionNode &folded_node = network.add_function(*constant_fn);
- folded_sockets[param_index] = &folded_node.output(0);
- }
- return folded_sockets;
-}
-
-static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes(
- MFNetwork &network, Span<const MFInputSocket *> sockets_to_compute, ResourceScope &scope)
-{
- MFNetworkEvaluator network_fn{{}, sockets_to_compute};
-
- MFContextBuilder context;
- MFParamsBuilder params{network_fn, 1};
- prepare_params_for_constant_folding(network_fn, params, scope);
- network_fn.call({0}, params, context);
- return add_constant_folded_sockets(network_fn, params, scope, network);
-}
-
-class MyClass {
- MFDummyNode node;
-};
-
-/**
- * Find function nodes that always output the same value and replace those with constant nodes.
- */
-void constant_folding(MFNetwork &network, ResourceScope &scope)
-{
- Vector<MFDummyNode *> temporary_nodes;
- Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes);
- if (inputs_to_fold.size() == 0) {
- return;
- }
-
- Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes(
- network, inputs_to_fold, scope);
-
- for (int i : inputs_to_fold.index_range()) {
- MFOutputSocket &original_socket = *inputs_to_fold[i]->origin();
- network.relink(original_socket, *folded_sockets[i]);
- }
-
- network.remove(temporary_nodes.as_span().cast<MFNode *>());
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Common Sub-network Elimination
- * \{ */
-
-static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint64_t> node_hashes)
-{
- if (node.function().depends_on_context()) {
- return BLI_rng_get_uint(rng);
- }
- if (node.has_unlinked_inputs()) {
- return BLI_rng_get_uint(rng);
- }
-
- uint64_t combined_inputs_hash = 394659347u;
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin_socket = input_socket->origin();
- uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()],
- origin_socket->index());
- combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash);
- }
-
- uint64_t function_hash = node.function().hash();
- uint64_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash);
- return node_hash;
-}
-
-/**
- * Produces a hash for every node. Two nodes with the same hash should have a high probability of
- * outputting the same values.
- */
-static Array<uint64_t> compute_node_hashes(MFNetwork &network)
-{
- RNG *rng = BLI_rng_new(0);
- Array<uint64_t> node_hashes(network.node_id_amount());
- Array<bool> node_is_hashed(network.node_id_amount(), false);
-
- /* No dummy nodes are not assumed to output the same values. */
- for (MFDummyNode *node : network.dummy_nodes()) {
- uint64_t node_hash = BLI_rng_get_uint(rng);
- node_hashes[node->id()] = node_hash;
- node_is_hashed[node->id()] = true;
- }
-
- Stack<MFFunctionNode *> nodes_to_check;
- nodes_to_check.push_multiple(network.function_nodes());
-
- while (!nodes_to_check.is_empty()) {
- MFFunctionNode &node = *nodes_to_check.peek();
- if (node_is_hashed[node.id()]) {
- nodes_to_check.pop();
- continue;
- }
-
- /* Make sure that origin nodes are hashed first. */
- bool all_dependencies_ready = true;
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin_socket = input_socket->origin();
- if (origin_socket != nullptr) {
- MFNode &origin_node = origin_socket->node();
- if (!node_is_hashed[origin_node.id()]) {
- all_dependencies_ready = false;
- nodes_to_check.push(&origin_node.as_function());
- }
- }
- }
- if (!all_dependencies_ready) {
- continue;
- }
-
- uint64_t node_hash = compute_node_hash(node, rng, node_hashes);
- node_hashes[node.id()] = node_hash;
- node_is_hashed[node.id()] = true;
- nodes_to_check.pop();
- }
-
- BLI_rng_free(rng);
- return node_hashes;
-}
-
-static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network,
- Span<uint64_t> node_hashes)
-{
- MultiValueMap<uint64_t, MFNode *> nodes_by_hash;
- for (int id : IndexRange(network.node_id_amount())) {
- MFNode *node = network.node_or_null_by_id(id);
- if (node != nullptr) {
- uint64_t node_hash = node_hashes[id];
- nodes_by_hash.add(node_hash, node);
- }
- }
- return nodes_by_hash;
-}
-
-static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b)
-{
- if (&a == &b) {
- return true;
- }
- if (typeid(a) == typeid(b)) {
- return a.equals(b);
- }
- return false;
-}
-
-static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b)
-{
- if (cache.in_same_set(a.id(), b.id())) {
- return true;
- }
-
- if (a.is_dummy() || b.is_dummy()) {
- return false;
- }
- if (!functions_are_equal(a.as_function().function(), b.as_function().function())) {
- return false;
- }
- for (int i : a.inputs().index_range()) {
- const MFOutputSocket *origin_a = a.input(i).origin();
- const MFOutputSocket *origin_b = b.input(i).origin();
- if (origin_a == nullptr || origin_b == nullptr) {
- return false;
- }
- if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) {
- return false;
- }
- }
-
- cache.join(a.id(), b.id());
- return true;
-}
-
-static void relink_duplicate_nodes(MFNetwork &network,
- MultiValueMap<uint64_t, MFNode *> &nodes_by_hash)
-{
- DisjointSet same_node_cache{network.node_id_amount()};
-
- for (Span<MFNode *> nodes_with_same_hash : nodes_by_hash.values()) {
- if (nodes_with_same_hash.size() <= 1) {
- continue;
- }
-
- Vector<MFNode *, 16> nodes_to_check = nodes_with_same_hash;
- while (nodes_to_check.size() >= 2) {
- Vector<MFNode *, 16> remaining_nodes;
-
- MFNode &deduplicated_node = *nodes_to_check[0];
- for (MFNode *node : nodes_to_check.as_span().drop_front(1)) {
- /* This is true with fairly high probability, but hash collisions can happen. So we have to
- * check if the node actually output the same values. */
- if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) {
- for (int i : deduplicated_node.outputs().index_range()) {
- network.relink(node->output(i), deduplicated_node.output(i));
- }
- }
- else {
- remaining_nodes.append(node);
- }
- }
- nodes_to_check = std::move(remaining_nodes);
- }
- }
-}
-
-/**
- * Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node
- * groups were used to create the network.
- */
-void common_subnetwork_elimination(MFNetwork &network)
-{
- Array<uint64_t> node_hashes = compute_node_hashes(network);
- MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes);
- relink_duplicate_nodes(network, nodes_by_hash);
-}
-
-/** \} */
-
-} // namespace blender::fn::mf_network_optimization
diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc
deleted file mode 100644
index 7b9738e5ca4..00000000000
--- a/source/blender/functions/tests/FN_multi_function_network_test.cc
+++ /dev/null
@@ -1,280 +0,0 @@
-/* Apache License, Version 2.0 */
-
-#include "testing/testing.h"
-
-#include "FN_multi_function_builder.hh"
-#include "FN_multi_function_network.hh"
-#include "FN_multi_function_network_evaluation.hh"
-
-namespace blender::fn::tests {
-namespace {
-
-TEST(multi_function_network, Test1)
-{
- CustomMF_SI_SO<int, int> add_10_fn("add 10", [](int value) { return value + 10; });
- CustomMF_SI_SI_SO<int, int, int> multiply_fn("multiply", [](int a, int b) { return a * b; });
-
- MFNetwork network;
-
- MFNode &node1 = network.add_function(add_10_fn);
- MFNode &node2 = network.add_function(multiply_fn);
- MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle<int>());
- MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle<int>());
- network.add_link(node1.output(0), node2.input(0));
- network.add_link(node1.output(0), node2.input(1));
- network.add_link(node2.output(0), output_socket);
- network.add_link(input_socket, node1.input(0));
-
- MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}};
-
- {
- Array<int> values = {4, 6, 1, 2, 0};
- Array<int> results(values.size(), 0);
-
- MFParamsBuilder params(network_fn, values.size());
- params.add_readonly_single_input(values.as_span());
- params.add_uninitialized_single_output(results.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({0, 2, 3, 4}, params, context);
-
- EXPECT_EQ(results[0], 14 * 14);
- EXPECT_EQ(results[1], 0);
- EXPECT_EQ(results[2], 11 * 11);
- EXPECT_EQ(results[3], 12 * 12);
- EXPECT_EQ(results[4], 10 * 10);
- }
- {
- int value = 3;
- Array<int> results(5, 0);
-
- MFParamsBuilder params(network_fn, results.size());
- params.add_readonly_single_input(&value);
- params.add_uninitialized_single_output(results.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({1, 2, 4}, params, context);
-
- EXPECT_EQ(results[0], 0);
- EXPECT_EQ(results[1], 13 * 13);
- EXPECT_EQ(results[2], 13 * 13);
- EXPECT_EQ(results[3], 0);
- EXPECT_EQ(results[4], 13 * 13);
- }
-}
-
-class ConcatVectorsFunction : public MultiFunction {
- public:
- ConcatVectorsFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Concat Vectors"};
- signature.vector_mutable<int>("A");
- signature.vector_input<int>("B");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray &a = params.vector_mutable(0);
- const GVVectorArray &b = params.readonly_vector_input(1);
- a.extend(mask, b);
- }
-};
-
-class AppendFunction : public MultiFunction {
- public:
- AppendFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Append"};
- signature.vector_mutable<int>("Vector");
- signature.single_input<int>("Value");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0);
- const VArray<int> &values = params.readonly_single_input<int>(1);
-
- for (int64_t i : mask) {
- vectors.append(i, values[i]);
- }
- }
-};
-
-class SumVectorFunction : public MultiFunction {
- public:
- SumVectorFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Sum Vectors"};
- signature.vector_input<int>("Vector");
- signature.single_output<int>("Sum");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0);
- MutableSpan<int> sums = params.uninitialized_single_output<int>(1);
-
- for (int64_t i : mask) {
- int sum = 0;
- for (int j : IndexRange(vectors.get_vector_size(i))) {
- sum += vectors.get_vector_element(i, j);
- }
- sums[i] = sum;
- }
- }
-};
-
-class CreateRangeFunction : public MultiFunction {
- public:
- CreateRangeFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Create Range"};
- signature.single_input<int>("Size");
- signature.vector_output<int>("Range");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size");
- GVectorArray_TypedMutableRef<int> ranges = params.vector_output<int>(1, "Range");
-
- for (int64_t i : mask) {
- int size = sizes[i];
- for (int j : IndexRange(size)) {
- ranges.append(i, j);
- }
- }
- }
-};
-
-TEST(multi_function_network, Test2)
-{
- CustomMF_SI_SO<int, int> add_3_fn("add 3", [](int value) { return value + 3; });
-
- ConcatVectorsFunction concat_vectors_fn;
- AppendFunction append_fn;
- SumVectorFunction sum_fn;
- CreateRangeFunction create_range_fn;
-
- MFNetwork network;
-
- MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector<int>());
- MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle<int>());
- MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector<int>());
- MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle<int>());
-
- MFNode &node1 = network.add_function(add_3_fn);
- MFNode &node2 = network.add_function(create_range_fn);
- MFNode &node3 = network.add_function(concat_vectors_fn);
- MFNode &node4 = network.add_function(sum_fn);
- MFNode &node5 = network.add_function(append_fn);
- MFNode &node6 = network.add_function(sum_fn);
-
- network.add_link(input2, node1.input(0));
- network.add_link(node1.output(0), node2.input(0));
- network.add_link(node2.output(0), node3.input(1));
- network.add_link(input1, node3.input(0));
- network.add_link(input1, node4.input(0));
- network.add_link(node4.output(0), node5.input(1));
- network.add_link(node3.output(0), node5.input(0));
- network.add_link(node5.output(0), node6.input(0));
- network.add_link(node3.output(0), output1);
- network.add_link(node6.output(0), output2);
-
- // std::cout << network.to_dot() << "\n\n";
-
- MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}};
-
- {
- Array<int> input_value_1 = {3, 6};
- int input_value_2 = 4;
-
- GVectorArray output_value_1(CPPType::get<int32_t>(), 5);
- Array<int> output_value_2(5, -1);
-
- MFParamsBuilder params(network_fn, 5);
- GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5};
- params.add_readonly_vector_input(inputs_1);
- params.add_readonly_single_input(&input_value_2);
- params.add_vector_output(output_value_1);
- params.add_uninitialized_single_output(output_value_2.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({1, 2, 4}, params, context);
-
- EXPECT_EQ(output_value_1[0].size(), 0);
- EXPECT_EQ(output_value_1[1].size(), 9);
- EXPECT_EQ(output_value_1[2].size(), 9);
- EXPECT_EQ(output_value_1[3].size(), 0);
- EXPECT_EQ(output_value_1[4].size(), 9);
-
- EXPECT_EQ(output_value_2[0], -1);
- EXPECT_EQ(output_value_2[1], 39);
- EXPECT_EQ(output_value_2[2], 39);
- EXPECT_EQ(output_value_2[3], -1);
- EXPECT_EQ(output_value_2[4], 39);
- }
- {
- GVectorArray input_value_1(CPPType::get<int32_t>(), 3);
- GVectorArray_TypedMutableRef<int> input_value_1_ref{input_value_1};
- input_value_1_ref.extend(0, {3, 4, 5});
- input_value_1_ref.extend(1, {1, 2});
-
- Array<int> input_value_2 = {4, 2, 3};
-
- GVectorArray output_value_1(CPPType::get<int32_t>(), 3);
- Array<int> output_value_2(3, -1);
-
- MFParamsBuilder params(network_fn, 3);
- params.add_readonly_vector_input(input_value_1);
- params.add_readonly_single_input(input_value_2.as_span());
- params.add_vector_output(output_value_1);
- params.add_uninitialized_single_output(output_value_2.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({0, 1, 2}, params, context);
-
- EXPECT_EQ(output_value_1[0].size(), 10);
- EXPECT_EQ(output_value_1[1].size(), 7);
- EXPECT_EQ(output_value_1[2].size(), 6);
-
- EXPECT_EQ(output_value_2[0], 45);
- EXPECT_EQ(output_value_2[1], 16);
- EXPECT_EQ(output_value_2[2], 15);
- }
-}
-
-} // namespace
-} // namespace blender::fn::tests
diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc
index 3d73e020eb2..91c72a51dd6 100644
--- a/source/blender/functions/tests/FN_multi_function_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_test.cc
@@ -4,6 +4,7 @@
#include "FN_multi_function.hh"
#include "FN_multi_function_builder.hh"
+#include "FN_multi_function_test_common.hh"
namespace blender::fn::tests {
namespace {
@@ -59,33 +60,6 @@ TEST(multi_function, AddFunction)
EXPECT_EQ(output[2], 36);
}
-class AddPrefixFunction : public MultiFunction {
- public:
- AddPrefixFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Add Prefix"};
- signature.single_input<std::string>("Prefix");
- signature.single_mutable<std::string>("Strings");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix");
- MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings");
-
- for (int64_t i : mask) {
- strings[i] = prefixes[i] + strings[i];
- }
- }
-};
-
TEST(multi_function, AddPrefixFunction)
{
AddPrefixFunction fn;
@@ -113,43 +87,13 @@ TEST(multi_function, AddPrefixFunction)
EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation");
}
-class CreateRangeFunction : public MultiFunction {
- public:
- CreateRangeFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Create Range"};
- signature.single_input<uint>("Size");
- signature.vector_output<uint>("Range");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<uint> &sizes = params.readonly_single_input<uint>(0, "Size");
- GVectorArray &ranges = params.vector_output(1, "Range");
-
- for (int64_t i : mask) {
- uint size = sizes[i];
- for (uint j : IndexRange(size)) {
- ranges.append(i, &j);
- }
- }
- }
-};
-
TEST(multi_function, CreateRangeFunction)
{
CreateRangeFunction fn;
- GVectorArray ranges(CPPType::get<uint>(), 5);
- GVectorArray_TypedMutableRef<uint> ranges_ref{ranges};
- Array<uint> sizes = {3, 0, 6, 1, 4};
+ GVectorArray ranges(CPPType::get<int>(), 5);
+ GVectorArray_TypedMutableRef<int> ranges_ref{ranges};
+ Array<int> sizes = {3, 0, 6, 1, 4};
MFParamsBuilder params(fn, ranges.size());
params.add_readonly_single_input(sizes.as_span());
@@ -172,34 +116,6 @@ TEST(multi_function, CreateRangeFunction)
EXPECT_EQ(ranges_ref[2][1], 1);
}
-class GenericAppendFunction : public MultiFunction {
- private:
- MFSignature signature_;
-
- public:
- GenericAppendFunction(const CPPType &type)
- {
- MFSignatureBuilder signature{"Append"};
- signature.vector_mutable("Vector", type);
- signature.single_input("Value", type);
- signature_ = signature.build();
- this->set_signature(&signature_);
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray &vectors = params.vector_mutable(0, "Vector");
- const GVArray &values = params.readonly_single_input(1, "Value");
-
- for (int64_t i : mask) {
- BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer);
- values.get(i, buffer);
- vectors.append(i, buffer);
- values.type().destruct(buffer);
- }
- }
-};
-
TEST(multi_function, GenericAppendFunction)
{
GenericAppendFunction fn(CPPType::get<int32_t>());
diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh
new file mode 100644
index 00000000000..51c8fac8a96
--- /dev/null
+++ b/source/blender/functions/tests/FN_multi_function_test_common.hh
@@ -0,0 +1,174 @@
+/* Apache License, Version 2.0 */
+
+#include "FN_multi_function.hh"
+
+namespace blender::fn::tests {
+
+class AddPrefixFunction : public MultiFunction {
+ public:
+ AddPrefixFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Add Prefix"};
+ signature.single_input<std::string>("Prefix");
+ signature.single_mutable<std::string>("Strings");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix");
+ MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings");
+
+ for (int64_t i : mask) {
+ strings[i] = prefixes[i] + strings[i];
+ }
+ }
+};
+
+class CreateRangeFunction : public MultiFunction {
+ public:
+ CreateRangeFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Create Range"};
+ signature.single_input<int>("Size");
+ signature.vector_output<int>("Range");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size");
+ GVectorArray &ranges = params.vector_output(1, "Range");
+
+ for (int64_t i : mask) {
+ int size = sizes[i];
+ for (int j : IndexRange(size)) {
+ ranges.append(i, &j);
+ }
+ }
+ }
+};
+
+class GenericAppendFunction : public MultiFunction {
+ private:
+ MFSignature signature_;
+
+ public:
+ GenericAppendFunction(const CPPType &type)
+ {
+ MFSignatureBuilder signature{"Append"};
+ signature.vector_mutable("Vector", type);
+ signature.single_input("Value", type);
+ signature_ = signature.build();
+ this->set_signature(&signature_);
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray &vectors = params.vector_mutable(0, "Vector");
+ const GVArray &values = params.readonly_single_input(1, "Value");
+
+ for (int64_t i : mask) {
+ BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer);
+ values.get(i, buffer);
+ vectors.append(i, buffer);
+ values.type().destruct(buffer);
+ }
+ }
+};
+
+class ConcatVectorsFunction : public MultiFunction {
+ public:
+ ConcatVectorsFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Concat Vectors"};
+ signature.vector_mutable<int>("A");
+ signature.vector_input<int>("B");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray &a = params.vector_mutable(0);
+ const GVVectorArray &b = params.readonly_vector_input(1);
+ a.extend(mask, b);
+ }
+};
+
+class AppendFunction : public MultiFunction {
+ public:
+ AppendFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Append"};
+ signature.vector_mutable<int>("Vector");
+ signature.single_input<int>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0);
+ const VArray<int> &values = params.readonly_single_input<int>(1);
+
+ for (int64_t i : mask) {
+ vectors.append(i, values[i]);
+ }
+ }
+};
+
+class SumVectorFunction : public MultiFunction {
+ public:
+ SumVectorFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Sum Vectors"};
+ signature.vector_input<int>("Vector");
+ signature.single_output<int>("Sum");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0);
+ MutableSpan<int> sums = params.uninitialized_single_output<int>(1);
+
+ for (int64_t i : mask) {
+ int sum = 0;
+ for (int j : IndexRange(vectors.get_vector_size(i))) {
+ sum += vectors.get_vector_element(i, j);
+ }
+ sums[i] = sum;
+ }
+ }
+};
+
+} // namespace blender::fn::tests