Functions: remove multi-function network

The multi-function network system was able to compose multiple
multi-functions into a new one and to evaluate that efficiently.
This functionality was heavily used by the particle nodes prototype
a year ago. However, since then we only used multi-functions
without the need to compose them in geometry nodes.

The upcoming "fields" in geometry nodes will need a way to
compose multi-functions again. Unfortunately, the code removed
in this commit was not ideal for this different kind of function
composition. I've been working on an alternative that will be added
separately when it becomes needed.

I've had to update all the function nodes, because their interface
depended on the multi-function network data structure a bit.
The actual multi-function implementations are still the same though.
This commit is contained in:
Jacques Lucke 2021-08-20 13:14:39 +02:00
parent 7d8c71e800
commit 0081200812
44 changed files with 514 additions and 3953 deletions

View File

@ -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;

View File

@ -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"

View File

@ -33,9 +33,6 @@ set(SRC
intern/generic_virtual_vector_array.cc
intern/multi_function.cc
intern/multi_function_builder.cc
intern/multi_function_network.cc
intern/multi_function_network_evaluation.cc
intern/multi_function_network_optimization.cc
FN_cpp_type.hh
FN_cpp_type_make.hh
@ -49,9 +46,6 @@ set(SRC
FN_multi_function_builder.hh
FN_multi_function_context.hh
FN_multi_function_data_type.hh
FN_multi_function_network.hh
FN_multi_function_network_evaluation.hh
FN_multi_function_network_optimization.hh
FN_multi_function_param_type.hh
FN_multi_function_params.hh
FN_multi_function_signature.hh
@ -68,7 +62,6 @@ if(WITH_GTESTS)
tests/FN_cpp_type_test.cc
tests/FN_generic_span_test.cc
tests/FN_generic_vector_array_test.cc
tests/FN_multi_function_network_test.cc
tests/FN_multi_function_test.cc
)
set(TEST_LIB

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -160,6 +160,21 @@ class MFSignatureBuilder {
}
}
void add(StringRef name, const MFParamType &param_type)
{
switch (param_type.interface_type()) {
case MFParamType::Input:
this->input(name, param_type.data_type());
break;
case MFParamType::Mutable:
this->mutable_(name, param_type.data_type());
break;
case MFParamType::Output:
this->output(name, param_type.data_type());
break;
}
}
/* Context */
/** This indicates that the function accesses the context. This disables optimizations that

View File

@ -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

View File

@ -1,501 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup fn
*/
/* Used to check if two multi-functions have the exact same type. */
#include <typeinfo>
#include "FN_multi_function_builder.hh"
#include "FN_multi_function_network_evaluation.hh"
#include "FN_multi_function_network_optimization.hh"
#include "BLI_disjoint_set.hh"
#include "BLI_ghash.h"
#include "BLI_map.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_rand.h"
#include "BLI_stack.hh"
namespace blender::fn::mf_network_optimization {
/* -------------------------------------------------------------------- */
/** \name Utility functions to find nodes in a network.
* \{ */
static bool set_tag_and_check_if_modified(bool &tag, bool new_value)
{
if (tag != new_value) {
tag = new_value;
return true;
}
return false;
}
static Array<bool> mask_nodes_to_the_left(MFNetwork &network, Span<MFNode *> nodes)
{
Array<bool> is_to_the_left(network.node_id_amount(), false);
Stack<MFNode *> nodes_to_check;
for (MFNode *node : nodes) {
is_to_the_left[node->id()] = true;
nodes_to_check.push(node);
}
while (!nodes_to_check.is_empty()) {
MFNode &node = *nodes_to_check.pop();
for (MFInputSocket *input_socket : node.inputs()) {
MFOutputSocket *origin = input_socket->origin();
if (origin != nullptr) {
MFNode &origin_node = origin->node();
if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) {
nodes_to_check.push(&origin_node);
}
}
}
}
return is_to_the_left;
}
static Array<bool> mask_nodes_to_the_right(MFNetwork &network, Span<MFNode *> nodes)
{
Array<bool> is_to_the_right(network.node_id_amount(), false);
Stack<MFNode *> nodes_to_check;
for (MFNode *node : nodes) {
is_to_the_right[node->id()] = true;
nodes_to_check.push(node);
}
while (!nodes_to_check.is_empty()) {
MFNode &node = *nodes_to_check.pop();
for (MFOutputSocket *output_socket : node.outputs()) {
for (MFInputSocket *target_socket : output_socket->targets()) {
MFNode &target_node = target_socket->node();
if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) {
nodes_to_check.push(&target_node);
}
}
}
}
return is_to_the_right;
}
static Vector<MFNode *> find_nodes_based_on_mask(MFNetwork &network,
Span<bool> id_mask,
bool mask_value)
{
Vector<MFNode *> nodes;
for (int id : id_mask.index_range()) {
if (id_mask[id] == mask_value) {
MFNode *node = network.node_or_null_by_id(id);
if (node != nullptr) {
nodes.append(node);
}
}
}
return nodes;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dead Node Removal
* \{ */
/**
* Unused nodes are all those nodes that no dummy node depends upon.
*/
void dead_node_removal(MFNetwork &network)
{
Array<bool> node_is_used_mask = mask_nodes_to_the_left(network,
network.dummy_nodes().cast<MFNode *>());
Vector<MFNode *> nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false);
network.remove(nodes_to_remove);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Constant Folding
* \{ */
static bool function_node_can_be_constant(MFFunctionNode *node)
{
if (node->has_unlinked_inputs()) {
return false;
}
if (node->function().depends_on_context()) {
return false;
}
return true;
}
static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network)
{
Vector<MFNode *> non_constant_nodes;
non_constant_nodes.extend(network.dummy_nodes().cast<MFNode *>());
for (MFFunctionNode *node : network.function_nodes()) {
if (!function_node_can_be_constant(node)) {
non_constant_nodes.append(node);
}
}
return non_constant_nodes;
}
static bool output_has_non_constant_target_node(MFOutputSocket *output_socket,
Span<bool> is_not_constant_mask)
{
for (MFInputSocket *target_socket : output_socket->targets()) {
MFNode &target_node = target_socket->node();
bool target_is_not_constant = is_not_constant_mask[target_node.id()];
if (target_is_not_constant) {
return true;
}
}
return false;
}
static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket)
{
for (MFInputSocket *target_socket : output_socket->targets()) {
if (target_socket->node().is_dummy()) {
return target_socket;
}
}
return nullptr;
}
static Vector<MFInputSocket *> find_constant_inputs_to_fold(
MFNetwork &network, Vector<MFDummyNode *> &r_temporary_nodes)
{
Vector<MFNode *> non_constant_nodes = find_non_constant_nodes(network);
Array<bool> is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes);
Vector<MFNode *> constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false);
Vector<MFInputSocket *> sockets_to_compute;
for (MFNode *node : constant_nodes) {
if (node->inputs().size() == 0) {
continue;
}
for (MFOutputSocket *output_socket : node->outputs()) {
MFDataType data_type = output_socket->data_type();
if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) {
MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket);
if (dummy_target == nullptr) {
dummy_target = &network.add_output("Dummy", data_type);
network.add_link(*output_socket, *dummy_target);
r_temporary_nodes.append(&dummy_target->node().as_dummy());
}
sockets_to_compute.append(dummy_target);
}
}
}
return sockets_to_compute;
}
static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
MFParamsBuilder &params,
ResourceScope &scope)
{
for (int param_index : network_fn.param_indices()) {
MFParamType param_type = network_fn.param_type(param_index);
MFDataType data_type = param_type.data_type();
switch (data_type.category()) {
case MFDataType::Single: {
/* Allocates memory for a single constant folded value. */
const CPPType &cpp_type = data_type.single_type();
void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment());
GMutableSpan array{cpp_type, buffer, 1};
params.add_uninitialized_single_output(array);
break;
}
case MFDataType::Vector: {
/* Allocates memory for a constant folded vector. */
const CPPType &cpp_type = data_type.vector_base_type();
GVectorArray &vector_array = scope.construct<GVectorArray>(AT, cpp_type, 1);
params.add_vector_output(vector_array);
break;
}
}
}
}
static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn,
MFParamsBuilder &params,
ResourceScope &scope,
MFNetwork &network)
{
Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr};
for (int param_index : network_fn.param_indices()) {
MFParamType param_type = network_fn.param_type(param_index);
MFDataType data_type = param_type.data_type();
const MultiFunction *constant_fn = nullptr;
switch (data_type.category()) {
case MFDataType::Single: {
const CPPType &cpp_type = data_type.single_type();
GMutableSpan array = params.computed_array(param_index);
void *buffer = array.data();
scope.add(buffer, array.type().destruct_fn(), AT);
constant_fn = &scope.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
break;
}
case MFDataType::Vector: {
GVectorArray &vector_array = params.computed_vector_array(param_index);
GSpan array = vector_array[0];
constant_fn = &scope.construct<CustomMF_GenericConstantArray>(AT, array);
break;
}
}
MFFunctionNode &folded_node = network.add_function(*constant_fn);
folded_sockets[param_index] = &folded_node.output(0);
}
return folded_sockets;
}
static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes(
MFNetwork &network, Span<const MFInputSocket *> sockets_to_compute, ResourceScope &scope)
{
MFNetworkEvaluator network_fn{{}, sockets_to_compute};
MFContextBuilder context;
MFParamsBuilder params{network_fn, 1};
prepare_params_for_constant_folding(network_fn, params, scope);
network_fn.call({0}, params, context);
return add_constant_folded_sockets(network_fn, params, scope, network);
}
class MyClass {
MFDummyNode node;
};
/**
* Find function nodes that always output the same value and replace those with constant nodes.
*/
void constant_folding(MFNetwork &network, ResourceScope &scope)
{
Vector<MFDummyNode *> temporary_nodes;
Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes);
if (inputs_to_fold.size() == 0) {
return;
}
Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes(
network, inputs_to_fold, scope);
for (int i : inputs_to_fold.index_range()) {
MFOutputSocket &original_socket = *inputs_to_fold[i]->origin();
network.relink(original_socket, *folded_sockets[i]);
}
network.remove(temporary_nodes.as_span().cast<MFNode *>());
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Common Sub-network Elimination
* \{ */
static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint64_t> node_hashes)
{
if (node.function().depends_on_context()) {
return BLI_rng_get_uint(rng);
}
if (node.has_unlinked_inputs()) {
return BLI_rng_get_uint(rng);
}
uint64_t combined_inputs_hash = 394659347u;
for (MFInputSocket *input_socket : node.inputs()) {
MFOutputSocket *origin_socket = input_socket->origin();
uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()],
origin_socket->index());
combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash);
}
uint64_t function_hash = node.function().hash();
uint64_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash);
return node_hash;
}
/**
* Produces a hash for every node. Two nodes with the same hash should have a high probability of
* outputting the same values.
*/
static Array<uint64_t> compute_node_hashes(MFNetwork &network)
{
RNG *rng = BLI_rng_new(0);
Array<uint64_t> node_hashes(network.node_id_amount());
Array<bool> node_is_hashed(network.node_id_amount(), false);
/* No dummy nodes are not assumed to output the same values. */
for (MFDummyNode *node : network.dummy_nodes()) {
uint64_t node_hash = BLI_rng_get_uint(rng);
node_hashes[node->id()] = node_hash;
node_is_hashed[node->id()] = true;
}
Stack<MFFunctionNode *> nodes_to_check;
nodes_to_check.push_multiple(network.function_nodes());
while (!nodes_to_check.is_empty()) {
MFFunctionNode &node = *nodes_to_check.peek();
if (node_is_hashed[node.id()]) {
nodes_to_check.pop();
continue;
}
/* Make sure that origin nodes are hashed first. */
bool all_dependencies_ready = true;
for (MFInputSocket *input_socket : node.inputs()) {
MFOutputSocket *origin_socket = input_socket->origin();
if (origin_socket != nullptr) {
MFNode &origin_node = origin_socket->node();
if (!node_is_hashed[origin_node.id()]) {
all_dependencies_ready = false;
nodes_to_check.push(&origin_node.as_function());
}
}
}
if (!all_dependencies_ready) {
continue;
}
uint64_t node_hash = compute_node_hash(node, rng, node_hashes);
node_hashes[node.id()] = node_hash;
node_is_hashed[node.id()] = true;
nodes_to_check.pop();
}
BLI_rng_free(rng);
return node_hashes;
}
static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network,
Span<uint64_t> node_hashes)
{
MultiValueMap<uint64_t, MFNode *> nodes_by_hash;
for (int id : IndexRange(network.node_id_amount())) {
MFNode *node = network.node_or_null_by_id(id);
if (node != nullptr) {
uint64_t node_hash = node_hashes[id];
nodes_by_hash.add(node_hash, node);
}
}
return nodes_by_hash;
}
static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b)
{
if (&a == &b) {
return true;
}
if (typeid(a) == typeid(b)) {
return a.equals(b);
}
return false;
}
static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b)
{
if (cache.in_same_set(a.id(), b.id())) {
return true;
}
if (a.is_dummy() || b.is_dummy()) {
return false;
}
if (!functions_are_equal(a.as_function().function(), b.as_function().function())) {
return false;
}
for (int i : a.inputs().index_range()) {
const MFOutputSocket *origin_a = a.input(i).origin();
const MFOutputSocket *origin_b = b.input(i).origin();
if (origin_a == nullptr || origin_b == nullptr) {
return false;
}
if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) {
return false;
}
}
cache.join(a.id(), b.id());
return true;
}
static void relink_duplicate_nodes(MFNetwork &network,
MultiValueMap<uint64_t, MFNode *> &nodes_by_hash)
{
DisjointSet same_node_cache{network.node_id_amount()};
for (Span<MFNode *> nodes_with_same_hash : nodes_by_hash.values()) {
if (nodes_with_same_hash.size() <= 1) {
continue;
}
Vector<MFNode *, 16> nodes_to_check = nodes_with_same_hash;
while (nodes_to_check.size() >= 2) {
Vector<MFNode *, 16> remaining_nodes;
MFNode &deduplicated_node = *nodes_to_check[0];
for (MFNode *node : nodes_to_check.as_span().drop_front(1)) {
/* This is true with fairly high probability, but hash collisions can happen. So we have to
* check if the node actually output the same values. */
if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) {
for (int i : deduplicated_node.outputs().index_range()) {
network.relink(node->output(i), deduplicated_node.output(i));
}
}
else {
remaining_nodes.append(node);
}
}
nodes_to_check = std::move(remaining_nodes);
}
}
}
/**
* Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node
* groups were used to create the network.
*/
void common_subnetwork_elimination(MFNetwork &network)
{
Array<uint64_t> node_hashes = compute_node_hashes(network);
MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes);
relink_duplicate_nodes(network, nodes_by_hash);
}
/** \} */
} // namespace blender::fn::mf_network_optimization

