diff options
55 files changed, 3070 insertions, 3955 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index caa7ab6de0a..c4393246926 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -111,8 +111,7 @@ typedef struct bNodeSocketTemplate { #ifdef __cplusplus namespace blender { namespace nodes { -class SocketMFNetworkBuilder; -class NodeMFNetworkBuilder; +class NodeMultiFunctionBuilder; class GeoNodeExecParams; } // namespace nodes namespace fn { @@ -121,18 +120,16 @@ class MFDataType; } // namespace fn } // namespace blender -using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder); +using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder); using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); using SocketGetGeometryNodesCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); -using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder); #else -typedef void *NodeExpandInMFNetworkFunction; -typedef void *SocketExpandInMFNetworkFunction; +typedef void *NodeMultiFunctionBuildFunction; typedef void *NodeGeometryExecFunction; typedef void *SocketGetCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPTypeFunction; @@ -196,8 +193,6 @@ typedef struct bNodeSocketType { /* Callback to free the socket type. */ void (*free_self)(struct bNodeSocketType *stype); - /* Expands the socket into a multi-function node that outputs the socket value. */ - SocketExpandInMFNetworkFunction expand_in_mf_network; /* Return the CPPType of this socket. */ SocketGetCPPTypeFunction get_base_cpp_type; /* Get the value of this socket in a generic way. */ @@ -332,8 +327,8 @@ typedef struct bNodeType { /* gpu */ NodeGPUExecFunction gpu_fn; - /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */ - NodeExpandInMFNetworkFunction expand_in_mf_network; + /* Build a multi-function for this node. */ + NodeMultiFunctionBuildFunction build_multi_function; /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 5aac29c19a7..4c97ccdf8b1 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -49,14 +49,10 @@ #include "BKE_simulation.h" #include "NOD_geometry.h" -#include "NOD_node_tree_multi_function.hh" #include "BLI_map.hh" #include "BLT_translation.h" -#include "FN_multi_function_network_evaluation.hh" -#include "FN_multi_function_network_optimization.hh" - #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index f04c0e9c80a..3dc56f13c4e 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -58,11 +58,7 @@ class IndexMask { */ IndexMask(Span<int64_t> indices) : indices_(indices) { -#ifdef DEBUG - for (int64_t i = 1; i < indices.size(); i++) { - BLI_assert(indices[i - 1] < indices[i]); - } -#endif + BLI_assert(IndexMask::indices_are_valid_index_mask(indices)); } /** @@ -94,6 +90,21 @@ class IndexMask { { } + static bool indices_are_valid_index_mask(Span<int64_t> indices) + { + if (!indices.is_empty()) { + if (indices.first() < 0) { + return false; + } + } + for (int64_t i = 1; i < indices.size(); i++) { + if (indices[i - 1] >= indices[i]) { + return false; + } + } + return true; + } + operator Span<int64_t>() const { return indices_; @@ -204,6 +215,11 @@ class IndexMask { { return indices_.size(); } + + bool is_empty() const + { + return indices_.is_empty(); + } }; } // namespace blender diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 809294ad274..a44086cfec1 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -33,9 +33,9 @@ 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 + intern/multi_function_procedure.cc + intern/multi_function_procedure_builder.cc + intern/multi_function_procedure_executor.cc FN_cpp_type.hh FN_cpp_type_make.hh @@ -49,11 +49,11 @@ 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_procedure.hh + FN_multi_function_procedure_builder.hh + FN_multi_function_procedure_executor.hh FN_multi_function_signature.hh ) @@ -68,7 +68,7 @@ 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_procedure_test.cc tests/FN_multi_function_test.cc ) set(TEST_LIB diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh index eeba0c9dba2..179e85671f8 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/functions/FN_generic_vector_array.hh @@ -82,6 +82,8 @@ class GVectorArray : NonCopyable, NonMovable { void extend(IndexMask mask, const GVVectorArray &values); void extend(IndexMask mask, const GVectorArray &values); + void clear(IndexMask mask); + GMutableSpan operator[](int64_t index); GSpan operator[](int64_t index) const; 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_params.hh b/source/blender/functions/FN_multi_function_params.hh index e292d11def7..a480287d578 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -54,6 +54,11 @@ class MFParamsBuilder { MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size); + template<typename T> void add_readonly_single_input_value(T value, StringRef expected_name = "") + { + T *value_ptr = &scope_.add_value<T>(std::move(value), __func__); + this->add_readonly_single_input(value_ptr, expected_name); + } template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>( @@ -83,6 +88,12 @@ class MFParamsBuilder { this->add_readonly_vector_input( scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name); } + void add_readonly_vector_input(const GSpan single_vector, StringRef expected_name = "") + { + this->add_readonly_vector_input( + scope_.construct<GVVectorArray_For_SingleGSpan>(__func__, single_vector, min_array_size_), + expected_name); + } void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") { this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name); diff --git a/source/blender/functions/FN_multi_function_procedure.hh b/source/blender/functions/FN_multi_function_procedure.hh new file mode 100644 index 00000000000..b9540540992 --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure.hh @@ -0,0 +1,360 @@ +/* + * 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.hh" + +namespace blender::fn { + +class MFVariable; +class MFInstruction; +class MFCallInstruction; +class MFBranchInstruction; +class MFDestructInstruction; +class MFDummyInstruction; +class MFProcedure; + +enum class MFInstructionType { + Call, + Branch, + Destruct, + Dummy, +}; + +class MFVariable : NonCopyable, NonMovable { + private: + MFDataType data_type_; + Vector<MFInstruction *> users_; + std::string name_; + int id_; + + friend MFProcedure; + friend MFCallInstruction; + friend MFBranchInstruction; + friend MFDestructInstruction; + + public: + MFDataType data_type() const; + Span<MFInstruction *> users(); + + StringRefNull name() const; + void set_name(std::string name); + + int id() const; +}; + +class MFInstruction : NonCopyable, NonMovable { + protected: + MFInstructionType type_; + Vector<MFInstruction *> prev_; + + friend MFProcedure; + friend MFCallInstruction; + friend MFBranchInstruction; + friend MFDestructInstruction; + friend MFDummyInstruction; + + public: + MFInstructionType type() const; + Span<MFInstruction *> prev(); +}; + +class MFCallInstruction : public MFInstruction { + private: + const MultiFunction *fn_ = nullptr; + MFInstruction *next_ = nullptr; + MutableSpan<MFVariable *> params_; + + friend MFProcedure; + + public: + const MultiFunction &fn() const; + + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); + + void set_param_variable(int param_index, MFVariable *variable); + void set_params(Span<MFVariable *> variables); + Span<MFVariable *> params(); + Span<const MFVariable *> params() const; +}; + +class MFBranchInstruction : public MFInstruction { + private: + MFVariable *condition_ = nullptr; + MFInstruction *branch_true_ = nullptr; + MFInstruction *branch_false_ = nullptr; + + public: + MFVariable *condition(); + const MFVariable *condition() const; + void set_condition(MFVariable *variable); + + MFInstruction *branch_true(); + const MFInstruction *branch_true() const; + void set_branch_true(MFInstruction *instruction); + + MFInstruction *branch_false(); + const MFInstruction *branch_false() const; + void set_branch_false(MFInstruction *instruction); +}; + +class MFDestructInstruction : public MFInstruction { + private: + MFVariable *variable_ = nullptr; + MFInstruction *next_ = nullptr; + + public: + MFVariable *variable(); + const MFVariable *variable() const; + void set_variable(MFVariable *variable); + + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); +}; + +class MFDummyInstruction : public MFInstruction { + private: + MFInstruction *next_ = nullptr; + + public: + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); +}; + +class MFProcedure : NonCopyable, NonMovable { + private: + LinearAllocator<> allocator_; + Vector<MFCallInstruction *> call_instructions_; + Vector<MFBranchInstruction *> branch_instructions_; + Vector<MFDestructInstruction *> destruct_instructions_; + Vector<MFDummyInstruction *> dummy_instructions_; + Vector<MFVariable *> variables_; + Vector<std::pair<MFParamType::InterfaceType, MFVariable *>> params_; + MFInstruction *entry_ = nullptr; + + public: + MFProcedure() = default; + ~MFProcedure(); + + MFVariable &new_variable(MFDataType data_type, std::string name = ""); + MFCallInstruction &new_call_instruction(const MultiFunction &fn); + MFBranchInstruction &new_branch_instruction(); + MFDestructInstruction &new_destruct_instruction(); + MFDummyInstruction &new_dummy_instruction(); + + void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable); + + Span<std::pair<MFParamType::InterfaceType, const MFVariable *>> params() const; + + MFInstruction *entry(); + const MFInstruction *entry() const; + void set_entry(MFInstruction &entry); + + Span<MFVariable *> variables(); + Span<const MFVariable *> variables() const; + + void assert_valid() const; + + std::string to_dot() const; +}; + +namespace multi_function_procedure_types { +using MFVariable = fn::MFVariable; +using MFInstruction = fn::MFInstruction; +using MFCallInstruction = fn::MFCallInstruction; +using MFBranchInstruction = fn::MFBranchInstruction; +using MFDestructInstruction = fn::MFDestructInstruction; +using MFProcedure = fn::MFProcedure; +} // namespace multi_function_procedure_types + +/* -------------------------------------------------------------------- + * MFVariable inline methods. + */ + +inline MFDataType MFVariable::data_type() const +{ + return data_type_; +} + +inline Span<MFInstruction *> MFVariable::users() +{ + return users_; +} + +inline StringRefNull MFVariable::name() const +{ + return name_; +} + +inline int MFVariable::id() const +{ + return id_; +} + +/* -------------------------------------------------------------------- + * MFInstruction inline methods. + */ + +inline MFInstructionType MFInstruction::type() const +{ + return type_; +} + +inline Span<MFInstruction *> MFInstruction::prev() +{ + return prev_; +} + +/* -------------------------------------------------------------------- + * MFCallInstruction inline methods. + */ + +inline const MultiFunction &MFCallInstruction::fn() const +{ + return *fn_; +} + +inline MFInstruction *MFCallInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFCallInstruction::next() const +{ + return next_; +} + +inline Span<MFVariable *> MFCallInstruction::params() +{ + return params_; +} + +inline Span<const MFVariable *> MFCallInstruction::params() const +{ + return params_; +} + +/* -------------------------------------------------------------------- + * MFBranchInstruction inline methods. + */ + +inline MFVariable *MFBranchInstruction::condition() +{ + return condition_; +} + +inline const MFVariable *MFBranchInstruction::condition() const +{ + return condition_; +} + +inline MFInstruction *MFBranchInstruction::branch_true() +{ + return branch_true_; +} + +inline const MFInstruction *MFBranchInstruction::branch_true() const +{ + return branch_true_; +} + +inline MFInstruction *MFBranchInstruction::branch_false() +{ + return branch_false_; +} + +inline const MFInstruction *MFBranchInstruction::branch_false() const +{ + return branch_false_; +} + +/* -------------------------------------------------------------------- + * MFDestructInstruction inline methods. + */ + +inline MFVariable *MFDestructInstruction::variable() +{ + return variable_; +} + +inline const MFVariable *MFDestructInstruction::variable() const +{ + return variable_; +} + +inline MFInstruction *MFDestructInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFDestructInstruction::next() const +{ + return next_; +} + +/* -------------------------------------------------------------------- + * MFDummyInstruction inline methods. + */ + +inline MFInstruction *MFDummyInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFDummyInstruction::next() const +{ + return next_; +} + +/* -------------------------------------------------------------------- + * MFProcedure inline methods. + */ + +inline Span<std::pair<MFParamType::InterfaceType, const MFVariable *>> MFProcedure::params() const +{ + return params_.as_span().cast<std::pair<MFParamType::InterfaceType, const MFVariable *>>(); +} + +inline MFInstruction *MFProcedure::entry() +{ + return entry_; +} + +inline const MFInstruction *MFProcedure::entry() const +{ + return entry_; +} + +inline Span<MFVariable *> MFProcedure::variables() +{ + return variables_; +} + +inline Span<const MFVariable *> MFProcedure::variables() const +{ + return variables_; +} + +} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_procedure_builder.hh b/source/blender/functions/FN_multi_function_procedure_builder.hh new file mode 100644 index 00000000000..5f968f2de31 --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure_builder.hh @@ -0,0 +1,249 @@ +/* + * 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_procedure.hh" + +namespace blender::fn { + +class MFInstructionCursor { + private: + MFInstruction *instruction_ = nullptr; + /* Only used when it is a branch instruction. */ + bool branch_output_ = false; + /* Only used when instruction is null. */ + bool is_entry_ = false; + + public: + MFInstructionCursor() = default; + + MFInstructionCursor(MFCallInstruction &instruction); + MFInstructionCursor(MFDestructInstruction &instruction); + MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output); + MFInstructionCursor(MFDummyInstruction &instruction); + + static MFInstructionCursor Entry(); + + void insert(MFProcedure &procedure, MFInstruction *new_instruction); +}; + +class MFProcedureBuilder { + private: + MFProcedure *procedure_ = nullptr; + Vector<MFInstructionCursor> cursors_; + + public: + struct Branch; + struct Loop; + + MFProcedureBuilder(MFProcedure &procedure, + MFInstructionCursor initial_cursor = MFInstructionCursor::Entry()); + + MFProcedureBuilder(Span<MFProcedureBuilder *> builders); + + MFProcedureBuilder(Branch &branch); + + void set_cursor(const MFInstructionCursor &cursor); + void set_cursor(Span<MFInstructionCursor> cursors); + void set_cursor(Span<MFProcedureBuilder *> builders); + void set_cursor_after_branch(Branch &branch); + void set_cursor_after_loop(Loop &loop); + + void add_destruct(MFVariable &variable); + void add_destruct(Span<MFVariable *> variables); + + Branch add_branch(MFVariable &condition); + + Loop add_loop(); + void add_loop_continue(Loop &loop); + void add_loop_break(Loop &loop); + + MFCallInstruction &add_call(const MultiFunction &fn); + MFCallInstruction &add_call(const MultiFunction &fn, Span<MFVariable *> variables); + + Vector<MFVariable *> add_call_with_new_variables( + const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables = {}); + + template<int OutputN> + std::array<MFVariable *, OutputN> add_call_with_new_variables( + const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables = {}); + + void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable); + MFVariable &add_parameter(MFParamType param_type, std::string name = ""); + + MFVariable &add_input_parameter(MFDataType data_type, std::string name = ""); + template<typename T> MFVariable &add_single_input_parameter(std::string name = ""); + template<typename T> MFVariable &add_single_mutable_parameter(std::string name = ""); + + void add_output_parameter(MFVariable &variable); + + private: + void link_to_cursors(MFInstruction *instruction); +}; + +struct MFProcedureBuilder::Branch { + MFProcedureBuilder branch_true; + MFProcedureBuilder branch_false; +}; + +struct MFProcedureBuilder::Loop { + MFInstruction *begin = nullptr; + MFDummyInstruction *end = nullptr; +}; + +/* -------------------------------------------------------------------- + * MFInstructionCursor inline methods. + */ + +inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction) + : instruction_(&instruction) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction) + : instruction_(&instruction) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction, + bool branch_output) + : instruction_(&instruction), branch_output_(branch_output) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction) + : instruction_(&instruction) +{ +} + +inline MFInstructionCursor MFInstructionCursor::Entry() +{ + MFInstructionCursor cursor; + cursor.is_entry_ = true; + return cursor; +} + +/* -------------------------------------------------------------------- + * MFProcedureBuilder inline methods. + */ + +inline MFProcedureBuilder::MFProcedureBuilder(Branch &branch) + : MFProcedureBuilder(*branch.branch_true.procedure_) +{ + this->set_cursor_after_branch(branch); +} + +inline MFProcedureBuilder::MFProcedureBuilder(MFProcedure &procedure, + MFInstructionCursor initial_cursor) + : procedure_(&procedure), cursors_({initial_cursor}) +{ +} + +inline MFProcedureBuilder::MFProcedureBuilder(Span<MFProcedureBuilder *> builders) + : MFProcedureBuilder(*builders[0]->procedure_) +{ + this->set_cursor(builders); +} + +inline void MFProcedureBuilder::set_cursor(const MFInstructionCursor &cursor) +{ + cursors_ = {cursor}; +} + +inline void MFProcedureBuilder::set_cursor(Span<MFInstructionCursor> cursors) +{ + cursors_ = cursors; +} + +inline void MFProcedureBuilder::set_cursor_after_branch(Branch &branch) +{ + this->set_cursor({&branch.branch_false, &branch.branch_true}); +} + +inline void MFProcedureBuilder::set_cursor_after_loop(Loop &loop) +{ + this->set_cursor(MFInstructionCursor{*loop.end}); +} + +inline void MFProcedureBuilder::set_cursor(Span<MFProcedureBuilder *> builders) +{ + cursors_.clear(); + for (MFProcedureBuilder *builder : builders) { + cursors_.extend(builder->cursors_); + } +} + +template<int OutputN> +inline std::array<MFVariable *, OutputN> MFProcedureBuilder::add_call_with_new_variables( + const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables) +{ + Vector<MFVariable *> output_variables = this->add_call_with_new_variables( + fn, input_and_mutable_variables); + BLI_assert(output_variables.size() == OutputN); + + std::array<MFVariable *, OutputN> output_array; + initialized_copy_n(output_variables.data(), OutputN, output_array.data()); + return output_array; +} + +inline void MFProcedureBuilder::add_parameter(MFParamType::InterfaceType interface_type, + MFVariable &variable) +{ + procedure_->add_parameter(interface_type, variable); +} + +inline MFVariable &MFProcedureBuilder::add_parameter(MFParamType param_type, std::string name) +{ + MFVariable &variable = procedure_->new_variable(param_type.data_type(), std::move(name)); + this->add_parameter(param_type.interface_type(), variable); + return variable; +} + +inline MFVariable &MFProcedureBuilder::add_input_parameter(MFDataType data_type, std::string name) +{ + return this->add_parameter(MFParamType(MFParamType::Input, data_type), std::move(name)); +} + +template<typename T> +inline MFVariable &MFProcedureBuilder::add_single_input_parameter(std::string name) +{ + return this->add_parameter(MFParamType::ForSingleInput(CPPType::get<T>()), std::move(name)); +} + +template<typename T> +inline MFVariable &MFProcedureBuilder::add_single_mutable_parameter(std::string name) +{ + return this->add_parameter(MFParamType::ForMutableSingle(CPPType::get<T>()), std::move(name)); +} + +inline void MFProcedureBuilder::add_output_parameter(MFVariable &variable) +{ + this->add_parameter(MFParamType::Output, variable); +} + +inline void MFProcedureBuilder::link_to_cursors(MFInstruction *instruction) +{ + for (MFInstructionCursor &cursor : cursors_) { + cursor.insert(*procedure_, instruction); + } +} + +} // namespace blender::fn diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/functions/FN_multi_function_procedure_executor.hh index 2be78f929db..e352be41d9e 100644 --- a/source/blender/nodes/NOD_type_callbacks.hh +++ b/source/blender/functions/FN_multi_function_procedure_executor.hh @@ -16,19 +16,23 @@ #pragma once -#include <optional> +/** \file + * \ingroup fn + */ -#include "BKE_node.h" +#include "FN_multi_function_procedure.hh" -#include "FN_multi_function_data_type.hh" +namespace blender::fn { -namespace blender::nodes { +class MFProcedureExecutor : public MultiFunction { + private: + MFSignature signature_; + const MFProcedure &procedure_; -using fn::CPPType; -using fn::MFDataType; + public: + MFProcedureExecutor(std::string name, const MFProcedure &procedure); -std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype); -bool socket_is_mf_data_socket(const bNodeSocketType &stype); -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder); + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; -} // namespace blender::nodes +} // namespace blender::fn 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 ¶m_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/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc index 9556d24218e..ec95a283919 100644 --- a/source/blender/functions/intern/generic_vector_array.cc +++ b/source/blender/functions/intern/generic_vector_array.cc @@ -78,6 +78,15 @@ void GVectorArray::extend(IndexMask mask, const GVectorArray &values) this->extend(mask, virtual_values); } +void GVectorArray::clear(IndexMask mask) +{ + for (const int64_t i : mask) { + Item &item = items_[i]; + type_.destruct_n(item.start, item.length); + item.length = 0; + } +} + GMutableSpan GVectorArray::operator[](const int64_t index) { Item &item = items_[index]; 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 ¶ms, - 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 ¶ms, - 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/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc new file mode 100644 index 00000000000..d9bf611fa34 --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -0,0 +1,326 @@ +/* + * 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 "FN_multi_function_procedure.hh" + +#include "BLI_dot_export.hh" + +namespace blender::fn { + +void MFVariable::set_name(std::string name) +{ + name_ = std::move(name); +} + +void MFCallInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + next_ = instruction; +} + +void MFCallInstruction::set_param_variable(int param_index, MFVariable *variable) +{ + if (params_[param_index] != nullptr) { + params_[param_index]->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + BLI_assert(fn_->param_type(param_index).data_type() == variable->data_type()); + variable->users_.append(this); + } + params_[param_index] = variable; +} + +void MFCallInstruction::set_params(Span<MFVariable *> variables) +{ + BLI_assert(variables.size() == params_.size()); + for (const int i : variables.index_range()) { + this->set_param_variable(i, variables[i]); + } +} + +void MFBranchInstruction::set_condition(MFVariable *variable) +{ + if (condition_ != nullptr) { + condition_->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + variable->users_.append(this); + } + condition_ = variable; +} + +void MFBranchInstruction::set_branch_true(MFInstruction *instruction) +{ + if (branch_true_ != nullptr) { + branch_true_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + branch_true_ = instruction; +} + +void MFBranchInstruction::set_branch_false(MFInstruction *instruction) +{ + if (branch_false_ != nullptr) { + branch_false_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + branch_false_ = instruction; +} + +void MFDestructInstruction::set_variable(MFVariable *variable) +{ + if (variable_ != nullptr) { + variable_->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + variable->users_.append(this); + } + variable_ = variable; +} + +void MFDestructInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + next_ = instruction; +} + +void MFDummyInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + next_ = instruction; +} + +MFVariable &MFProcedure::new_variable(MFDataType data_type, std::string name) +{ + MFVariable &variable = *allocator_.construct<MFVariable>().release(); + variable.name_ = std::move(name); + variable.data_type_ = data_type; + variable.id_ = variables_.size(); + variables_.append(&variable); + return variable; +} + +MFCallInstruction &MFProcedure::new_call_instruction(const MultiFunction &fn) +{ + MFCallInstruction &instruction = *allocator_.construct<MFCallInstruction>().release(); + instruction.type_ = MFInstructionType::Call; + instruction.fn_ = &fn; + instruction.params_ = allocator_.allocate_array<MFVariable *>(fn.param_amount()); + instruction.params_.fill(nullptr); + call_instructions_.append(&instruction); + return instruction; +} + +MFBranchInstruction &MFProcedure::new_branch_instruction() +{ + MFBranchInstruction &instruction = *allocator_.construct<MFBranchInstruction>().release(); + instruction.type_ = MFInstructionType::Branch; + branch_instructions_.append(&instruction); + return instruction; +} + +MFDestructInstruction &MFProcedure::new_destruct_instruction() +{ + MFDestructInstruction &instruction = *allocator_.construct<MFDestructInstruction>().release(); + instruction.type_ = MFInstructionType::Destruct; + destruct_instructions_.append(&instruction); + return instruction; +} + +MFDummyInstruction &MFProcedure::new_dummy_instruction() +{ + MFDummyInstruction &instruction = *allocator_.construct<MFDummyInstruction>().release(); + instruction.type_ = MFInstructionType::Dummy; + dummy_instructions_.append(&instruction); + return instruction; +} + +void MFProcedure::add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable) +{ + params_.append({interface_type, &variable}); +} + +void MFProcedure::set_entry(MFInstruction &entry) +{ + entry_ = &entry; +} + +void MFProcedure::assert_valid() const +{ + /** + * - Non parameter variables are destructed. + * - At every instruction, every variable is either initialized or uninitialized. + * - Input and mutable parameters of call instructions are initialized. + * - Condition of branch instruction is initialized. + * - Output parameters of call instructions are not initialized. + * - Input parameters are never destructed. + * - Mutable and output parameteres are initialized on every exit. + * - No aliasing issues in call instructions (can happen when variable is used more than once). + */ +} + +MFProcedure::~MFProcedure() +{ + for (MFCallInstruction *instruction : call_instructions_) { + instruction->~MFCallInstruction(); + } + for (MFBranchInstruction *instruction : branch_instructions_) { + instruction->~MFBranchInstruction(); + } + for (MFDestructInstruction *instruction : destruct_instructions_) { + instruction->~MFDestructInstruction(); + } + for (MFDummyInstruction *instruction : dummy_instructions_) { + instruction->~MFDummyInstruction(); + } + for (MFVariable *variable : variables_) { + variable->~MFVariable(); + } +} + +static std::string optional_variable_to_string(const MFVariable *variable) +{ + if (variable == nullptr) { + return "<null>"; + } + std::stringstream ss; + ss << variable->name() << "$" << variable->id(); + return ss.str(); +} + +std::string MFProcedure::to_dot() const +{ + dot::DirectedGraph digraph; + Map<MFInstruction *, dot::Node *> dot_nodes; + + for (MFCallInstruction *instruction : call_instructions_) { + std::stringstream ss; + const MultiFunction &fn = instruction->fn(); + ss << fn.name(); + ss << "("; + for (const int param_index : fn.param_indices()) { + MFParamType param_type = fn.param_type(param_index); + switch (param_type.interface_type()) { + case MFParamType::Input: { + ss << "in: "; + break; + } + case MFParamType::Output: { + ss << "out: "; + break; + } + case MFParamType::Mutable: { + ss << "mut: "; + break; + } + } + MFVariable *variable = instruction->params()[param_index]; + ss << optional_variable_to_string(variable); + if (param_index < fn.param_amount() - 1) { + ss << ", "; + } + } + ss << ")"; + dot::Node &dot_node = digraph.new_node(ss.str()); + dot_node.set_shape(dot::Attr_shape::Rectangle); + dot_nodes.add_new(instruction, &dot_node); + } + for (MFBranchInstruction *instruction : branch_instructions_) { + MFVariable *variable = instruction->condition(); + std::stringstream ss; + ss << "Branch: " << optional_variable_to_string(variable); + dot::Node &dot_node = digraph.new_node(ss.str()); + dot_node.set_shape(dot::Attr_shape::Rectangle); + dot_nodes.add_new(instruction, &dot_node); + } + for (MFDestructInstruction *instruction : destruct_instructions_) { + MFVariable *variable = instruction->variable(); + std::stringstream ss; + ss << "Destruct: " << optional_variable_to_string(variable); + dot::Node &dot_node = digraph.new_node(ss.str()); + dot_node.set_shape(dot::Attr_shape::Rectangle); + dot_nodes.add_new(instruction, &dot_node); + } + for (MFDummyInstruction *instruction : dummy_instructions_) { + dot::Node &dot_node = digraph.new_node("Dummy"); + dot_node.set_shape(dot::Attr_shape::Rectangle); + dot_nodes.add_new(instruction, &dot_node); + } + + auto create_end_node = [&]() -> dot::Node & { + dot::Node &node = digraph.new_node(""); + node.set_shape(dot::Attr_shape::Circle); + return node; + }; + + auto add_edge_to_instruction_or_end = [&](dot::Node &dot_from, MFInstruction *to) { + if (to == nullptr) { + dot::Node &dot_end_node = create_end_node(); + digraph.new_edge(dot_from, dot_end_node); + } + else { + dot::Node &dot_to = *dot_nodes.lookup(to); + digraph.new_edge(dot_from, dot_to); + } + }; + + for (MFCallInstruction *instruction : call_instructions_) { + dot::Node &dot_node = *dot_nodes.lookup(instruction); + add_edge_to_instruction_or_end(dot_node, instruction->next()); + } + + for (MFBranchInstruction *instruction : branch_instructions_) { + dot::Node &dot_node = *dot_nodes.lookup(instruction); + add_edge_to_instruction_or_end(dot_node, instruction->branch_true()); + add_edge_to_instruction_or_end(dot_node, instruction->branch_false()); + } + + for (MFDestructInstruction *instruction : destruct_instructions_) { + dot::Node &dot_node = *dot_nodes.lookup(instruction); + add_edge_to_instruction_or_end(dot_node, instruction->next()); + } + + for (MFDummyInstruction *instruction : dummy_instructions_) { + dot::Node &dot_node = *dot_nodes.lookup(instruction); + add_edge_to_instruction_or_end(dot_node, instruction->next()); + } + + dot::Node &dot_entry = digraph.new_node("Entry"); + add_edge_to_instruction_or_end(dot_entry, entry_); + + return digraph.to_dot_string(); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure_builder.cc b/source/blender/functions/intern/multi_function_procedure_builder.cc new file mode 100644 index 00000000000..b27a25d63cf --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure_builder.cc @@ -0,0 +1,161 @@ +/* + * 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 "FN_multi_function_procedure_builder.hh" + +namespace blender::fn { + +void MFInstructionCursor::insert(MFProcedure &procedure, MFInstruction *new_instruction) +{ + if (instruction_ == nullptr) { + if (is_entry_) { + procedure.set_entry(*new_instruction); + } + else { + /* The cursors points at nothing, nothing to do. */ + } + } + else { + switch (instruction_->type()) { + case MFInstructionType::Call: { + static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction); + break; + } + case MFInstructionType::Branch: { + MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>( + instruction_); + if (branch_output_) { + branch_instruction.set_branch_true(new_instruction); + } + else { + branch_instruction.set_branch_false(new_instruction); + } + break; + } + case MFInstructionType::Destruct: { + static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction); + break; + } + case MFInstructionType::Dummy: { + static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction); + break; + } + } + } +} + +void MFProcedureBuilder::add_destruct(MFVariable &variable) +{ + MFDestructInstruction &instruction = procedure_->new_destruct_instruction(); + instruction.set_variable(&variable); + this->link_to_cursors(&instruction); + cursors_ = {MFInstructionCursor{instruction}}; +} + +void MFProcedureBuilder::add_destruct(Span<MFVariable *> variables) +{ + for (MFVariable *variable : variables) { + this->add_destruct(*variable); + } +} + +MFCallInstruction &MFProcedureBuilder::add_call(const MultiFunction &fn) +{ + MFCallInstruction &instruction = procedure_->new_call_instruction(fn); + this->link_to_cursors(&instruction); + cursors_ = {MFInstructionCursor{instruction}}; + return instruction; +} + +MFCallInstruction &MFProcedureBuilder::add_call(const MultiFunction &fn, + Span<MFVariable *> variables) +{ + MFCallInstruction &instruction = this->add_call(fn); + instruction.set_params(variables); + return instruction; +} + +Vector<MFVariable *> MFProcedureBuilder::add_call_with_new_variables( + const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables) +{ + Vector<MFVariable *> output_variables; + MFCallInstruction &instruction = this->add_call(fn); + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + switch (param_type.interface_type()) { + case MFParamType::Input: + case MFParamType::Mutable: { + MFVariable *variable = input_and_mutable_variables.first(); + instruction.set_param_variable(param_index, variable); + input_and_mutable_variables = input_and_mutable_variables.drop_front(1); + break; + } + case MFParamType::Output: { + MFVariable &variable = procedure_->new_variable(param_type.data_type()); + instruction.set_param_variable(param_index, &variable); + output_variables.append(&variable); + break; + } + } + } + /* All passed in variables should have been dropped in the loop above. */ + BLI_assert(input_and_mutable_variables.is_empty()); + return output_variables; +} + +MFProcedureBuilder::Branch MFProcedureBuilder::add_branch(MFVariable &condition) +{ + MFBranchInstruction &instruction = procedure_->new_branch_instruction(); + instruction.set_condition(&condition); + this->link_to_cursors(&instruction); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); + + Branch branch{*procedure_, *procedure_}; + branch.branch_true.set_cursor(MFInstructionCursor{instruction, true}); + branch.branch_false.set_cursor(MFInstructionCursor{instruction, false}); + return branch; +} + +MFProcedureBuilder::Loop MFProcedureBuilder::add_loop() +{ + MFDummyInstruction &loop_begin = procedure_->new_dummy_instruction(); + MFDummyInstruction &loop_end = procedure_->new_dummy_instruction(); + this->link_to_cursors(&loop_begin); + cursors_ = {MFInstructionCursor{loop_begin}}; + + Loop loop; + loop.begin = &loop_begin; + loop.end = &loop_end; + + return loop; +} + +void MFProcedureBuilder::add_loop_continue(Loop &loop) +{ + this->link_to_cursors(loop.begin); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); +} + +void MFProcedureBuilder::add_loop_break(Loop &loop) +{ + this->link_to_cursors(loop.end); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc new file mode 100644 index 00000000000..c6906a5ba7c --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure_executor.cc @@ -0,0 +1,1140 @@ +/* + * 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 "FN_multi_function_procedure_executor.hh" + +#include "BLI_stack.hh" + +namespace blender::fn { + +MFProcedureExecutor::MFProcedureExecutor(std::string name, const MFProcedure &procedure) + : procedure_(procedure) +{ + MFSignatureBuilder signature(std::move(name)); + + for (const std::pair<MFParamType::InterfaceType, const MFVariable *> ¶m : + procedure.params()) { + signature.add(param.second->name(), MFParamType(param.first, param.second->data_type())); + } + + signature_ = signature.build(); + this->set_signature(&signature_); +} + +using IndicesSplitVectors = std::array<Vector<int64_t>, 2>; + +namespace { +enum class ValueType { + GVArray = 0, + Span = 1, + GVVectorArray = 2, + GVectorArray = 3, + OneSingle = 4, + OneVector = 5, +}; +constexpr int tot_variable_value_types = 6; +} // namespace + +struct VariableValue { + ValueType type; + + VariableValue(ValueType type) : type(type) + { + } +}; + +/* This variable is the unmodified virtual array from the caller. */ +struct VariableValue_GVArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVArray; + const GVArray &data; + + VariableValue_GVArray(const GVArray &data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has a different value for every index. Some values may be uninitialized. The span + * may be owned by the caller. */ +struct VariableValue_Span : public VariableValue { + static inline constexpr ValueType static_type = ValueType::Span; + void *data; + bool owned; + + VariableValue_Span(void *data, bool owned) : VariableValue(static_type), data(data), owned(owned) + { + } +}; + +/* This variable is the unmodified virtual vector array from the caller. */ +struct VariableValue_GVVectorArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVVectorArray; + const GVVectorArray &data; + + VariableValue_GVVectorArray(const GVVectorArray &data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has a different vector for every index. */ +struct VariableValue_GVectorArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVectorArray; + GVectorArray &data; + bool owned; + + VariableValue_GVectorArray(GVectorArray &data, bool owned) + : VariableValue(static_type), data(data), owned(owned) + { + } +}; + +/* This variable has the same value for every index. */ +struct VariableValue_OneSingle : public VariableValue { + static inline constexpr ValueType static_type = ValueType::OneSingle; + void *data; + bool is_initialized = false; + + VariableValue_OneSingle(void *data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has the same vector for every index. */ +struct VariableValue_OneVector : public VariableValue { + static inline constexpr ValueType static_type = ValueType::OneVector; + GVectorArray &data; + + VariableValue_OneVector(GVectorArray &data) : VariableValue(static_type), data(data) + { + } +}; + +static_assert(std::is_trivially_destructible_v<VariableValue_GVArray>); +static_assert(std::is_trivially_destructible_v<VariableValue_Span>); +static_assert(std::is_trivially_destructible_v<VariableValue_GVVectorArray>); +static_assert(std::is_trivially_destructible_v<VariableValue_GVectorArray>); +static_assert(std::is_trivially_destructible_v<VariableValue_OneSingle>); +static_assert(std::is_trivially_destructible_v<VariableValue_OneVector>); + +class VariableState; + +class ValueAllocator : NonCopyable, NonMovable { + private: + std::array<Stack<VariableValue *>, tot_variable_value_types> values_free_lists_; + + public: + ValueAllocator() = default; + + ~ValueAllocator() + { + for (Stack<VariableValue *> &stack : values_free_lists_) { + while (!stack.is_empty()) { + MEM_freeN(stack.pop()); + } + } + } + + template<typename... Args> VariableState *obtain_variable_state(Args &&...args); + + void release_variable_state(VariableState *state); + + VariableValue_GVArray *obtain_GVArray(const GVArray &varray) + { + return this->obtain<VariableValue_GVArray>(varray); + } + + VariableValue_GVVectorArray *obtain_GVVectorArray(const GVVectorArray &varray) + { + return this->obtain<VariableValue_GVVectorArray>(varray); + } + + VariableValue_Span *obtain_Span_not_owned(void *buffer) + { + return this->obtain<VariableValue_Span>(buffer, false); + } + + VariableValue_Span *obtain_Span(const CPPType &type, int size) + { + void *buffer = MEM_mallocN_aligned(type.size() * size, type.alignment(), __func__); + return this->obtain<VariableValue_Span>(buffer, true); + } + + VariableValue_GVectorArray *obtain_GVectorArray_not_owned(GVectorArray &data) + { + return this->obtain<VariableValue_GVectorArray>(data, false); + } + + VariableValue_GVectorArray *obtain_GVectorArray(const CPPType &type, int size) + { + GVectorArray *vector_array = new GVectorArray(type, size); + return this->obtain<VariableValue_GVectorArray>(*vector_array, true); + } + + VariableValue_OneSingle *obtain_OneSingle(const CPPType &type) + { + void *buffer = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + return this->obtain<VariableValue_OneSingle>(buffer); + } + + VariableValue_OneVector *obtain_OneVector(const CPPType &type) + { + GVectorArray *vector_array = new GVectorArray(type, 1); + return this->obtain<VariableValue_OneVector>(*vector_array); + } + + void release_value(VariableValue *value, const MFDataType &data_type) + { + switch (value->type) { + case ValueType::GVArray: { + break; + } + case ValueType::Span: { + auto *value_typed = static_cast<VariableValue_Span *>(value); + if (value_typed->owned) { + /* Assumes all values in the buffer are uninitialized already. */ + MEM_freeN(value_typed->data); + } + break; + } + case ValueType::GVVectorArray: { + break; + } + case ValueType::GVectorArray: { + auto *value_typed = static_cast<VariableValue_GVectorArray *>(value); + if (value_typed->owned) { + delete &value_typed->data; + } + break; + } + case ValueType::OneSingle: { + auto *value_typed = static_cast<VariableValue_OneSingle *>(value); + if (value_typed->is_initialized) { + const CPPType &type = data_type.single_type(); + type.destruct(value_typed->data); + } + MEM_freeN(value_typed->data); + break; + } + case ValueType::OneVector: { + auto *value_typed = static_cast<VariableValue_OneVector *>(value); + delete &value_typed->data; + break; + } + } + + Stack<VariableValue *> &stack = values_free_lists_[(int)value->type]; + stack.push(value); + } + + private: + template<typename T, typename... Args> T *obtain(Args &&...args) + { + static_assert(std::is_base_of_v<VariableValue, T>); + Stack<VariableValue *> &stack = values_free_lists_[(int)T::static_type]; + if (stack.is_empty()) { + void *buffer = MEM_mallocN(sizeof(T), __func__); + return new (buffer) T(std::forward<Args>(args)...); + } + return new (stack.pop()) T(std::forward<Args>(args)...); + } +}; + +class VariableState : NonCopyable, NonMovable { + private: + VariableValue *value_; + int tot_initialized_; + /* This a non-owning pointer to either span buffer or #GVectorArray or null. */ + void *caller_provided_storage_ = nullptr; + + public: + VariableState(VariableValue &value, int tot_initialized, void *caller_provided_storage = nullptr) + : value_(&value), + tot_initialized_(tot_initialized), + caller_provided_storage_(caller_provided_storage) + { + } + + void destruct_self(ValueAllocator &value_allocator, const MFDataType &data_type) + { + value_allocator.release_value(value_, data_type); + value_allocator.release_variable_state(this); + } + + /* True if this contains only one value for all indices, i.e. the value for all indices is + * the same. */ + bool is_one() const + { + switch (value_->type) { + case ValueType::GVArray: + return this->value_as<VariableValue_GVArray>()->data.is_single(); + case ValueType::Span: + return tot_initialized_ == 0; + case ValueType::GVVectorArray: + return this->value_as<VariableValue_GVVectorArray>()->data.is_single_vector(); + case ValueType::GVectorArray: + return tot_initialized_ == 0; + case ValueType::OneSingle: + return true; + case ValueType::OneVector: + return true; + } + BLI_assert_unreachable(); + return false; + } + + bool is_fully_initialized(const IndexMask full_mask) + { + return tot_initialized_ == full_mask.size(); + } + + bool is_fully_uninitialized(const IndexMask full_mask) + { + UNUSED_VARS(full_mask); + return tot_initialized_ == 0; + } + + void add_as_input(MFParamsBuilder ¶ms, IndexMask mask, const MFDataType &data_type) const + { + /* Sanity check to make sure that enough values are initialized. */ + BLI_assert(mask.size() <= tot_initialized_); + + switch (value_->type) { + case ValueType::GVArray: { + params.add_readonly_single_input(this->value_as<VariableValue_GVArray>()->data); + break; + } + case ValueType::Span: { + const void *data = this->value_as<VariableValue_Span>()->data; + const GSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_readonly_single_input(span); + break; + } + case ValueType::GVVectorArray: { + params.add_readonly_vector_input(this->value_as<VariableValue_GVVectorArray>()->data); + break; + } + case ValueType::GVectorArray: { + params.add_readonly_vector_input(this->value_as<VariableValue_GVectorArray>()->data); + break; + } + case ValueType::OneSingle: { + const auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + const GPointer gpointer{data_type.single_type(), value_typed->data}; + params.add_readonly_single_input(gpointer); + break; + } + case ValueType::OneVector: { + params.add_readonly_vector_input(this->value_as<VariableValue_OneVector>()->data[0]); + break; + } + } + } + + /* TODO: What happens when the same parameter is passed to the same function more than ones. + * Maybe restrict this so that one variable can only be used either as multiple inputs, one + * mutable or one output. */ + void ensure_is_mutable(IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + if (ELEM(value_->type, ValueType::Span, ValueType::GVectorArray)) { + return; + } + + const int array_size = full_mask.min_array_size(); + + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + VariableValue_Span *new_value = nullptr; + if (caller_provided_storage_ == nullptr) { + new_value = value_allocator.obtain_Span(type, array_size); + } + else { + /* Reuse the storage provided caller when possible. */ + new_value = value_allocator.obtain_Span_not_owned(caller_provided_storage_); + } + if (value_->type == ValueType::GVArray) { + /* Fill new buffer with data from virtual array. */ + this->value_as<VariableValue_GVArray>()->data.materialize_to_uninitialized( + full_mask, new_value->data); + } + else if (value_->type == ValueType::OneSingle) { + auto *old_value_typed_ = this->value_as<VariableValue_OneSingle>(); + if (old_value_typed_->is_initialized) { + /* Fill the buffer with a single value. */ + type.fill_construct_indices(old_value_typed_->data, new_value->data, full_mask); + } + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + VariableValue_GVectorArray *new_value = nullptr; + if (caller_provided_storage_ == nullptr) { + new_value = value_allocator.obtain_GVectorArray(type, array_size); + } + else { + new_value = value_allocator.obtain_GVectorArray_not_owned( + *(GVectorArray *)caller_provided_storage_); + } + if (value_->type == ValueType::GVVectorArray) { + /* Fill new vector array with data from virtual vector array. */ + new_value->data.extend(full_mask, this->value_as<VariableValue_GVVectorArray>()->data); + } + else if (value_->type == ValueType::OneVector) { + /* Fill all indices with the same value. */ + const GSpan vector = this->value_as<VariableValue_OneVector>()->data[0]; + new_value->data.extend(full_mask, GVVectorArray_For_SingleGSpan{vector, array_size}); + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + } + } + + void add_as_mutable(MFParamsBuilder ¶ms, + IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + /* Sanity check to make sure that enough values are initialized. */ + BLI_assert(mask.size() <= tot_initialized_); + + this->ensure_is_mutable(full_mask, data_type, value_allocator); + + switch (value_->type) { + case ValueType::Span: { + void *data = this->value_as<VariableValue_Span>()->data; + const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_single_mutable(span); + break; + } + case ValueType::GVectorArray: { + params.add_vector_mutable(this->value_as<VariableValue_GVectorArray>()->data); + break; + } + case ValueType::GVArray: + case ValueType::GVVectorArray: + case ValueType::OneSingle: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + } + + void add_as_output(MFParamsBuilder ¶ms, + IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + /* Sanity check to make sure that enough values are not initialized. */ + BLI_assert(mask.size() <= full_mask.size() - tot_initialized_); + this->ensure_is_mutable(full_mask, data_type, value_allocator); + + switch (value_->type) { + case ValueType::Span: { + void *data = this->value_as<VariableValue_Span>()->data; + const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_uninitialized_single_output(span); + break; + } + case ValueType::GVectorArray: { + params.add_vector_output(this->value_as<VariableValue_GVectorArray>()->data); + break; + } + case ValueType::GVArray: + case ValueType::GVVectorArray: + case ValueType::OneSingle: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + + tot_initialized_ += mask.size(); + } + + void add_as_input__one(MFParamsBuilder ¶ms, const MFDataType &data_type) const + { + BLI_assert(this->is_one()); + + switch (value_->type) { + case ValueType::GVArray: { + params.add_readonly_single_input(this->value_as<VariableValue_GVArray>()->data); + break; + } + case ValueType::GVVectorArray: { + params.add_readonly_vector_input(this->value_as<VariableValue_GVVectorArray>()->data); + break; + } + case ValueType::OneSingle: { + const auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + GPointer ptr{data_type.single_type(), value_typed->data}; + params.add_readonly_single_input(ptr); + break; + } + case ValueType::OneVector: { + params.add_readonly_vector_input(this->value_as<VariableValue_OneVector>()->data); + break; + } + case ValueType::Span: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + } + + void ensure_is_mutable__one(const MFDataType &data_type, ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + if (ELEM(value_->type, ValueType::OneSingle, ValueType::OneVector)) { + return; + } + + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + VariableValue_OneSingle *new_value = value_allocator.obtain_OneSingle(type); + if (value_->type == ValueType::GVArray) { + this->value_as<VariableValue_GVArray>()->data.get_internal_single_to_uninitialized( + new_value->data); + new_value->is_initialized = true; + } + else if (value_->type == ValueType::Span) { + BLI_assert(tot_initialized_ == 0); + /* Nothing to do, the single value is uninitialized already. */ + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + VariableValue_OneVector *new_value = value_allocator.obtain_OneVector(type); + if (value_->type == ValueType::GVVectorArray) { + const GVVectorArray &old_vector_array = + this->value_as<VariableValue_GVVectorArray>()->data; + new_value->data.extend(IndexRange(1), old_vector_array); + } + else if (value_->type == ValueType::GVectorArray) { + BLI_assert(tot_initialized_ == 0); + /* Nothing to do. */ + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + } + } + + void add_as_mutable__one(MFParamsBuilder ¶ms, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + this->ensure_is_mutable__one(data_type, value_allocator); + + switch (value_->type) { + case ValueType::OneSingle: { + auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + params.add_single_mutable(GMutableSpan{data_type.single_type(), value_typed->data, 1}); + break; + } + case ValueType::OneVector: { + params.add_vector_mutable(this->value_as<VariableValue_OneVector>()->data); + break; + } + case ValueType::GVArray: + case ValueType::Span: + case ValueType::GVVectorArray: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + } + + void add_as_output__one(MFParamsBuilder ¶ms, + IndexMask mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + this->ensure_is_mutable__one(data_type, value_allocator); + + switch (value_->type) { + case ValueType::OneSingle: { + auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(!value_typed->is_initialized); + params.add_uninitialized_single_output( + GMutableSpan{data_type.single_type(), value_typed->data, 1}); + /* It becomes initialized when the multi-function is called. */ + value_typed->is_initialized = true; + break; + } + case ValueType::OneVector: { + auto *value_typed = this->value_as<VariableValue_OneVector>(); + BLI_assert(value_typed->data[0].is_empty()); + params.add_vector_output(this->value_as<VariableValue_OneVector>()->data); + break; + } + case ValueType::GVArray: + case ValueType::Span: + case ValueType::GVVectorArray: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + + tot_initialized_ += mask.size(); + } + + void destruct(IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + /* Sanity check to make sure that enough indices can be destructed. */ + BLI_assert(tot_initialized_ >= mask.size()); + + switch (value_->type) { + case ValueType::GVArray: { + if (mask.size() == full_mask.size()) { + /* All elements are destructed. The elements are owned by the caller, so we don't + * actually destruct them. */ + value_allocator.release_value(value_, data_type); + value_ = value_allocator.obtain_OneSingle(data_type.single_type()); + } + else { + /* Not all elements are destructed. Since we can't work on the original array, we have to + * create a copy first. */ + this->ensure_is_mutable(full_mask, data_type, value_allocator); + BLI_assert(value_->type == ValueType::Span); + const CPPType &type = data_type.single_type(); + type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask); + } + break; + } + case ValueType::Span: { + const CPPType &type = data_type.single_type(); + type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask); + break; + } + case ValueType::GVVectorArray: { + if (mask.size() == full_mask.size()) { + /* All elements are cleared. The elements are owned by the caller, so don't actually + * destruct them. */ + value_allocator.release_value(value_, data_type); + value_ = value_allocator.obtain_OneVector(data_type.vector_base_type()); + } + else { + /* Not all elements are cleared. Since we can't work on the original vector array, we + * have to create a copy first. A possible future optimization is to create the partial + * copy directly. */ + this->ensure_is_mutable(full_mask, data_type, value_allocator); + BLI_assert(value_->type == ValueType::GVectorArray); + this->value_as<VariableValue_GVectorArray>()->data.clear(mask); + } + break; + } + case ValueType::GVectorArray: { + this->value_as<VariableValue_GVectorArray>()->data.clear(mask); + break; + } + case ValueType::OneSingle: { + auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + if (mask.size() == tot_initialized_) { + const CPPType &type = data_type.single_type(); + type.destruct(value_typed->data); + value_typed->is_initialized = false; + } + break; + } + case ValueType::OneVector: { + auto *value_typed = this->value_as<VariableValue_OneVector>(); + if (mask.size() == tot_initialized_) { + value_typed->data.clear({0}); + } + break; + } + } + + tot_initialized_ -= mask.size(); + } + + void indices_split(IndexMask mask, IndicesSplitVectors &r_indices) + { + BLI_assert(mask.size() <= tot_initialized_); + + switch (value_->type) { + case ValueType::GVArray: { + const GVArray_Typed<bool> varray{this->value_as<VariableValue_GVArray>()->data}; + for (const int i : mask) { + r_indices[varray[i]].append(i); + } + break; + } + case ValueType::Span: { + const Span<bool> span((bool *)this->value_as<VariableValue_Span>()->data, + mask.min_array_size()); + for (const int i : mask) { + r_indices[span[i]].append(i); + } + break; + } + case ValueType::OneSingle: { + auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + const bool condition = *(bool *)value_typed->data; + r_indices[condition].extend(mask); + break; + } + case ValueType::GVVectorArray: + case ValueType::GVectorArray: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + } + + template<typename T> T *value_as() + { + BLI_assert(value_->type == T::static_type); + return static_cast<T *>(value_); + } + + template<typename T> const T *value_as() const + { + BLI_assert(value_->type == T::static_type); + return static_cast<T *>(value_); + } +}; + +template<typename... Args> VariableState *ValueAllocator::obtain_variable_state(Args &&...args) +{ + return new VariableState(std::forward<Args>(args)...); +} + +void ValueAllocator::release_variable_state(VariableState *state) +{ + delete state; +} + +class VariableStates { + private: + ValueAllocator value_allocator_; + Map<const MFVariable *, VariableState *> variable_states_; + IndexMask full_mask_; + + public: + VariableStates(IndexMask full_mask) : full_mask_(full_mask) + { + } + + ~VariableStates() + { + for (auto &&item : variable_states_.items()) { + const MFVariable *variable = item.key; + VariableState *state = item.value; + state->destruct_self(value_allocator_, variable->data_type()); + } + } + + ValueAllocator &value_allocator() + { + return value_allocator_; + } + + const IndexMask &full_mask() const + { + return full_mask_; + } + + void add_initial_variable_states(const MFProcedureExecutor &fn, + const MFProcedure &procedure, + MFParams ¶ms) + { + for (const int param_index : fn.param_indices()) { + MFParamType param_type = fn.param_type(param_index); + const MFVariable *variable = procedure.params()[param_index].second; + + auto add_state = [&](VariableValue *value, + bool input_is_initialized, + void *caller_provided_storage = nullptr) { + const int tot_initialized = input_is_initialized ? full_mask_.size() : 0; + variable_states_.add_new(variable, + value_allocator_.obtain_variable_state( + *value, tot_initialized, caller_provided_storage)); + }; + + switch (param_type.category()) { + case MFParamType::SingleInput: { + const GVArray &data = params.readonly_single_input(param_index); + add_state(value_allocator_.obtain_GVArray(data), true); + break; + } + case MFParamType::VectorInput: { + const GVVectorArray &data = params.readonly_vector_input(param_index); + add_state(value_allocator_.obtain_GVVectorArray(data), true); + break; + } + case MFParamType::SingleOutput: { + GMutableSpan data = params.uninitialized_single_output(param_index); + add_state(value_allocator_.obtain_Span_not_owned(data.data()), false, data.data()); + break; + } + case MFParamType::VectorOutput: { + GVectorArray &data = params.vector_output(param_index); + add_state(value_allocator_.obtain_GVectorArray_not_owned(data), false, &data); + break; + } + case MFParamType::SingleMutable: { + GMutableSpan data = params.single_mutable(param_index); + add_state(value_allocator_.obtain_Span_not_owned(data.data()), true, data.data()); + break; + } + case MFParamType::VectorMutable: { + GVectorArray &data = params.vector_mutable(param_index); + add_state(value_allocator_.obtain_GVectorArray_not_owned(data), true, &data); + break; + } + } + } + } + + void add_as_param(VariableState &variable_state, + MFParamsBuilder ¶ms, + const MFParamType ¶m_type, + const IndexMask &mask) + { + const MFDataType data_type = param_type.data_type(); + switch (param_type.interface_type()) { + case MFParamType::Input: { + variable_state.add_as_input(params, mask, data_type); + break; + } + case MFParamType::Mutable: { + variable_state.add_as_mutable(params, mask, full_mask_, data_type, value_allocator_); + break; + } + case MFParamType::Output: { + variable_state.add_as_output(params, mask, full_mask_, data_type, value_allocator_); + break; + } + } + } + + void add_as_param__one(VariableState &variable_state, + MFParamsBuilder ¶ms, + const MFParamType ¶m_type, + const IndexMask &mask) + { + const MFDataType data_type = param_type.data_type(); + switch (param_type.interface_type()) { + case MFParamType::Input: { + variable_state.add_as_input__one(params, data_type); + break; + } + case MFParamType::Mutable: { + variable_state.add_as_mutable__one(params, data_type, value_allocator_); + break; + } + case MFParamType::Output: { + variable_state.add_as_output__one(params, mask, data_type, value_allocator_); + break; + } + } + } + + void destruct(const MFVariable &variable, const IndexMask &mask) + { + VariableState &variable_state = this->get_variable_state(variable); + variable_state.destruct(mask, full_mask_, variable.data_type(), value_allocator_); + } + + VariableState &get_variable_state(const MFVariable &variable) + { + return *variable_states_.lookup_or_add_cb( + &variable, [&]() { return this->create_new_state_for_variable(variable); }); + } + + VariableState *create_new_state_for_variable(const MFVariable &variable) + { + MFDataType data_type = variable.data_type(); + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + return value_allocator_.obtain_variable_state(*value_allocator_.obtain_OneSingle(type), 0); + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + return value_allocator_.obtain_variable_state(*value_allocator_.obtain_OneVector(type), 0); + } + } + BLI_assert_unreachable(); + return nullptr; + } +}; + +static bool evaluate_as_one(const MultiFunction &fn, + Span<VariableState *> param_variable_states, + const IndexMask &mask, + const IndexMask &full_mask) +{ + if (fn.depends_on_context()) { + return false; + } + if (mask.size() < full_mask.size()) { + return false; + } + for (VariableState *state : param_variable_states) { + if (!state->is_one()) { + return false; + } + } + return true; +} + +static void execute_call_instruction(const MFCallInstruction &instruction, + IndexMask mask, + VariableStates &variable_states, + const MFContext &context) +{ + const MultiFunction &fn = instruction.fn(); + + Vector<VariableState *> param_variable_states; + param_variable_states.resize(fn.param_amount()); + + for (const int param_index : fn.param_indices()) { + const MFVariable *variable = instruction.params()[param_index]; + VariableState &variable_state = variable_states.get_variable_state(*variable); + param_variable_states[param_index] = &variable_state; + } + + if (evaluate_as_one(fn, param_variable_states, mask, variable_states.full_mask())) { + MFParamsBuilder params(fn, 1); + + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + VariableState &variable_state = *param_variable_states[param_index]; + variable_states.add_as_param__one(variable_state, params, param_type, mask); + } + + fn.call(IndexRange(1), params, context); + } + else { + MFParamsBuilder params(fn, mask.min_array_size()); + + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + VariableState &variable_state = *param_variable_states[param_index]; + variable_states.add_as_param(variable_state, params, param_type, mask); + } + + fn.call(mask, params, context); + } +} + +struct NextInstructionInfo { + const MFInstruction *instruction = nullptr; + Vector<int64_t> owned_indices; + + IndexMask mask() const + { + return this->owned_indices.as_span(); + } + + operator bool() const + { + return this->instruction != nullptr; + } +}; + +class InstructionScheduler { + private: + Map<const MFInstruction *, Vector<Vector<int64_t>>> indices_by_instruction_; + + public: + InstructionScheduler() = default; + + void add_referenced_indices(const MFInstruction *instruction, IndexMask mask) + { + if (instruction == nullptr) { + return; + } + if (mask.is_empty()) { + return; + } + indices_by_instruction_.lookup_or_add_default(instruction).append(mask.indices()); + } + + void add_owned_indices(const MFInstruction *instruction, Vector<int64_t> indices) + { + if (instruction == nullptr) { + return; + } + if (indices.is_empty()) { + return; + } + BLI_assert(IndexMask::indices_are_valid_index_mask(indices)); + indices_by_instruction_.lookup_or_add_default(instruction).append(std::move(indices)); + } + + void add_previous_instruction_indices(const MFInstruction *instruction, + NextInstructionInfo &instr_info) + { + this->add_owned_indices(instruction, std::move(instr_info.owned_indices)); + } + + NextInstructionInfo pop_next() + { + if (indices_by_instruction_.is_empty()) { + return {}; + } + /* TODO: Implement better mechanism to determine next instruction. */ + const MFInstruction *instruction = *indices_by_instruction_.keys().begin(); + + Vector<int64_t> indices = this->pop_indices_array(instruction); + if (indices.is_empty()) { + return {}; + } + + NextInstructionInfo next_instruction_info; + next_instruction_info.instruction = instruction; + next_instruction_info.owned_indices = std::move(indices); + return next_instruction_info; + } + + private: + Vector<int64_t> pop_indices_array(const MFInstruction *instruction) + { + Vector<Vector<int64_t>> *indices = indices_by_instruction_.lookup_ptr(instruction); + if (indices == nullptr) { + return {}; + } + Vector<int64_t> r_indices; + while (!indices->is_empty() && r_indices.is_empty()) { + r_indices = (*indices).pop_last(); + } + if (indices->is_empty()) { + indices_by_instruction_.remove_contained(instruction); + } + return r_indices; + } +}; + +void MFProcedureExecutor::call(IndexMask full_mask, MFParams params, MFContext context) const +{ + if (procedure_.entry() == nullptr) { + return; + } + + LinearAllocator<> allocator; + + VariableStates variable_states{full_mask}; + variable_states.add_initial_variable_states(*this, procedure_, params); + + InstructionScheduler scheduler; + scheduler.add_referenced_indices(procedure_.entry(), full_mask); + + while (NextInstructionInfo instr_info = scheduler.pop_next()) { + const MFInstruction &instruction = *instr_info.instruction; + switch (instruction.type()) { + case MFInstructionType::Call: { + const MFCallInstruction &call_instruction = static_cast<const MFCallInstruction &>( + instruction); + execute_call_instruction(call_instruction, instr_info.mask(), variable_states, context); + scheduler.add_previous_instruction_indices(call_instruction.next(), instr_info); + break; + } + case MFInstructionType::Branch: { + const MFBranchInstruction &branch_instruction = static_cast<const MFBranchInstruction &>( + instruction); + const MFVariable *condition_var = branch_instruction.condition(); + VariableState &variable_state = variable_states.get_variable_state(*condition_var); + + IndicesSplitVectors new_indices; + variable_state.indices_split(instr_info.mask(), new_indices); + scheduler.add_owned_indices(branch_instruction.branch_false(), new_indices[false]); + scheduler.add_owned_indices(branch_instruction.branch_true(), new_indices[true]); + break; + } + case MFInstructionType::Destruct: { + const MFDestructInstruction &destruct_instruction = + static_cast<const MFDestructInstruction &>(instruction); + const MFVariable *variable = destruct_instruction.variable(); + variable_states.destruct(*variable, instr_info.mask()); + scheduler.add_previous_instruction_indices(destruct_instruction.next(), instr_info); + break; + } + case MFInstructionType::Dummy: { + const MFDummyInstruction &dummy_instruction = static_cast<const MFDummyInstruction &>( + instruction); + scheduler.add_previous_instruction_indices(dummy_instruction.next(), instr_info); + break; + } + } + } + + for (const int param_index : this->param_indices()) { + const MFParamType param_type = this->param_type(param_index); + const MFVariable *variable = procedure_.params()[param_index].second; + VariableState &variable_state = variable_states.get_variable_state(*variable); + switch (param_type.interface_type()) { + case MFParamType::Input: { + /* Input variables must be destructed in the end. */ + BLI_assert(variable_state.is_fully_uninitialized(full_mask)); + break; + } + case MFParamType::Mutable: + case MFParamType::Output: { + /* Mutable and output variables must be initialized in the end. */ + BLI_assert(variable_state.is_fully_initialized(full_mask)); + /* Make sure that the data is in the memory provided by the caller. */ + variable_state.ensure_is_mutable( + full_mask, param_type.data_type(), variable_states.value_allocator()); + break; + } + } + } +} + +} // namespace blender::fn 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_procedure_test.cc b/source/blender/functions/tests/FN_multi_function_procedure_test.cc new file mode 100644 index 00000000000..5e728db4d3b --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_procedure_test.cc @@ -0,0 +1,279 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_procedure_builder.hh" +#include "FN_multi_function_procedure_executor.hh" +#include "FN_multi_function_test_common.hh" + +namespace blender::fn::tests { + +TEST(multi_function_procedure, SimpleTest) +{ + /** + * procedure(int var1, int var2, int *var4) { + * int var3 = var1 + var2; + * var4 = var2 + var3; + * } + */ + + CustomMF_SI_SI_SO<int, int, int> add_fn{"add", [](int a, int b) { return a + b; }}; + CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_input_parameter<int>(); + MFVariable *var2 = &builder.add_single_input_parameter<int>(); + auto [var3] = builder.add_call_with_new_variables<1>(add_fn, {var1, var2}); + auto [var4] = builder.add_call_with_new_variables<1>(add_fn, {var2, var3}); + builder.add_call(add_10_fn, {var4}); + builder.add_destruct({var1, var2, var3}); + builder.add_output_parameter(*var4); + + MFProcedureExecutor executor{"My Procedure", procedure}; + + MFParamsBuilder params{executor, 3}; + MFContextBuilder context; + + Array<int> input_array = {1, 2, 3}; + params.add_readonly_single_input(input_array.as_span()); + params.add_readonly_single_input_value(3); + + Array<int> output_array(3); + params.add_uninitialized_single_output(output_array.as_mutable_span()); + + executor.call(IndexRange(3), params, context); + + EXPECT_EQ(output_array[0], 17); + EXPECT_EQ(output_array[1], 18); + EXPECT_EQ(output_array[2], 19); +} + +TEST(multi_function_procedure, BranchTest) +{ + /** + * procedure(int &var1, bool var2) { + * if (var2) { + * var1 += 100; + * } + * else { + * var1 += 10; + * } + * var1 += 10; + * } + */ + + CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }}; + CustomMF_SM<int> add_100_fn{"add_100", [](int &a) { a += 100; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_mutable_parameter<int>(); + MFVariable *var2 = &builder.add_single_input_parameter<bool>(); + + MFProcedureBuilder::Branch branch = builder.add_branch(*var2); + branch.branch_false.add_call(add_10_fn, {var1}); + branch.branch_true.add_call(add_100_fn, {var1}); + builder.set_cursor_after_branch(branch); + builder.add_call(add_10_fn, {var1}); + builder.add_destruct({var2}); + + MFProcedureExecutor procedure_fn{"Condition Test", procedure}; + MFParamsBuilder params(procedure_fn, 5); + + Array<int> values_a = {1, 5, 3, 6, 2}; + Array<bool> values_cond = {true, false, true, true, false}; + + params.add_single_mutable(values_a.as_mutable_span()); + params.add_readonly_single_input(values_cond.as_span()); + + MFContextBuilder context; + procedure_fn.call({1, 2, 3, 4}, params, context); + + EXPECT_EQ(values_a[0], 1); + EXPECT_EQ(values_a[1], 25); + EXPECT_EQ(values_a[2], 113); + EXPECT_EQ(values_a[3], 116); + EXPECT_EQ(values_a[4], 22); +} + +TEST(multi_function_procedure, EvaluateOne) +{ + /** + * procedure(int var1, int var2) { + * var2 = var1 + 10; + * } + */ + + int tot_evaluations = 0; + CustomMF_SI_SO<int, int> add_10_fn{"add_10", [&](int a) { + tot_evaluations++; + return a + 10; + }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_input_parameter<int>(); + auto [var2] = builder.add_call_with_new_variables<1>(add_10_fn, {var1}); + builder.add_destruct(*var1); + builder.add_output_parameter(*var2); + + MFProcedureExecutor procedure_fn{"Evaluate One", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array<int> values_out = {1, 2, 3, 4, 5}; + params.add_readonly_single_input_value(1); + params.add_uninitialized_single_output(values_out.as_mutable_span()); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(values_out[0], 11); + EXPECT_EQ(values_out[1], 11); + EXPECT_EQ(values_out[2], 3); + EXPECT_EQ(values_out[3], 11); + EXPECT_EQ(values_out[4], 11); + /* We expect only one evaluation, because the input is constant. */ + EXPECT_EQ(tot_evaluations, 1); +} + +TEST(multi_function_procedure, SimpleLoop) +{ + /** + * procedure(int count, int *out) { + * out = 1; + * int index = 0' + * loop { + * if (index >= count) { + * break; + * } + * out *= 2; + * index += 1; + * } + * out += 1000; + * } + */ + + CustomMF_Constant<int> const_1_fn{1}; + CustomMF_Constant<int> const_0_fn{0}; + CustomMF_SI_SI_SO<int, int, bool> greater_or_equal_fn{"greater or equal", + [](int a, int b) { return a >= b; }}; + CustomMF_SM<int> double_fn{"double", [](int &a) { a *= 2; }}; + CustomMF_SM<int> add_1000_fn{"add 1000", [](int &a) { a += 1000; }}; + CustomMF_SM<int> add_1_fn{"add 1", [](int &a) { a += 1; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var_count = &builder.add_single_input_parameter<int>("count"); + auto [var_out] = builder.add_call_with_new_variables<1>(const_1_fn); + var_out->set_name("out"); + auto [var_index] = builder.add_call_with_new_variables<1>(const_0_fn); + var_index->set_name("index"); + + MFProcedureBuilder::Loop loop = builder.add_loop(); + auto [var_condition] = builder.add_call_with_new_variables<1>(greater_or_equal_fn, + {var_index, var_count}); + var_condition->set_name("condition"); + MFProcedureBuilder::Branch branch = builder.add_branch(*var_condition); + branch.branch_true.add_destruct(*var_condition); + branch.branch_true.add_loop_break(loop); + branch.branch_false.add_destruct(*var_condition); + builder.set_cursor_after_branch(branch); + builder.add_call(double_fn, {var_out}); + builder.add_call(add_1_fn, {var_index}); + builder.add_loop_continue(loop); + builder.set_cursor_after_loop(loop); + builder.add_call(add_1000_fn, {var_out}); + builder.add_destruct({var_count, var_index}); + builder.add_output_parameter(*var_out); + + MFProcedureExecutor procedure_fn{"Simple Loop", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array<int> counts = {4, 3, 7, 6, 4}; + Array<int> results(5, -1); + + params.add_readonly_single_input(counts.as_span()); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(results[0], 1016); + EXPECT_EQ(results[1], 1008); + EXPECT_EQ(results[2], -1); + EXPECT_EQ(results[3], 1064); + EXPECT_EQ(results[4], 1016); +} + +TEST(multi_function_procedure, Vectors) +{ + /** + * procedure(vector<int> v1, vector<int> &v2, vector<int> *v3) { + * v1.extend(v2); + * int constant = 5; + * v2.append(constant); + * v2.extend(v1); + * int len = sum(v2); + * v3 = range(len); + * } + */ + + CreateRangeFunction create_range_fn; + ConcatVectorsFunction extend_fn; + GenericAppendFunction append_fn{CPPType::get<int>()}; + SumVectorFunction sum_elements_fn; + CustomMF_Constant<int> constant_5_fn{5}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var_v1 = &builder.add_input_parameter(MFDataType::ForVector<int>()); + MFVariable *var_v2 = &builder.add_parameter(MFParamType::ForMutableVector(CPPType::get<int>())); + builder.add_call(extend_fn, {var_v1, var_v2}); + auto [var_constant] = builder.add_call_with_new_variables<1>(constant_5_fn); + builder.add_call(append_fn, {var_v2, var_constant}); + builder.add_destruct(*var_constant); + builder.add_call(extend_fn, {var_v2, var_v1}); + auto [var_len] = builder.add_call_with_new_variables<1>(sum_elements_fn, {var_v2}); + auto [var_v3] = builder.add_call_with_new_variables<1>(create_range_fn, {var_len}); + builder.add_destruct({var_v1, var_len}); + builder.add_output_parameter(*var_v3); + + MFProcedureExecutor procedure_fn{"Vectors", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array<int> v1 = {5, 2, 3}; + GVectorArray v2{CPPType::get<int>(), 5}; + GVectorArray v3{CPPType::get<int>(), 5}; + + int value_10 = 10; + v2.append(0, &value_10); + v2.append(4, &value_10); + + params.add_readonly_vector_input(v1.as_span()); + params.add_vector_mutable(v2); + params.add_vector_output(v3); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(v2[0].size(), 6); + EXPECT_EQ(v2[1].size(), 4); + EXPECT_EQ(v2[2].size(), 0); + EXPECT_EQ(v2[3].size(), 4); + EXPECT_EQ(v2[4].size(), 6); + + EXPECT_EQ(v3[0].size(), 35); + EXPECT_EQ(v3[1].size(), 15); + EXPECT_EQ(v3[2].size(), 0); + EXPECT_EQ(v3[3].size(), 15); + EXPECT_EQ(v3[4].size(), 35); +} + +} // 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 diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 3853b345c14..620c7ef438a 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -84,7 +84,8 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" #include "NOD_geometry_nodes_eval_log.hh" -#include "NOD_node_tree_multi_function.hh" + +#include "FN_multi_function.hh" using blender::destruct_ptr; using blender::float3; @@ -858,7 +859,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, { blender::ResourceScope scope; blender::LinearAllocator<> &allocator = scope.linear_allocator(); - blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope); + blender::nodes::NodeMultiFunctions mf_by_node{tree, scope}; Map<DOutputSocket, GMutablePointer> group_inputs; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 47dfd9bc8f6..5646e37707c 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -836,7 +836,7 @@ class GeometryNodesEvaluator { } /* Use the multi-function implementation if it exists. */ - const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr); + const MultiFunction *multi_function = params_.mf_by_node->try_get(node); if (multi_function != nullptr) { this->execute_multi_function_node(node, *multi_function, node_state); return; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh index d8c60d31986..5151be07aa2 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -20,12 +20,14 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry_nodes_eval_log.hh" -#include "NOD_node_tree_multi_function.hh" +#include "NOD_multi_function.hh" #include "FN_generic_pointer.hh" #include "DNA_modifier_types.h" +#include "FN_multi_function.hh" + namespace geo_log = blender::nodes::geometry_nodes_eval_log; namespace blender::modifiers::geometry_nodes { @@ -45,7 +47,7 @@ struct GeometryNodesEvaluationParams { * necessary in all cases. Sometimes `log_socket_value_fn` might just want to look at the value * and then it can be freed. */ Vector<DSocket> force_compute_sockets; - nodes::MultiFunctionByNode *mf_by_node; + nodes::NodeMultiFunctions *mf_by_node; const NodesModifierData *modifier_; Depsgraph *depsgraph; Object *self_object; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 46fb9f54bfe..8680fcee49a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -345,11 +345,10 @@ set(SRC intern/node_common.c intern/node_exec.cc intern/node_geometry_exec.cc + intern/node_multi_function.cc intern/node_socket.cc - intern/node_tree_multi_function.cc intern/node_tree_ref.cc intern/node_util.c - intern/type_callbacks.cc intern/type_conversions.cc composite/node_composite_util.h @@ -366,13 +365,12 @@ set(SRC NOD_geometry_exec.hh NOD_geometry_nodes_eval_log.hh NOD_math_functions.hh - NOD_node_tree_multi_function.hh + NOD_multi_function.hh NOD_node_tree_ref.hh NOD_shader.h NOD_socket.h NOD_static_types.h NOD_texture.h - NOD_type_callbacks.hh NOD_type_conversions.hh intern/node_common.h intern/node_exec.h diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh new file mode 100644 index 00000000000..96118ef3f76 --- /dev/null +++ b/source/blender/nodes/NOD_multi_function.hh @@ -0,0 +1,115 @@ +/* + * 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.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +namespace blender::nodes { + +using namespace fn::multi_function_types; + +class NodeMultiFunctions; + +class NodeMultiFunctionBuilder : NonCopyable, NonMovable { + private: + ResourceScope &resource_scope_; + bNode &node_; + bNodeTree &tree_; + const MultiFunction *built_fn_ = nullptr; + + friend NodeMultiFunctions; + + public: + NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree); + + void set_matching_fn(const MultiFunction *fn); + void set_matching_fn(const MultiFunction &fn); + template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args); + + bNode &node(); + bNodeTree &tree(); + + ResourceScope &resource_scope(); +}; + +class NodeMultiFunctions { + private: + Map<const bNode *, const MultiFunction *> map_; + + public: + NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope); + + const MultiFunction *try_get(const DNode &node) const; +}; + +/* -------------------------------------------------------------------- + * NodeMultiFunctionBuilder inline methods. + */ + +inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, + bNode &node, + bNodeTree &tree) + : resource_scope_(resource_scope), node_(node), tree_(tree) +{ +} + +inline bNode &NodeMultiFunctionBuilder::node() +{ + return node_; +} + +inline bNodeTree &NodeMultiFunctionBuilder::tree() +{ + return tree_; +} + +inline ResourceScope &NodeMultiFunctionBuilder::resource_scope() +{ + return resource_scope_; +} + +inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) +{ + built_fn_ = fn; +} + +inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) +{ + this->set_matching_fn(&fn); +} + +template<typename T, typename... Args> +inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) +{ + const T &fn = resource_scope_.construct<T>(__func__, std::forward<Args>(args)...); + this->set_matching_fn(&fn); +} + +/* -------------------------------------------------------------------- + * NodeMultiFunctions inline methods. + */ + +inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const +{ + return map_.lookup_default(node->bnode(), nullptr); +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh deleted file mode 100644 index 7eeeaef0b98..00000000000 --- a/source/blender/nodes/NOD_node_tree_multi_function.hh +++ /dev/null @@ -1,390 +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 nodes - * - * This file allows you to generate a multi-function network from a user-generated node tree. - */ - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network.hh" - -#include "NOD_derived_node_tree.hh" -#include "NOD_type_callbacks.hh" - -#include "BLI_multi_value_map.hh" -#include "BLI_resource_scope.hh" - -namespace blender::nodes { - -/** - * A MFNetworkTreeMap maps various components of a node tree to components of a fn::MFNetwork. This - * is necessary for further processing of a multi-function network that has been generated from a - * node tree. - */ -class MFNetworkTreeMap { - private: - /** - * Store by id instead of using a hash table to avoid unnecessary hash table lookups. - * - * Input sockets in a node tree can have multiple corresponding sockets in the generated - * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes. - */ - const DerivedNodeTree &tree_; - fn::MFNetwork &network_; - MultiValueMap<DSocket, fn::MFSocket *> sockets_by_dsocket_; - - public: - MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network) - : tree_(tree), network_(network) - { - } - - const DerivedNodeTree &tree() const - { - return tree_; - } - - const fn::MFNetwork &network() const - { - return network_; - } - - fn::MFNetwork &network() - { - return network_; - } - - void add(const DSocket &dsocket, fn::MFSocket &socket) - { - BLI_assert(dsocket->is_input() == socket.is_input()); - BLI_assert(dsocket->is_input() || sockets_by_dsocket_.lookup(dsocket).is_empty()); - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DInputSocket &dsocket, fn::MFInputSocket &socket) - { - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket) - { - /* There can be at most one matching output socket. */ - BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty()); - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DTreeContext &context, - Span<const InputSocketRef *> dsockets, - Span<fn::MFInputSocket *> sockets) - { - assert_same_size(dsockets, sockets); - for (int i : dsockets.index_range()) { - this->add(DInputSocket(&context, dsockets[i]), *sockets[i]); - } - } - - void add(const DTreeContext &context, - Span<const OutputSocketRef *> dsockets, - Span<fn::MFOutputSocket *> sockets) - { - assert_same_size(dsockets, sockets); - for (int i : dsockets.index_range()) { - this->add(DOutputSocket(&context, dsockets[i]), *sockets[i]); - } - } - - void add_try_match(const DNode &dnode, fn::MFNode &node) - { - this->add_try_match(*dnode.context(), - dnode->inputs().cast<const SocketRef *>(), - node.inputs().cast<fn::MFSocket *>()); - this->add_try_match(*dnode.context(), - dnode->outputs().cast<const SocketRef *>(), - node.outputs().cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const InputSocketRef *> dsockets, - Span<fn::MFInputSocket *> sockets) - { - this->add_try_match( - context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const OutputSocketRef *> dsockets, - Span<fn::MFOutputSocket *> sockets) - { - this->add_try_match( - context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const SocketRef *> dsockets, - Span<fn::MFSocket *> sockets) - { - int used_sockets = 0; - for (const SocketRef *dsocket : dsockets) { - if (!dsocket->is_available()) { - continue; - } - if (!socket_is_mf_data_socket(*dsocket->typeinfo())) { - continue; - } - fn::MFSocket *socket = sockets[used_sockets]; - this->add(DSocket(&context, dsocket), *socket); - used_sockets++; - } - } - - fn::MFOutputSocket &lookup(const DOutputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket)[0]->as_output(); - } - - Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket).cast<fn::MFInputSocket *>(); - } - - fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket) - { - Span<fn::MFInputSocket *> sockets = this->lookup(dsocket); - BLI_assert(sockets.size() == 1); - fn::MFInputSocket &socket = *sockets[0]; - BLI_assert(socket.node().is_dummy()); - return socket; - } - - fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket) - { - fn::MFOutputSocket &socket = this->lookup(dsocket); - BLI_assert(socket.node().is_dummy()); - return socket; - } - - bool is_mapped(const DSocket &dsocket) const - { - return !sockets_by_dsocket_.lookup(dsocket).is_empty(); - } -}; - -/** - * This data is necessary throughout the generation of a MFNetwork from a node tree. - */ -struct CommonMFNetworkBuilderData { - ResourceScope &scope; - fn::MFNetwork &network; - MFNetworkTreeMap &network_map; - const DerivedNodeTree &tree; -}; - -class MFNetworkBuilderBase { - protected: - CommonMFNetworkBuilderData &common_; - - public: - MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common) - { - } - - /** - * Returns the network that is currently being built. - */ - fn::MFNetwork &network() - { - return common_.network; - } - - /** - * Returns the map between the node tree and the multi-function network that is being built. - */ - MFNetworkTreeMap &network_map() - { - return common_.network_map; - } - - /** - * Returns a resource collector that will only be destructed after the multi-function network is - * destructed. - */ - ResourceScope &resource_scope() - { - return common_.scope; - } - - /** - * Constructs a new function that will live at least as long as the MFNetwork. - */ - template<typename T, typename... Args> T &construct_fn(Args &&...args) - { - BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), ""); - void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T)); - T *fn = new (buffer) T(std::forward<Args>(args)...); - common_.scope.add(destruct_ptr<T>(fn), fn->name().c_str()); - return *fn; - } -}; - -/** - * This class is used by socket implementations to define how an unlinked input socket is handled - * in a multi-function network. - */ -class SocketMFNetworkBuilder : public MFNetworkBuilderBase { - private: - bNodeSocket *bsocket_; - fn::MFOutputSocket *built_socket_ = nullptr; - - public: - SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket) - : MFNetworkBuilderBase(common), bsocket_(dsocket->bsocket()) - { - } - - /** - * Returns the socket that is currently being built. - */ - bNodeSocket &bsocket() - { - return *bsocket_; - } - - /** - * Utility method that returns bsocket->default_value for the current socket. - */ - template<typename T> T *socket_default_value() - { - return static_cast<T *>(bsocket_->default_value); - } - - /** - * Builds a function node for that socket that outputs the given constant value. - */ - template<typename T> void set_constant_value(T value) - { - this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value)); - } - void set_constant_value(const CPPType &type, const void *value) - { - /* The value has live as long as the generated mf network. */ - this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value); - } - - template<typename T, typename... Args> void construct_generator_fn(Args &&...args) - { - const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(args)...); - this->set_generator_fn(fn); - } - - /** - * Uses the first output of the given multi-function as value of the socket. - */ - void set_generator_fn(const fn::MultiFunction &fn) - { - fn::MFFunctionNode &node = common_.network.add_function(fn); - this->set_socket(node.output(0)); - } - - /** - * Define a multi-function socket that outputs the value of the bsocket. - */ - void set_socket(fn::MFOutputSocket &socket) - { - built_socket_ = &socket; - } - - fn::MFOutputSocket *built_socket() - { - return built_socket_; - } -}; - -/** - * This class is used by node implementations to define how a user-level node expands into - * multi-function nodes internally. - */ -class NodeMFNetworkBuilder : public MFNetworkBuilderBase { - private: - DNode dnode_; - - public: - NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, DNode dnode) - : MFNetworkBuilderBase(common), dnode_(dnode) - { - } - - /** - * Tells the builder to build a function that corresponds to the node that is being built. It - * will try to match up sockets. - */ - template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&...args) - { - T &function = this->construct_fn<T>(std::forward<Args>(args)...); - this->set_matching_fn(function); - return function; - } - - const fn::MultiFunction &get_not_implemented_fn() - { - return this->get_default_fn("Not Implemented (" + dnode_->name() + ")"); - } - - const fn::MultiFunction &get_default_fn(StringRef name); - - const void set_not_implemented() - { - this->set_matching_fn(this->get_not_implemented_fn()); - } - - /** - * Tells the builder that the given function corresponds to the node that is being built. It will - * try to match up sockets. For that it skips unavailable and non-data sockets. - */ - void set_matching_fn(const fn::MultiFunction &function) - { - fn::MFFunctionNode &node = common_.network.add_function(function); - common_.network_map.add_try_match(dnode_, node); - } - - /** - * Returns the node that is currently being built. - */ - bNode &bnode() - { - return *dnode_->bnode(); - } - - /** - * Returns the node that is currently being built. - */ - const DNode &dnode() const - { - return dnode_; - } -}; - -MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, - const DerivedNodeTree &tree, - ResourceScope &scope); - -using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>; -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope); - -} // namespace blender::nodes diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh index 9fbd6712827..96a8f29c3e9 100644 --- a/source/blender/nodes/function/node_function_util.hh +++ b/source/blender/nodes/function/node_function_util.hh @@ -30,7 +30,7 @@ #include "BLT_translation.h" #include "NOD_function.h" -#include "NOD_node_tree_multi_function.hh" +#include "NOD_multi_function.hh" #include "node_util.h" diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 7a83ff8e016..0ba9080918c 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -58,7 +58,7 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) { static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{ "And", [](bool a, bool b) { return a && b; }}; @@ -68,20 +68,21 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: - return and_fn; + return &and_fn; case NODE_BOOLEAN_MATH_OR: - return or_fn; + return &or_fn; case NODE_BOOLEAN_MATH_NOT: - return not_fn; + return ¬_fn; } - BLI_assert(false); - return blender::fn::dummy_multi_function; + BLI_assert_unreachable(); + return nullptr; } -static void node_boolean_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_boolean_math_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -93,7 +94,7 @@ void register_node_type_fn_boolean_math() node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out); node_type_label(&ntype, node_boolean_math_label); node_type_update(&ntype, node_boolean_math_update); - ntype.expand_in_mf_network = node_boolean_expand_in_mf_network; + ntype.build_multi_function = fn_node_boolean_math_build_multi_function; ntype.draw_buttons = fn_node_boolean_math_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 6c8df8f2ea0..16ffb761a15 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -64,7 +64,7 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &node) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{ "Less Than", [](float a, float b) { return a < b; }}; @@ -81,26 +81,27 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &node) switch (node.custom1) { case NODE_FLOAT_COMPARE_LESS_THAN: - return less_than_fn; + return &less_than_fn; case NODE_FLOAT_COMPARE_LESS_EQUAL: - return less_equal_fn; + return &less_equal_fn; case NODE_FLOAT_COMPARE_GREATER_THAN: - return greater_than_fn; + return &greater_than_fn; case NODE_FLOAT_COMPARE_GREATER_EQUAL: - return greater_equal_fn; + return &greater_equal_fn; case NODE_FLOAT_COMPARE_EQUAL: - return equal_fn; + return &equal_fn; case NODE_FLOAT_COMPARE_NOT_EQUAL: - return not_equal_fn; + return ¬_equal_fn; } - BLI_assert(false); - return blender::fn::dummy_multi_function; + BLI_assert_unreachable(); + return nullptr; } -static void node_float_compare_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_float_compare_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -112,7 +113,7 @@ void register_node_type_fn_float_compare() node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out); node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); - ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network; + ntype.build_multi_function = fn_node_float_compare_build_multi_function; ntype.draw_buttons = geo_node_float_compare_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 26cde576400..52acfefe615 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -50,7 +50,7 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) { static blender::fn::CustomMF_SI_SO<float, int> round_fn{"Round", [](float a) { return (int)round(a); }}; @@ -63,22 +63,23 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) { case FN_NODE_FLOAT_TO_INT_ROUND: - return round_fn; + return &round_fn; case FN_NODE_FLOAT_TO_INT_FLOOR: - return floor_fn; + return &floor_fn; case FN_NODE_FLOAT_TO_INT_CEIL: - return ceil_fn; + return &ceil_fn; case FN_NODE_FLOAT_TO_INT_TRUNCATE: - return trunc_fn; + return &trunc_fn; } BLI_assert_unreachable(); - return blender::fn::dummy_multi_function; + return nullptr; } -static void node_float_to_int_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_float_to_int_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -89,7 +90,7 @@ void register_node_type_fn_float_to_int() fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out); node_type_label(&ntype, node_float_to_int_label); - ntype.expand_in_mf_network = node_float_to_int_expand_in_mf_network; + ntype.build_multi_function = fn_node_float_to_int_build_multi_function; ntype.draw_buttons = fn_node_float_to_int_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index f16bdef2f38..560ace57aba 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -29,14 +29,14 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(layout, ptr, "string", 0, "", ICON_NONE); } -static void fn_node_input_string_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_input_string_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage); std::string string = std::string((node_storage->string) ? node_storage->string : ""); - - builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(string); + builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>( + std::move(string)); } static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -78,7 +78,7 @@ void register_node_type_fn_input_string() node_type_socket_templates(&ntype, nullptr, fn_node_input_string_out); node_type_init(&ntype, fn_node_input_string_init); node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); - ntype.expand_in_mf_network = fn_node_input_string_expand_in_mf_network; + ntype.build_multi_function = fn_node_input_string_build_multi_function; ntype.draw_buttons = fn_node_input_string_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 2cd4eb1d9df..244c045de9a 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -32,16 +32,14 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE); } -static void fn_node_vector_input_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_vector_input_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage); blender::float3 vector(node_storage->vector); - builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<blender::float3>>(vector); } - static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector), @@ -58,7 +56,7 @@ void register_node_type_fn_input_vector() node_type_init(&ntype, fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); - ntype.expand_in_mf_network = fn_node_vector_input_expand_in_mf_network; + ntype.build_multi_function = fn_node_vector_input_build_multi_function; ntype.draw_buttons = fn_node_input_vector_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index a3c9f44b6a1..47ec9adf6bd 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -67,10 +67,11 @@ class RandomFloatFunction : public blender::fn::MultiFunction { } }; -static void fn_node_random_float_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_random_float_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - builder.construct_and_set_matching_fn<RandomFloatFunction>(); + static RandomFloatFunction fn; + builder.set_matching_fn(fn); } void register_node_type_fn_random_float() @@ -79,6 +80,6 @@ void register_node_type_fn_random_float() fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); - ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network; + ntype.build_multi_function = fn_node_random_float_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index ffa20579acc..a3bbca90731 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -19,7 +19,6 @@ #include "DEG_depsgraph_query.h" #include "NOD_geometry_exec.hh" -#include "NOD_type_callbacks.hh" #include "NOD_type_conversions.hh" #include "node_geometry_util.hh" diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc new file mode 100644 index 00000000000..c91899ed8c2 --- /dev/null +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -0,0 +1,40 @@ +/* + * 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 "NOD_multi_function.hh" + +namespace blender::nodes { + +NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope) +{ + for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { + bNodeTree *btree = tree_ref->btree(); + for (const NodeRef *node : tree_ref->nodes()) { + bNode *bnode = node->bnode(); + if (bnode->typeinfo->build_multi_function == nullptr) { + continue; + } + NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree}; + bnode->typeinfo->build_multi_function(builder); + const MultiFunction *fn = builder.built_fn_; + if (fn != nullptr) { + map_.add_new(bnode, fn); + } + } + } +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 4be3fd2468b..528616eb23a 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -44,7 +44,6 @@ #include "MEM_guardedalloc.h" -#include "NOD_node_tree_multi_function.hh" #include "NOD_socket.h" #include "FN_cpp_type_make.hh" diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc deleted file mode 100644 index 7ab6495f733..00000000000 --- a/source/blender/nodes/intern/node_tree_multi_function.cc +++ /dev/null @@ -1,409 +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 "NOD_node_tree_multi_function.hh" -#include "NOD_type_conversions.hh" - -#include "FN_multi_function_network_evaluation.hh" - -#include "BLI_color.hh" -#include "BLI_float2.hh" -#include "BLI_float3.hh" - -namespace blender::nodes { - -const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name) -{ - Vector<fn::MFDataType, 10> input_types; - Vector<fn::MFDataType, 10> output_types; - - for (const InputSocketRef *dsocket : dnode_->inputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo()); - if (data_type.has_value()) { - input_types.append(*data_type); - } - } - } - for (const OutputSocketRef *dsocket : dnode_->outputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo()); - if (data_type.has_value()) { - output_types.append(*data_type); - } - } - } - - const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_DefaultOutput>( - name, input_types, output_types); - return fn; -} - -static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode) -{ - constexpr int stack_capacity = 10; - - Vector<fn::MFDataType, stack_capacity> input_types; - Vector<StringRef, stack_capacity> input_names; - Vector<const InputSocketRef *, stack_capacity> input_dsockets; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); - if (data_type.has_value()) { - input_types.append(*data_type); - input_names.append(dsocket->name()); - input_dsockets.append(dsocket); - } - } - } - - Vector<fn::MFDataType, stack_capacity> output_types; - Vector<StringRef, stack_capacity> output_names; - Vector<const OutputSocketRef *, stack_capacity> output_dsockets; - - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); - if (data_type.has_value()) { - output_types.append(*data_type); - output_names.append(dsocket->name()); - output_dsockets.append(dsocket); - } - } - } - - fn::MFDummyNode &dummy_node = common.network.add_dummy( - dnode->name(), input_types, output_types, input_names, output_names); - - common.network_map.add(*dnode.context(), input_dsockets, dummy_node.inputs()); - common.network_map.add(*dnode.context(), output_dsockets, dummy_node.outputs()); -} - -static bool has_data_sockets(const DNode &dnode) -{ - for (const InputSocketRef *socket : dnode->inputs()) { - if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { - return true; - } - } - for (const OutputSocketRef *socket : dnode->outputs()) { - if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { - return true; - } - } - return false; -} - -static void foreach_node_to_insert(CommonMFNetworkBuilderData &common, - FunctionRef<void(DNode)> callback) -{ - common.tree.foreach_node([&](const DNode dnode) { - if (dnode->is_group_node()) { - return; - } - /* Don't insert non-root group input/output nodes, because they will be inlined. */ - if (!dnode.context()->is_root()) { - if (dnode->is_group_input_node() || dnode->is_group_output_node()) { - return; - } - } - callback(dnode); - }); -} - -/** - * Expands all function nodes in the multi-function network. Nodes that don't have an expand - * function, but do have data sockets, will get corresponding dummy nodes. - */ -static void insert_nodes(CommonMFNetworkBuilderData &common) -{ - foreach_node_to_insert(common, [&](const DNode dnode) { - const bNodeType *node_type = dnode->typeinfo(); - if (node_type->expand_in_mf_network != nullptr) { - NodeMFNetworkBuilder builder{common, dnode}; - node_type->expand_in_mf_network(builder); - } - else if (has_data_sockets(dnode)) { - insert_dummy_node(common, dnode); - } - }); -} - -static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common, - fn::MFDataType type) -{ - const fn::MultiFunction *default_fn; - if (type.is_single()) { - default_fn = &common.scope.construct<fn::CustomMF_GenericConstant>( - AT, type.single_type(), type.single_type().default_value()); - } - else { - default_fn = &common.scope.construct<fn::CustomMF_GenericConstantArray>( - AT, fn::GSpan(type.vector_base_type())); - } - - fn::MFNode &node = common.network.add_function(*default_fn); - return node.output(0); -} - -static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common, - const DInputSocket &dsocket) -{ - BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo())); - - SocketMFNetworkBuilder builder{common, dsocket}; - socket_expand_in_mf_network(builder); - - fn::MFOutputSocket *built_socket = builder.built_socket(); - BLI_assert(built_socket != nullptr); - return built_socket; -} - -static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common) -{ - foreach_node_to_insert(common, [&](const DNode dnode) { - for (const InputSocketRef *socket_ref : dnode->inputs()) { - const DInputSocket to_dsocket{dnode.context(), socket_ref}; - if (!to_dsocket->is_available()) { - continue; - } - if (!socket_is_mf_data_socket(*to_dsocket->typeinfo())) { - continue; - } - - Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(to_dsocket); - BLI_assert(to_sockets.size() >= 1); - const fn::MFDataType to_type = to_sockets[0]->data_type(); - - Vector<DSocket> from_dsockets; - to_dsocket.foreach_origin_socket([&](DSocket socket) { from_dsockets.append(socket); }); - if (from_dsockets.size() > 1) { - fn::MFOutputSocket &from_socket = insert_default_value_for_type(common, to_type); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(from_socket, *to_socket); - } - continue; - } - if (from_dsockets.is_empty()) { - /* The socket is not linked. Need to use the value of the socket itself. */ - fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*built_socket, *to_socket); - } - continue; - } - if (from_dsockets[0]->is_input()) { - DInputSocket from_dsocket{from_dsockets[0]}; - fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*built_socket, *to_socket); - } - continue; - } - DOutputSocket from_dsocket{from_dsockets[0]}; - fn::MFOutputSocket *from_socket = &common.network_map.lookup(from_dsocket); - const fn::MFDataType from_type = from_socket->data_type(); - - if (from_type != to_type) { - const fn::MultiFunction *conversion_fn = - get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type); - if (conversion_fn != nullptr) { - fn::MFNode &node = common.network.add_function(*conversion_fn); - common.network.add_link(*from_socket, node.input(0)); - from_socket = &node.output(0); - } - else { - from_socket = &insert_default_value_for_type(common, to_type); - } - } - - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*from_socket, *to_socket); - } - } - }); -} - -/** - * Expands all function nodes contained in the given node tree within the given multi-function - * network. - * - * Returns a mapping between the original node tree and the generated nodes/sockets for further - * processing. - */ -MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, - const DerivedNodeTree &tree, - ResourceScope &scope) -{ - MFNetworkTreeMap network_map{tree, network}; - - CommonMFNetworkBuilderData common{scope, network, network_map, tree}; - - insert_nodes(common); - insert_links_and_unlinked_inputs(common); - - return network_map; -} - -/** - * A single node is allowed to expand into multiple nodes before evaluation. Depending on what - * nodes it expands to, it belongs a different type of the ones below. - */ -enum class NodeExpandType { - SingleFunctionNode, - MultipleFunctionNodes, - HasDummyNodes, -}; - -/** - * Checks how the given node expanded in the multi-function network. If it is only a single - * function node, the corresponding function is returned as well. - */ -static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map, - const DNode &dnode, - const fn::MultiFunction **r_single_function) -{ - const fn::MFFunctionNode *single_function_node = nullptr; - bool has_multiple_nodes = false; - bool has_dummy_nodes = false; - - auto check_mf_node = [&](fn::MFNode &mf_node) { - if (mf_node.is_function()) { - if (single_function_node == nullptr) { - single_function_node = &mf_node.as_function(); - } - if (&mf_node != single_function_node) { - has_multiple_nodes = true; - } - } - else { - BLI_assert(mf_node.is_dummy()); - has_dummy_nodes = true; - } - }; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - for (fn::MFInputSocket *mf_input : - network_map.lookup(DInputSocket(dnode.context(), dsocket))) { - check_mf_node(mf_input->node()); - } - } - } - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket)); - check_mf_node(mf_output.node()); - } - } - - if (has_dummy_nodes) { - return NodeExpandType::HasDummyNodes; - } - if (has_multiple_nodes) { - return NodeExpandType::MultipleFunctionNodes; - } - *r_single_function = &single_function_node->function(); - return NodeExpandType::SingleFunctionNode; -} - -static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple( - const DNode &dnode, - fn::MFNetwork &network, - MFNetworkTreeMap &network_map, - ResourceScope &scope) -{ - Vector<const fn::MFOutputSocket *> dummy_fn_inputs; - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo()); - fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type); - for (fn::MFInputSocket *mf_input : - network_map.lookup(DInputSocket(dnode.context(), dsocket))) { - network.add_link(fn_input, *mf_input); - dummy_fn_inputs.append(&fn_input); - } - } - } - Vector<const fn::MFInputSocket *> dummy_fn_outputs; - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket)); - MFDataType data_type = mf_output.data_type(); - fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type); - network.add_link(mf_output, fn_output); - dummy_fn_outputs.append(&fn_output); - } - } - - fn::MFNetworkEvaluator &fn_evaluator = scope.construct<fn::MFNetworkEvaluator>( - __func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs)); - return fn_evaluator; -} - -/** - * Returns a single multi-function for every node that supports it. This makes it easier to reuse - * the multi-function implementation of nodes in different contexts. - */ -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope) -{ - /* Build a network that nodes can insert themselves into. However, the individual nodes are not - * connected. */ - fn::MFNetwork &network = scope.construct<fn::MFNetwork>(__func__); - MFNetworkTreeMap network_map{tree, network}; - MultiFunctionByNode functions_by_node; - - CommonMFNetworkBuilderData common{scope, network, network_map, tree}; - - tree.foreach_node([&](DNode dnode) { - const bNodeType *node_type = dnode->typeinfo(); - if (node_type->expand_in_mf_network == nullptr) { - /* This node does not have a multi-function implementation. */ - return; - } - - NodeMFNetworkBuilder builder{common, dnode}; - node_type->expand_in_mf_network(builder); - - const fn::MultiFunction *single_function = nullptr; - const NodeExpandType expand_type = get_node_expand_type(network_map, dnode, &single_function); - - switch (expand_type) { - case NodeExpandType::HasDummyNodes: { - /* Dummy nodes cannot be executed, so skip them. */ - break; - } - case NodeExpandType::SingleFunctionNode: { - /* This is the common case. Most nodes just expand to a single function. */ - functions_by_node.add_new(dnode, single_function); - break; - } - case NodeExpandType::MultipleFunctionNodes: { - /* If a node expanded into multiple functions, a new function has to be created that - * combines those. */ - const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple( - dnode, network, network_map, scope); - functions_by_node.add_new(dnode, &fn); - break; - } - } - }); - - return functions_by_node; -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc deleted file mode 100644 index 881a02c92e9..00000000000 --- a/source/blender/nodes/intern/type_callbacks.cc +++ /dev/null @@ -1,60 +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 "NOD_node_tree_multi_function.hh" -#include "NOD_type_callbacks.hh" - -namespace blender::nodes { - -std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype) -{ - const CPPType *cpp_type = stype.get_base_cpp_type ? stype.get_base_cpp_type() : nullptr; - if (cpp_type != nullptr) { - return MFDataType::ForSingle(*cpp_type); - } - return {}; -} - -bool socket_is_mf_data_socket(const bNodeSocketType &stype) -{ - if (!socket_mf_type_get(stype).has_value()) { - return false; - } - if (stype.expand_in_mf_network == nullptr && stype.get_base_cpp_value == nullptr) { - return false; - } - return true; -} - -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder) -{ - bNodeSocket &socket = builder.bsocket(); - if (socket.typeinfo->expand_in_mf_network != nullptr) { - socket.typeinfo->expand_in_mf_network(builder); - } - else if (socket.typeinfo->get_base_cpp_value != nullptr) { - const CPPType &type = *socket.typeinfo->get_base_cpp_type(); - void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(), - type.alignment()); - socket.typeinfo->get_base_cpp_value(socket, buffer); - builder.set_constant_value(type, buffer); - } - else { - BLI_assert_unreachable(); - } -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index dc44f0fa98f..a75354d3381 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -72,7 +72,7 @@ #ifdef __cplusplus # include "FN_multi_function_builder.hh" -# include "NOD_node_tree_multi_function.hh" +# include "NOD_multi_function.hh" # include "BLI_color.hh" # include "BLI_float3.hh" diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 4f77421cfe0..f105f8bcaf9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -51,7 +51,7 @@ static int gpu_shader_clamp(GPUMaterial *mat, GPU_stack_link(mat, node, "clamp_range", in, out); } -static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_clamp_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{ "Clamp (Min Max)", @@ -65,7 +65,7 @@ static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuil return clamp_f(value, b, a); }}; - int clamp_type = builder.bnode().custom1; + int clamp_type = builder.node().custom1; if (clamp_type == NODE_CLAMP_MINMAX) { builder.set_matching_fn(minmax_fn); } @@ -82,7 +82,7 @@ void register_node_type_sh_clamp(void) node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); - ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network; + ntype.build_multi_function = sh_node_clamp_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index f1d5040a292..df075d6e973 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -143,9 +143,10 @@ class CurveVecFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_curve_vec_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap); @@ -162,7 +163,7 @@ void register_node_type_sh_curve_vec(void) node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec); node_type_gpu(&ntype, gpu_shader_curve_vec); - ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network; + ntype.build_multi_function = sh_node_curve_vec_build_multi_function; nodeRegisterType(&ntype); } @@ -317,9 +318,10 @@ class CurveRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_curve_rgb_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap); @@ -336,7 +338,7 @@ void register_node_type_sh_curve_rgb(void) node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb); node_type_gpu(&ntype, gpu_shader_curve_rgb); - ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_curve_rgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index ad7abd9d491..e4739e2864d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -261,9 +261,10 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction { } }; -static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_map_range_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); bool clamp = bnode.custom1 != 0; int interpolation_type = bnode.custom2; @@ -301,7 +302,6 @@ static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetwork break; } default: - builder.set_not_implemented(); break; } } @@ -315,7 +315,7 @@ void register_node_type_sh_map_range(void) node_type_init(&ntype, node_shader_init_map_range); node_type_update(&ntype, node_shader_update_map_range); node_type_gpu(&ntype, gpu_shader_map_range); - ntype.expand_in_mf_network = sh_node_map_range_expand_in_mf_network; + ntype.build_multi_function = sh_node_map_range_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 7a846031456..8e20a922c92 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -69,11 +69,9 @@ static int gpu_shader_math(GPUMaterial *mat, return 0; } -static const blender::fn::MultiFunction &get_base_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) { - const int mode = builder.bnode().custom1; - + const int mode = node.custom1; const blender::fn::MultiFunction *base_fn = nullptr; blender::nodes::try_dispatch_float_math_fl_to_fl( @@ -82,7 +80,7 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } blender::nodes::try_dispatch_float_math_fl_fl_to_fl( @@ -92,7 +90,7 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl( @@ -102,36 +100,22 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } - return builder.get_not_implemented_fn(); + return nullptr; } -static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_math_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &base_function = get_base_multi_function(builder); - - const blender::nodes::DNode &dnode = builder.dnode(); - blender::fn::MFNetwork &network = builder.network(); - blender::fn::MFFunctionNode &base_node = network.add_function(base_function); - - builder.network_map().add_try_match(*dnode.context(), dnode->inputs(), base_node.inputs()); + const blender::fn::MultiFunction *base_function = get_base_multi_function(builder.node()); - const bool clamp_output = builder.bnode().custom2 != 0; + const bool clamp_output = builder.node().custom2 != 0; if (clamp_output) { - static blender::fn::CustomMF_SI_SO<float, float> clamp_fn{"Clamp", [](float value) { - CLAMP(value, 0.0f, 1.0f); - return value; - }}; - blender::fn::MFFunctionNode &clamp_node = network.add_function(clamp_fn); - network.add_link(base_node.output(0), clamp_node.input(0)); - builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)), - clamp_node.output(0)); + /* TODO */ } else { - builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)), - base_node.output(0)); + builder.set_matching_fn(base_function); } } @@ -144,7 +128,7 @@ void register_node_type_sh_math(void) node_type_label(&ntype, node_math_label); node_type_gpu(&ntype, gpu_shader_math); node_type_update(&ntype, node_math_update); - ntype.expand_in_mf_network = sh_node_math_expand_in_mf_network; + ntype.build_multi_function = sh_node_math_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index 47011caeeb6..ade35a40366 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -174,9 +174,9 @@ class MixRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_mix_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_mix_rgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &node = builder.bnode(); + bNode &node = builder.node(); bool clamp = node.custom2 & SHD_MIXRGB_CLAMP; int mix_type = node.custom1; builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type); @@ -191,7 +191,7 @@ void register_node_type_sh_mix_rgb(void) node_type_label(&ntype, node_blend_label); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); node_type_gpu(&ntype, gpu_shader_mix_rgb); - ntype.expand_in_mf_network = sh_node_mix_rgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_mix_rgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index a7239154633..2779fc6bf68 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -96,7 +96,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_seprgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_seprgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static SeparateRGBFunction fn; builder.set_matching_fn(fn); @@ -110,7 +110,7 @@ void register_node_type_sh_seprgb(void) node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb); node_type_gpu(&ntype, gpu_shader_seprgb); - ntype.expand_in_mf_network = sh_node_seprgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_seprgb_build_multi_function; nodeRegisterType(&ntype); } @@ -153,7 +153,7 @@ static int gpu_shader_combrgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_rgb", in, out); } -static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_combrgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{ "Combine RGB", @@ -169,7 +169,7 @@ void register_node_type_sh_combrgb(void) node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb); node_type_gpu(&ntype, gpu_shader_combrgb); - ntype.expand_in_mf_network = sh_node_combrgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_combrgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index efa9581c414..1fd794cdd0a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -81,7 +81,7 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction { } }; -static void sh_node_sepxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_sepxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static MF_SeparateXYZ separate_fn; builder.set_matching_fn(separate_fn); @@ -94,7 +94,7 @@ void register_node_type_sh_sepxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out); node_type_gpu(&ntype, gpu_shader_sepxyz); - ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network; + ntype.build_multi_function = sh_node_sepxyz_build_multi_function; nodeRegisterType(&ntype); } @@ -120,7 +120,7 @@ static int gpu_shader_combxyz(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_xyz", in, out); } -static void sh_node_combxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_combxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::float3> fn{ "Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }}; @@ -134,7 +134,7 @@ void register_node_type_sh_combxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out); node_type_gpu(&ntype, gpu_shader_combxyz); - ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network; + ntype.build_multi_function = sh_node_combxyz_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 5b2eb300aac..1bc42ab0cc6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -163,9 +163,10 @@ class ColorBandFunction : public blender::fn::MultiFunction { } }; -static void sh_node_valtorgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_valtorgb_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); const ColorBand *color_band = (const ColorBand *)bnode.storage; builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band); } @@ -181,7 +182,7 @@ void register_node_type_sh_valtorgb(void) node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_valtorgb); node_type_gpu(&ntype, gpu_shader_valtorgb); - ntype.expand_in_mf_network = sh_node_valtorgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_valtorgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 495c8d12824..602d5a1cf56 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -39,9 +39,9 @@ static int gpu_shader_value(GPUMaterial *mat, return GPU_stack_link(mat, node, "set_value", in, out, link); } -static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_value_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - const bNodeSocket *bsocket = builder.dnode()->output(0).bsocket(); + const bNodeSocket *bsocket = (bNodeSocket *)builder.node().outputs.first; const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value; builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value); } @@ -53,7 +53,7 @@ void register_node_type_sh_value(void) sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); node_type_socket_templates(&ntype, nullptr, sh_node_value_out); node_type_gpu(&ntype, gpu_shader_value); - ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network; + ntype.build_multi_function = sh_node_value_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 419a11201aa..4424db6aed1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -183,12 +183,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node } } -static const blender::fn::MultiFunction &get_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { using blender::float3; - NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1); + NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1); const blender::fn::MultiFunction *multi_fn = nullptr; @@ -199,7 +198,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3( @@ -209,7 +208,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3( @@ -219,7 +218,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl( @@ -229,7 +228,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3( @@ -239,7 +238,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_to_fl3( @@ -248,7 +247,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_to_fl( @@ -257,15 +256,16 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } - return builder.get_not_implemented_fn(); + return nullptr; } -static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_vector_math_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -278,7 +278,7 @@ void register_node_type_sh_vect_math(void) node_type_label(&ntype, node_vector_math_label); node_type_gpu(&ntype, gpu_shader_vector_math); node_type_update(&ntype, node_shader_update_vector_math); - ntype.expand_in_mf_network = sh_node_vector_math_expand_in_mf_network; + ntype.build_multi_function = sh_node_vector_math_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index 3b2c2fa5a03..bc51b7e29ea 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -100,11 +100,10 @@ static float3 sh_node_vector_rotate_euler(const float3 vector, return result + center; } -static const blender::fn::MultiFunction &get_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { - bool invert = builder.bnode().custom2; - const int mode = builder.bnode().custom1; + bool invert = node.custom2; + const int mode = node.custom1; switch (mode) { case NODE_VECTOR_ROTATE_TYPE_AXIS: { @@ -113,13 +112,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_X: { float3 axis = float3(1.0f, 0.0f, 0.0f); @@ -128,13 +127,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: { float3 axis = float3(0.0f, 1.0f, 0.0f); @@ -143,13 +142,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: { float3 axis = float3(0.0f, 0.0f, 1.0f); @@ -158,13 +157,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: { if (invert) { @@ -172,24 +171,24 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, true); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, false); }}; - return fn; + return &fn; } default: BLI_assert_unreachable(); - return builder.get_not_implemented_fn(); + return nullptr; } } -static void sh_node_vector_rotate_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_vector_rotate_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -211,7 +210,7 @@ void register_node_type_sh_vector_rotate(void) node_type_socket_templates(&ntype, sh_node_vector_rotate_in, sh_node_vector_rotate_out); node_type_gpu(&ntype, gpu_shader_vector_rotate); node_type_update(&ntype, node_shader_update_vector_rotate); - ntype.expand_in_mf_network = sh_node_vector_rotate_expand_in_mf_network; + ntype.build_multi_function = sh_node_vector_rotate_build_multi_function; nodeRegisterType(&ntype); } |