View File

@ -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

View File

@ -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>());

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,130 @@
/*
* 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;
/**
* Utility class to help nodes build a multi-function for themselves.
*/
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);
/**
* Assign a multi-function for the current node. The input and output parameters of the function
* have to match the available sockets in the node.
*/
void set_matching_fn(const MultiFunction *fn);
void set_matching_fn(const MultiFunction &fn);
/**
* Utility method for creating and assigning a multi-function when it can't have a static
* lifetime.
*/
template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args);
bNode &node();
bNodeTree &tree();
ResourceScope &resource_scope();
};
/**
* Gives access to multi-functions for all nodes in a node tree that support them.
*/
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

View File

@ -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

View File

@ -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"

View File

@ -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 &not_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);
}

View File

@ -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 &not_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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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"

View File

@ -14,21 +14,27 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <optional>
#include "BKE_node.h"
#include "FN_multi_function_data_type.hh"
#include "NOD_multi_function.hh"
namespace blender::nodes {
using fn::CPPType;
using fn::MFDataType;
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);
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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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,51 @@ 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)
class ClampWrapperFunction : public blender::fn::MultiFunction {
private:
const blender::fn::MultiFunction &fn_;
public:
ClampWrapperFunction(const blender::fn::MultiFunction &fn) : fn_(fn)
{
this->set_signature(&fn.signature());
}
void call(blender::IndexMask mask,
blender::fn::MFParams params,
blender::fn::MFContext context) const override
{
fn_.call(mask, params, context);
/* Assumes the output parameter is the last one. */
const int output_param_index = this->param_amount() - 1;
/* This has actually been initialized in the call above. */
blender::MutableSpan<float> results = params.uninitialized_single_output<float>(
output_param_index);
for (const int i : mask) {
float &value = results[i];
CLAMP(value, 0.0f, 1.0f);
}
}
};
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::fn::MultiFunction *base_function = get_base_multi_function(builder.node());
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 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));
builder.construct_and_set_matching_fn<ClampWrapperFunction>(*base_function);
}
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 +157,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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}