Nodes: refactor derived node tree

This is a complete rewrite of the derived node tree data structure.
It is a much thinner abstraction about `NodeTreeRef` than before.
This gives the user of the derived node tree more control and allows
for greater introspection capabilities (e.g. before muted nodes were
completely abstracted away; this was convenient, but came with
limitations).

Another nice benefit of the new structure is that it is much cheaper
to build, because it does not inline all nodes and sockets in nested
node groups.

Differential Revision: https://developer.blender.org/D10620
This commit is contained in:
Jacques Lucke 2021-03-06 16:51:06 +01:00
parent cfd766cebd
commit d2869943d2
11 changed files with 788 additions and 1254 deletions

View File

@ -77,6 +77,7 @@
#include "NOD_type_callbacks.hh"
using blender::float3;
using blender::FunctionRef;
using blender::IndexRange;
using blender::Map;
using blender::Set;
@ -90,8 +91,8 @@ using blender::bke::PersistentObjectHandle;
using blender::fn::GMutablePointer;
using blender::fn::GValueMap;
using blender::nodes::GeoNodeExecParams;
using namespace blender::nodes::derived_node_tree_types;
using namespace blender::fn::multi_function_types;
using namespace blender::nodes::derived_node_tree_types;
static void initData(ModifierData *md)
{
@ -254,8 +255,8 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
class GeometryNodesEvaluator {
private:
blender::LinearAllocator<> allocator_;
Map<std::pair<const DInputSocket *, const DOutputSocket *>, GMutablePointer> value_by_input_;
Vector<const DInputSocket *> group_outputs_;
Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_;
Vector<DInputSocket> group_outputs_;
blender::nodes::MultiFunctionByNode &mf_by_node_;
const blender::nodes::DataTypeConversions &conversions_;
const PersistentDataHandleMap &handle_map_;
@ -264,8 +265,8 @@ class GeometryNodesEvaluator {
Depsgraph *depsgraph_;
public:
GeometryNodesEvaluator(const Map<const DOutputSocket *, GMutablePointer> &group_input_data,
Vector<const DInputSocket *> group_outputs,
GeometryNodesEvaluator(const Map<DOutputSocket, GMutablePointer> &group_input_data,
Vector<DInputSocket> group_outputs,
blender::nodes::MultiFunctionByNode &mf_by_node,
const PersistentDataHandleMap &handle_map,
const Object *self_object,
@ -280,15 +281,15 @@ class GeometryNodesEvaluator {
depsgraph_(depsgraph)
{
for (auto item : group_input_data.items()) {
this->forward_to_inputs(*item.key, item.value);
this->forward_to_inputs(item.key, item.value);
}
}
Vector<GMutablePointer> execute()
{
Vector<GMutablePointer> results;
for (const DInputSocket *group_output : group_outputs_) {
Vector<GMutablePointer> result = this->get_input_values(*group_output);
for (const DInputSocket &group_output : group_outputs_) {
Vector<GMutablePointer> result = this->get_input_values(group_output);
results.append(result[0]);
}
for (GMutablePointer value : value_by_input_.values()) {
@ -298,62 +299,63 @@ class GeometryNodesEvaluator {
}
private:
Vector<GMutablePointer> get_input_values(const DInputSocket &socket_to_compute)
Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute)
{
Span<const DOutputSocket *> from_sockets = socket_to_compute.linked_sockets();
Span<const DGroupInput *> from_group_inputs = socket_to_compute.linked_group_inputs();
const int total_inputs = from_sockets.size() + from_group_inputs.size();
if (total_inputs == 0) {
/* The input is not connected, use the value from the socket itself. */
return {get_unlinked_input_value(socket_to_compute)};
}
if (from_group_inputs.size() == 1) {
return {get_unlinked_input_value(socket_to_compute)};
}
Vector<DSocket> from_sockets;
socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); });
/* Multi-input sockets contain a vector of inputs. */
if (socket_to_compute.is_multi_input_socket()) {
if (socket_to_compute->is_multi_input_socket()) {
Vector<GMutablePointer> values;
for (const DOutputSocket *from_socket : from_sockets) {
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
&socket_to_compute, from_socket);
std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
if (value.has_value()) {
values.append(*value);
}
else {
this->compute_output_and_forward(*from_socket);
GMutablePointer value = value_by_input_.pop(key);
values.append(value);
}
for (const DSocket from_socket : from_sockets) {
GMutablePointer value = get_input_from_incoming_link(socket_to_compute, from_socket);
values.append(value);
}
return values;
}
const DOutputSocket &from_socket = *from_sockets[0];
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
&socket_to_compute, &from_socket);
std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
if (value.has_value()) {
/* This input has been computed before, return it directly. */
return {*value};
if (from_sockets.is_empty()) {
/* The input is not connected, use the value from the socket itself. */
const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
return {get_unlinked_input_value(socket_to_compute, type)};
}
/* Compute the socket now. */
this->compute_output_and_forward(from_socket);
return {value_by_input_.pop(key)};
const DSocket from_socket = from_sockets[0];
GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket);
return {value};
}
void compute_output_and_forward(const DOutputSocket &socket_to_compute)
GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute,
const DSocket from_socket)
{
const DNode &node = socket_to_compute.node();
if (from_socket->is_output()) {
const DOutputSocket from_output_socket{from_socket};
const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute,
from_output_socket);
std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
if (value.has_value()) {
/* This input has been computed before, return it directly. */
return {*value};
}
if (!socket_to_compute.is_available()) {
/* Compute the socket now. */
this->compute_output_and_forward(from_output_socket);
return {value_by_input_.pop(key)};
}
/* Get value from an unlinked input socket. */
const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
const DInputSocket from_input_socket{from_socket};
return {get_unlinked_input_value(from_input_socket, type)};
}
void compute_output_and_forward(const DOutputSocket socket_to_compute)
{
const DNode node{socket_to_compute.context(), &socket_to_compute->node()};
if (!socket_to_compute->is_available()) {
/* If the output is not available, use a default value. */
const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute.typeinfo());
const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
void *buffer = allocator_.allocate(type.size(), type.alignment());
type.copy_to_uninitialized(type.default_value(), buffer);
this->forward_to_inputs(socket_to_compute, {type, buffer});
@ -362,9 +364,9 @@ class GeometryNodesEvaluator {
/* Prepare inputs required to execute the node. */
GValueMap<StringRef> node_inputs_map{allocator_};
for (const DInputSocket *input_socket : node.inputs()) {
for (const InputSocketRef *input_socket : node->inputs()) {
if (input_socket->is_available()) {
Vector<GMutablePointer> values = this->get_input_values(*input_socket);
Vector<GMutablePointer> values = this->get_input_values({node.context(), input_socket});
for (int i = 0; i < values.size(); ++i) {
/* Values from Multi Input Sockets are stored in input map with the format
* <identifier>[<index>]. */
@ -382,15 +384,15 @@ class GeometryNodesEvaluator {
this->execute_node(node, params);
/* Forward computed outputs to linked input sockets. */
for (const DOutputSocket *output_socket : node.outputs()) {
for (const OutputSocketRef *output_socket : node->outputs()) {
if (output_socket->is_available()) {
GMutablePointer value = node_outputs_map.extract(output_socket->identifier());
this->forward_to_inputs(*output_socket, value);
this->forward_to_inputs({node.context(), output_socket}, value);
}
}
}
void execute_node(const DNode &node, GeoNodeExecParams params)
void execute_node(const DNode node, GeoNodeExecParams params)
{
const bNode &bnode = params.node();
@ -403,7 +405,7 @@ class GeometryNodesEvaluator {
}
/* Use the multi-function implementation if it exists. */
const MultiFunction *multi_function = mf_by_node_.lookup_default(&node, nullptr);
const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr);
if (multi_function != nullptr) {
this->execute_multi_function_node(node, params, *multi_function);
return;
@ -413,51 +415,52 @@ class GeometryNodesEvaluator {
this->execute_unknown_node(node, params);
}
void store_ui_hints(const DNode &node, GeoNodeExecParams params) const
void store_ui_hints(const DNode node, GeoNodeExecParams params) const
{
for (const DInputSocket *dsocket : node.inputs()) {
if (!dsocket->is_available()) {
for (const InputSocketRef *socket_ref : node->inputs()) {
if (!socket_ref->is_available()) {
continue;
}
if (dsocket->bsocket()->type != SOCK_GEOMETRY) {
if (socket_ref->bsocket()->type != SOCK_GEOMETRY) {
continue;
}
bNodeTree *btree_cow = node.node_ref().tree().btree();
bNodeTree *btree_cow = node->btree();
bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
const NodeTreeEvaluationContext context(*self_object_, *modifier_);
const GeometrySet &geometry_set = params.get_input<GeometrySet>(dsocket->identifier());
const GeometrySet &geometry_set = params.get_input<GeometrySet>(socket_ref->identifier());
const Vector<const GeometryComponent *> components = geometry_set.get_components_for_read();
for (const GeometryComponent *component : components) {
component->attribute_foreach([&](StringRefNull attribute_name,
const AttributeMetaData &UNUSED(meta_data)) {
BKE_nodetree_attribute_hint_add(*btree_original, context, *node.bnode(), attribute_name);
return true;
});
component->attribute_foreach(
[&](StringRefNull attribute_name, const AttributeMetaData &UNUSED(meta_data)) {
BKE_nodetree_attribute_hint_add(
*btree_original, context, *node->bnode(), attribute_name);
return true;
});
}
}
}
void execute_multi_function_node(const DNode &node,
void execute_multi_function_node(const DNode node,
GeoNodeExecParams params,
const MultiFunction &fn)
{
MFContextBuilder fn_context;
MFParamsBuilder fn_params{fn, 1};
Vector<GMutablePointer> input_data;
for (const DInputSocket *dsocket : node.inputs()) {
if (dsocket->is_available()) {
GMutablePointer data = params.extract_input(dsocket->identifier());
for (const InputSocketRef *socket_ref : node->inputs()) {
if (socket_ref->is_available()) {
GMutablePointer data = params.extract_input(socket_ref->identifier());
fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1));
input_data.append(data);
}
}
Vector<GMutablePointer> output_data;
for (const DOutputSocket *dsocket : node.outputs()) {
if (dsocket->is_available()) {
const CPPType &type = *blender::nodes::socket_cpp_type_get(*dsocket->typeinfo());
for (const OutputSocketRef *socket_ref : node->outputs()) {
if (socket_ref->is_available()) {
const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo());
void *buffer = allocator_.allocate(type.size(), type.alignment());
fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1));
output_data.append(GMutablePointer(type, buffer));
@ -468,19 +471,19 @@ class GeometryNodesEvaluator {
value.destruct();
}
int output_index = 0;
for (const int i : node.outputs().index_range()) {
if (node.output(i).is_available()) {
for (const int i : node->outputs().index_range()) {
if (node->output(i).is_available()) {
GMutablePointer value = output_data[output_index];
params.set_output_by_move(node.output(i).identifier(), value);
params.set_output_by_move(node->output(i).identifier(), value);
value.destruct();
output_index++;
}
}
}
void execute_unknown_node(const DNode &node, GeoNodeExecParams params)
void execute_unknown_node(const DNode node, GeoNodeExecParams params)
{
for (const DOutputSocket *socket : node.outputs()) {
for (const OutputSocketRef *socket : node->outputs()) {
if (socket->is_available()) {
const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
params.set_output_by_copy(socket->identifier(), {type, type.default_value()});
@ -488,17 +491,18 @@ class GeometryNodesEvaluator {
}
}
void forward_to_inputs(const DOutputSocket &from_socket, GMutablePointer value_to_forward)
void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward)
{
/* For all sockets that are linked with the from_socket push the value to their node. */
Span<const DInputSocket *> to_sockets_all = from_socket.linked_sockets();
Vector<DInputSocket> to_sockets_all;
from_socket.foreach_target_socket(
[&](DInputSocket to_socket) { to_sockets_all.append(to_socket); });
const CPPType &from_type = *value_to_forward.type();
Vector<const DInputSocket *> to_sockets_same_type;
for (const DInputSocket *to_socket : to_sockets_all) {
Vector<DInputSocket> to_sockets_same_type;
for (const DInputSocket &to_socket : to_sockets_all) {
const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
to_socket, &from_socket);
const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
if (from_type == to_type) {
to_sockets_same_type.append(to_socket);
}
@ -520,23 +524,21 @@ class GeometryNodesEvaluator {
}
else if (to_sockets_same_type.size() == 1) {
/* This value is only used on one input socket, no need to copy it. */
const DInputSocket *to_socket = to_sockets_same_type[0];
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
to_socket, &from_socket);
const DInputSocket to_socket = to_sockets_same_type[0];
const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
add_value_to_input_socket(key, value_to_forward);
}
else {
/* Multiple inputs use the value, make a copy for every input except for one. */
const DInputSocket *first_to_socket = to_sockets_same_type[0];
Span<const DInputSocket *> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
const DInputSocket first_to_socket = to_sockets_same_type[0];
Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
const CPPType &type = *value_to_forward.type();
const std::pair<const DInputSocket *, const DOutputSocket *> first_key = std::make_pair(
first_to_socket, &from_socket);
const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket,
from_socket);
add_value_to_input_socket(first_key, value_to_forward);
for (const DInputSocket *to_socket : other_to_sockets) {
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
to_socket, &from_socket);
for (const DInputSocket &to_socket : other_to_sockets) {
const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
void *buffer = allocator_.allocate(type.size(), type.alignment());
type.copy_to_uninitialized(value_to_forward.get(), buffer);
add_value_to_input_socket(key, GMutablePointer{type, buffer});
@ -544,22 +546,17 @@ class GeometryNodesEvaluator {
}
}
void add_value_to_input_socket(const std::pair<const DInputSocket *, const DOutputSocket *> key,
void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key,
GMutablePointer value)
{
value_by_input_.add_new(key, value);
}
GMutablePointer get_unlinked_input_value(const DInputSocket &socket)
GMutablePointer get_unlinked_input_value(const DInputSocket &socket,
const CPPType &required_type)
{
bNodeSocket *bsocket;
if (socket.linked_group_inputs().size() == 0) {
bsocket = socket.bsocket();
}
else {
bsocket = socket.linked_group_inputs()[0]->bsocket();
}
const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket.typeinfo());
bNodeSocket *bsocket = socket->bsocket();
const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
void *buffer = allocator_.allocate(type.size(), type.alignment());
if (bsocket->type == SOCK_OBJECT) {
@ -576,7 +573,19 @@ class GeometryNodesEvaluator {
blender::nodes::socket_cpp_value_get(*bsocket, buffer);
}
return {type, buffer};
if (type == required_type) {
return {type, buffer};
}
if (conversions_.is_convertible(type, required_type)) {
void *converted_buffer = allocator_.allocate(required_type.size(),
required_type.alignment());
conversions_.convert(type, required_type, buffer, converted_buffer);
type.destruct(buffer);
return {required_type, converted_buffer};
}
void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment());
type.copy_to_uninitialized(type.default_value(), default_buffer);
return {required_type, default_buffer};
}
};
@ -985,7 +994,7 @@ static void fill_data_handle_map(const NodesModifierSettings &settings,
{
Set<ID *> used_ids;
find_used_ids_from_settings(settings, used_ids);
find_used_ids_from_nodes(*tree.btree(), used_ids);
find_used_ids_from_nodes(*tree.root_context().tree().btree(), used_ids);
int current_handle = 0;
for (ID *id : used_ids) {
@ -1013,8 +1022,8 @@ static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> tree
* often than necessary. It's going to be replaced soon.
*/
static GeometrySet compute_geometry(const DerivedNodeTree &tree,
Span<const DOutputSocket *> group_input_sockets,
const DInputSocket &socket_to_compute,
Span<const OutputSocketRef *> group_input_sockets,
const InputSocketRef &socket_to_compute,
GeometrySet input_geometry_set,
NodesModifierData *nmd,
const ModifierEvalContext *ctx)
@ -1026,32 +1035,33 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
PersistentDataHandleMap handle_map;
fill_data_handle_map(nmd->settings, tree, handle_map);
Map<const DOutputSocket *, GMutablePointer> group_inputs;
Map<DOutputSocket, GMutablePointer> group_inputs;
const DTreeContext *root_context = &tree.root_context();
if (group_input_sockets.size() > 0) {
Span<const DOutputSocket *> remaining_input_sockets = group_input_sockets;
Span<const OutputSocketRef *> remaining_input_sockets = group_input_sockets;
/* If the group expects a geometry as first input, use the geometry that has been passed to
* modifier. */
const DOutputSocket *first_input_socket = group_input_sockets[0];
const OutputSocketRef *first_input_socket = group_input_sockets[0];
if (first_input_socket->bsocket()->type == SOCK_GEOMETRY) {
GeometrySet *geometry_set_in = allocator.construct<GeometrySet>(
std::move(input_geometry_set));
group_inputs.add_new(first_input_socket, geometry_set_in);
group_inputs.add_new({root_context, first_input_socket}, geometry_set_in);
remaining_input_sockets = remaining_input_sockets.drop_front(1);
}
/* Initialize remaining group inputs. */
for (const DOutputSocket *socket : remaining_input_sockets) {
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
initialize_group_input(*nmd, handle_map, *socket->bsocket(), cpp_type, value_in);
group_inputs.add_new(socket, {cpp_type, value_in});
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
Vector<const DInputSocket *> group_outputs;
group_outputs.append(&socket_to_compute);
Vector<DInputSocket> group_outputs;
group_outputs.append({root_context, &socket_to_compute});
GeometryNodesEvaluator evaluator{group_inputs,
group_outputs,
@ -1126,16 +1136,17 @@ static void modifyGeometry(ModifierData *md,
check_property_socket_sync(ctx->object, md);
blender::nodes::NodeTreeRefMap tree_refs;
DerivedNodeTree tree{nmd->node_group, tree_refs};
NodeTreeRefMap tree_refs;
DerivedNodeTree tree{*nmd->node_group, tree_refs};
if (tree.has_link_cycles()) {
BKE_modifier_set_error(ctx->object, md, "Node group has cycles");
return;
}
Span<const DNode *> input_nodes = tree.nodes_by_type("NodeGroupInput");
Span<const DNode *> output_nodes = tree.nodes_by_type("NodeGroupOutput");
const NodeTreeRef &root_tree_ref = tree.root_context().tree();
Span<const NodeRef *> input_nodes = root_tree_ref.nodes_by_type("NodeGroupInput");
Span<const NodeRef *> output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput");
if (input_nodes.size() > 1) {
return;
@ -1144,16 +1155,18 @@ static void modifyGeometry(ModifierData *md,
return;
}
Span<const DOutputSocket *> group_inputs = (input_nodes.size() == 1) ?
input_nodes[0]->outputs().drop_back(1) :
Span<const DOutputSocket *>{};
Span<const DInputSocket *> group_outputs = output_nodes[0]->inputs().drop_back(1);
Span<const OutputSocketRef *> group_inputs;
if (input_nodes.size() == 1) {
group_inputs = input_nodes[0]->outputs().drop_back(1);
}
Span<const InputSocketRef *> group_outputs = output_nodes[0]->inputs().drop_back(1);
if (group_outputs.size() == 0) {
return;
}
const DInputSocket *group_output = group_outputs[0];
const InputSocketRef *group_output = group_outputs[0];
if (group_output->idname() != "NodeSocketGeometry") {
return;
}

View File

@ -19,544 +19,360 @@
/** \file
* \ingroup nodes
*
* DerivedNodeTree provides a flattened view on a bNodeTree, i.e. node groups are inlined. It
* builds on top of NodeTreeRef and supports similar queries efficiently.
*
* Every inlined node remembers its path to the parent ("call stack").
*
* Unlinked group node inputs are handled separately from other sockets.
*
* There is a dot graph exporter for debugging purposes.
* DerivedNodeTree builds on top of NodeTreeRef and makes working with (nested) node groups more
* convenient and safe. It does so by pairing nodes and sockets with a context. The context
* contains information about the current "instance" of the node or socket. A node might be
* "instanced" multiple times when it is in a node group that is used multiple times.
*/
#include "BLI_function_ref.hh"
#include "BLI_vector_set.hh"
#include "NOD_node_tree_ref.hh"
#include "BLI_vector_set.hh"
namespace blender::nodes {
class DSocket;
class DInputSocket;
class DOutputSocket;
class DNode;
class DParentNode;
class DGroupInput;
class DTreeContext;
class DerivedNodeTree;
class DSocket : NonCopyable, NonMovable {
struct DNode;
struct DSocket;
struct DInputSocket;
struct DOutputSocket;
/**
* The context attached to every node or socket in a derived node tree. It can be used to determine
* the place of a node in a hierarchy of node groups.
*
* Contexts are organized in a tree data structure to avoid having to store the entire path to the
* root node group for every node/socket.
*/
class DTreeContext {
private:
/* Null when this context is for the root node group. Otherwise it points to the context one
* level up. */
DTreeContext *parent_context_;
/* Null when this context is for the root node group. Otherwise it points to the group node in
* the parent node group that contains this context. */
const NodeRef *parent_node_;
/* The current node tree. */
const NodeTreeRef *tree_;
/* All the children contexts of this context. */
Map<const NodeRef *, DTreeContext *> children_;
friend DerivedNodeTree;
public:
const NodeTreeRef &tree() const;
const DTreeContext *parent_context() const;
const NodeRef *parent_node() const;
const DTreeContext *child_context(const NodeRef &node) const;
bool is_root() const;
};
/* A (nullable) reference to a node and the context it is in. It is unique within an entire nested
* node group hierarchy. This type is small and can be passed around by value. */
class DNode {
private:
const DTreeContext *context_ = nullptr;
const NodeRef *node_ref_ = nullptr;
public:
DNode() = default;
DNode(const DTreeContext *context, const NodeRef *node);
const DTreeContext *context() const;
const NodeRef *node_ref() const;
const NodeRef *operator->() const;
friend bool operator==(const DNode &a, const DNode &b);
friend bool operator!=(const DNode &a, const DNode &b);
operator bool() const;
uint64_t hash() const;
};
/* A (nullable) reference to a socket and the context it is in. It is unique within an entire
* nested node group hierarchy. This type is small and can be passed around by value.
*
* A #DSocket can represent an input or an output socket. If the type of a socket is known at
* compile time is is preferable to use #DInputSocket or #DOutputSocket instead. */
class DSocket {
protected:
DNode *node_;
const SocketRef *socket_ref_;
int id_;
friend DerivedNodeTree;
const DTreeContext *context_ = nullptr;
const SocketRef *socket_ref_ = nullptr;
public:
const DNode &node() const;
DSocket() = default;
DSocket(const DTreeContext *context, const SocketRef *socket);
DSocket(const DInputSocket &input_socket);
DSocket(const DOutputSocket &output_socket);
int id() const;
int index() const;
const DTreeContext *context() const;
const SocketRef *socket_ref() const;
const SocketRef *operator->() const;
bool is_input() const;
bool is_output() const;
friend bool operator==(const DSocket &a, const DSocket &b);
friend bool operator!=(const DSocket &a, const DSocket &b);
operator bool() const;
const DSocket &as_base() const;
const DInputSocket &as_input() const;
const DOutputSocket &as_output() const;
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
StringRefNull identifier() const;
bNodeSocketType *typeinfo() const;
const SocketRef &socket_ref() const;
bNodeSocket *bsocket() const;
bool is_available() const;
uint64_t hash() const;
};
/* A (nullable) reference to an input socket and the context it is in. */
class DInputSocket : public DSocket {
private:
Vector<DOutputSocket *> linked_sockets_;
Vector<DGroupInput *> linked_group_inputs_;
bool is_multi_input_socket_;
friend DerivedNodeTree;
public:
const InputSocketRef &socket_ref() const;
DInputSocket() = default;
DInputSocket(const DTreeContext *context, const InputSocketRef *socket);
explicit DInputSocket(const DSocket &base_socket);
Span<const DOutputSocket *> linked_sockets() const;
Span<const DGroupInput *> linked_group_inputs() const;
const InputSocketRef *socket_ref() const;
const InputSocketRef *operator->() const;
bool is_linked() const;
bool is_multi_input_socket() const;
DOutputSocket get_corresponding_group_node_output() const;
Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const;
void foreach_origin_socket(FunctionRef<void(DSocket)> callback) const;
};
/* A (nullable) reference to an output socket and the context it is in. */
class DOutputSocket : public DSocket {
private:
Vector<DInputSocket *> linked_sockets_;
friend DerivedNodeTree;
public:
const OutputSocketRef &socket_ref() const;
Span<const DInputSocket *> linked_sockets() const;
DOutputSocket() = default;
DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket);
explicit DOutputSocket(const DSocket &base_socket);
const OutputSocketRef *socket_ref() const;
const OutputSocketRef *operator->() const;
DInputSocket get_corresponding_group_node_input() const;
DInputSocket get_active_corresponding_group_output_socket() const;
void foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const;
};
class DGroupInput : NonCopyable, NonMovable {
private:
const InputSocketRef *socket_ref_;
DParentNode *parent_;
Vector<DInputSocket *> linked_sockets_;
int id_;
friend DerivedNodeTree;
public:
const InputSocketRef &socket_ref() const;
bNodeSocket *bsocket() const;
const DParentNode *parent() const;
Span<const DInputSocket *> linked_sockets() const;
int id() const;
StringRefNull name() const;
};
class DNode : NonCopyable, NonMovable {
private:
const NodeRef *node_ref_;
DParentNode *parent_;
Span<DInputSocket *> inputs_;
Span<DOutputSocket *> outputs_;
int id_;
friend DerivedNodeTree;
public:
const NodeRef &node_ref() const;
const DParentNode *parent() const;
Span<const DInputSocket *> inputs() const;
Span<const DOutputSocket *> outputs() const;
const DInputSocket &input(int index) const;
const DOutputSocket &output(int index) const;
const DInputSocket &input(int index, StringRef expected_name) const;
const DOutputSocket &output(int index, StringRef expected_name) const;
int id() const;
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
bNode *bnode() const;
bNodeType *typeinfo() const;
private:
void destruct_with_sockets();
};
class DParentNode : NonCopyable, NonMovable {
private:
const NodeRef *node_ref_;
DParentNode *parent_;
int id_;
friend DerivedNodeTree;
public:
const DParentNode *parent() const;
const NodeRef &node_ref() const;
int id() const;
};
class DerivedNodeTree : NonCopyable, NonMovable {
class DerivedNodeTree {
private:
LinearAllocator<> allocator_;
Vector<DNode *> nodes_by_id_;
Vector<DGroupInput *> group_inputs_;
Vector<DParentNode *> parent_nodes_;
Vector<DSocket *> sockets_by_id_;
Vector<DInputSocket *> input_sockets_;
Vector<DOutputSocket *> output_sockets_;
MultiValueMap<const bNodeType *, DNode *> nodes_by_type_;
DTreeContext *root_context_;
VectorSet<const NodeTreeRef *> used_node_tree_refs_;
bNodeTree *btree_;
public:
DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs);
DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs);
~DerivedNodeTree();
bNodeTree *btree() const;
Span<const DNode *> nodes() const;
Span<const DNode *> nodes_by_type(StringRefNull idname) const;
Span<const DNode *> nodes_by_type(const bNodeType *nodetype) const;
Span<const DSocket *> sockets() const;
Span<const DInputSocket *> input_sockets() const;
Span<const DOutputSocket *> output_sockets() const;
Span<const DGroupInput *> group_inputs() const;
const DTreeContext &root_context() const;
Span<const NodeTreeRef *> used_node_tree_refs() const;
bool has_link_cycles() const;
std::string to_dot() const;
void foreach_node(FunctionRef<void(DNode)> callback) const;
private:
/* Utility functions used during construction. */
void insert_nodes_and_links_in_id_order(const NodeTreeRef &tree_ref,
DParentNode *parent,
Vector<DNode *> &all_nodes);
DNode &create_node(const NodeRef &node_ref,
DParentNode *parent,
MutableSpan<DSocket *> r_sockets_map);
void expand_groups(Vector<DNode *> &all_nodes,
Vector<DGroupInput *> &all_group_inputs,
Vector<DParentNode *> &all_parent_nodes,
NodeTreeRefMap &node_tree_refs);
void expand_group_node(DNode &group_node,
Vector<DNode *> &all_nodes,
Vector<DGroupInput *> &all_group_inputs,
Vector<DParentNode *> &all_parent_nodes,
NodeTreeRefMap &node_tree_refs);
void create_group_inputs_for_unlinked_inputs(DNode &node,
Vector<DGroupInput *> &all_group_inputs);
void relink_group_inputs(const NodeTreeRef &group_ref,
Span<DNode *> nodes_by_id,
DNode &group_node);
void relink_group_outputs(const NodeTreeRef &group_ref,
Span<DNode *> nodes_by_id,
DNode &group_node);
void remove_expanded_group_interfaces(Vector<DNode *> &all_nodes);
void remove_unused_group_inputs(Vector<DGroupInput *> &all_group_inputs);
void relink_and_remove_muted_nodes(Vector<DNode *> &all_nodes);
void relink_muted_node(DNode &muted_node);
void store_in_this_and_init_ids(Vector<DNode *> &&all_nodes,
Vector<DGroupInput *> &&all_group_inputs,
Vector<DParentNode *> &&all_parent_nodes);
DTreeContext &construct_context_recursively(DTreeContext *parent_context,
const NodeRef *parent_node,
bNodeTree &btree,
NodeTreeRefMap &node_tree_refs);
void destruct_context_recursively(DTreeContext *context);
void foreach_node_in_context_recursive(const DTreeContext &context,
FunctionRef<void(DNode)> callback) const;
};
namespace derived_node_tree_types {
using namespace node_tree_ref_types;
using nodes::DerivedNodeTree;
using nodes::DGroupInput;
using nodes::DInputSocket;
using nodes::DNode;
using nodes::DOutputSocket;
using nodes::DParentNode;
}; // namespace derived_node_tree_types
using nodes::DSocket;
using nodes::DTreeContext;
} // namespace derived_node_tree_types
/* --------------------------------------------------------------------
* DSocket inline methods.
* DTreeContext inline methods.
*/
inline const DNode &DSocket::node() const
inline const NodeTreeRef &DTreeContext::tree() const
{
return *node_;
return *tree_;
}
inline int DSocket::id() const
inline const DTreeContext *DTreeContext::parent_context() const
{
return id_;
return parent_context_;
}
inline int DSocket::index() const
inline const NodeRef *DTreeContext::parent_node() const
{
return socket_ref_->index();
return parent_node_;
}
inline bool DSocket::is_input() const
inline const DTreeContext *DTreeContext::child_context(const NodeRef &node) const
{
return socket_ref_->is_input();
return children_.lookup_default(&node, nullptr);
}
inline bool DSocket::is_output() const
inline bool DTreeContext::is_root() const
{
return socket_ref_->is_output();
}
inline const DSocket &DSocket::as_base() const
{
return *this;
}
inline const DInputSocket &DSocket::as_input() const
{
return static_cast<const DInputSocket &>(*this);
}
inline const DOutputSocket &DSocket::as_output() const
{
return static_cast<const DOutputSocket &>(*this);
}
inline PointerRNA *DSocket::rna() const
{
return socket_ref_->rna();
}
inline StringRefNull DSocket::idname() const
{
return socket_ref_->idname();
}
inline StringRefNull DSocket::name() const
{
return socket_ref_->name();
}
inline StringRefNull DSocket::identifier() const
{
return socket_ref_->identifier();
}
inline bNodeSocketType *DSocket::typeinfo() const
{
return socket_ref_->bsocket()->typeinfo;
}
inline const SocketRef &DSocket::socket_ref() const
{
return *socket_ref_;
}
inline bNodeSocket *DSocket::bsocket() const
{
return socket_ref_->bsocket();
}
inline bool DSocket::is_available() const
{
return (socket_ref_->bsocket()->flag & SOCK_UNAVAIL) == 0;
}
/* --------------------------------------------------------------------
* DInputSocket inline methods.
*/
inline const InputSocketRef &DInputSocket::socket_ref() const
{
return socket_ref_->as_input();
}
inline Span<const DOutputSocket *> DInputSocket::linked_sockets() const
{
return linked_sockets_;
}
inline Span<const DGroupInput *> DInputSocket::linked_group_inputs() const
{
return linked_group_inputs_;
}
inline bool DInputSocket::is_linked() const
{
return linked_sockets_.size() > 0 || linked_group_inputs_.size() > 0;
}
inline bool DInputSocket::is_multi_input_socket() const
{
return is_multi_input_socket_;
}
/* --------------------------------------------------------------------
* DOutputSocket inline methods.
*/
inline const OutputSocketRef &DOutputSocket::socket_ref() const
{
return socket_ref_->as_output();
}
inline Span<const DInputSocket *> DOutputSocket::linked_sockets() const
{
return linked_sockets_;
}
/* --------------------------------------------------------------------
* DGroupInput inline methods.
*/
inline const InputSocketRef &DGroupInput::socket_ref() const
{
return *socket_ref_;
}
inline bNodeSocket *DGroupInput::bsocket() const
{
return socket_ref_->bsocket();
}
inline const DParentNode *DGroupInput::parent() const
{
return parent_;
}
inline Span<const DInputSocket *> DGroupInput::linked_sockets() const
{
return linked_sockets_;
}
inline int DGroupInput::id() const
{
return id_;
}
inline StringRefNull DGroupInput::name() const
{
return socket_ref_->name();
return parent_context_ == nullptr;
}
/* --------------------------------------------------------------------
* DNode inline methods.
*/
inline const NodeRef &DNode::node_ref() const
inline DNode::DNode(const DTreeContext *context, const NodeRef *node_ref)
: context_(context), node_ref_(node_ref)
{
return *node_ref_;
BLI_assert(node_ref == nullptr || &node_ref->tree() == &context->tree());
}
inline const DParentNode *DNode::parent() const
inline const DTreeContext *DNode::context() const
{
return parent_;
return context_;
}
inline Span<const DInputSocket *> DNode::inputs() const
inline const NodeRef *DNode::node_ref() const
{
return inputs_;
return node_ref_;
}
inline Span<const DOutputSocket *> DNode::outputs() const
inline bool operator==(const DNode &a, const DNode &b)
{
return outputs_;
return a.context_ == b.context_ && a.node_ref_ == b.node_ref_;
}
inline const DInputSocket &DNode::input(int index) const
inline bool operator!=(const DNode &a, const DNode &b)
{
return *inputs_[index];
return !(a == b);
}
inline const DOutputSocket &DNode::output(int index) const
inline DNode::operator bool() const
{
return *outputs_[index];
return node_ref_ != nullptr;
}
inline const DInputSocket &DNode::input(int index, StringRef expected_name) const
inline const NodeRef *DNode::operator->() const
{
const DInputSocket &socket = *inputs_[index];
BLI_assert(socket.name() == expected_name);
UNUSED_VARS_NDEBUG(expected_name);
return socket;
return node_ref_;
}
inline const DOutputSocket &DNode::output(int index, StringRef expected_name) const
inline uint64_t DNode::hash() const
{
const DOutputSocket &socket = *outputs_[index];
BLI_assert(socket.name() == expected_name);
UNUSED_VARS_NDEBUG(expected_name);
return socket;
}
inline int DNode::id() const
{
return id_;
}
inline PointerRNA *DNode::rna() const
{
return node_ref_->rna();
}
inline StringRefNull DNode::idname() const
{
return node_ref_->idname();
}
inline StringRefNull DNode::name() const
{
return node_ref_->name();
}
inline bNode *DNode::bnode() const
{
return node_ref_->bnode();
}
inline bNodeType *DNode::typeinfo() const
{
return node_ref_->bnode()->typeinfo;
return DefaultHash<const DTreeContext *>{}(context_) ^ DefaultHash<const NodeRef *>{}(node_ref_);
}
/* --------------------------------------------------------------------
* DParentNode inline methods.
* DSocket inline methods.
*/
inline const DParentNode *DParentNode::parent() const
inline DSocket::DSocket(const DTreeContext *context, const SocketRef *socket_ref)
: context_(context), socket_ref_(socket_ref)
{
return parent_;
BLI_assert(socket_ref == nullptr || &socket_ref->tree() == &context->tree());
}
inline const NodeRef &DParentNode::node_ref() const
inline DSocket::DSocket(const DInputSocket &input_socket)
: DSocket(input_socket.context_, input_socket.socket_ref_)
{
return *node_ref_;
}
inline int DParentNode::id() const
inline DSocket::DSocket(const DOutputSocket &output_socket)
: DSocket(output_socket.context_, output_socket.socket_ref_)
{
return id_;
}
inline const DTreeContext *DSocket::context() const
{
return context_;
}
inline const SocketRef *DSocket::socket_ref() const
{
return socket_ref_;
}
inline bool operator==(const DSocket &a, const DSocket &b)
{
return a.context_ == b.context_ && a.socket_ref_ == b.socket_ref_;
}
inline bool operator!=(const DSocket &a, const DSocket &b)
{
return !(a == b);
}
inline DSocket::operator bool() const
{
return socket_ref_ != nullptr;
}
inline const SocketRef *DSocket::operator->() const
{
return socket_ref_;
}
inline uint64_t DSocket::hash() const
{
return DefaultHash<const DTreeContext *>{}(context_) ^
DefaultHash<const SocketRef *>{}(socket_ref_);
}
/* --------------------------------------------------------------------
* DInputSocket inline methods.
*/
inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocketRef *socket_ref)
: DSocket(context, socket_ref)
{
}
inline DInputSocket::DInputSocket(const DSocket &base_socket) : DSocket(base_socket)
{
BLI_assert(base_socket->is_input());
}
inline const InputSocketRef *DInputSocket::socket_ref() const
{
return (const InputSocketRef *)socket_ref_;
}
inline const InputSocketRef *DInputSocket::operator->() const
{
return (const InputSocketRef *)socket_ref_;
}
/* --------------------------------------------------------------------
* DOutputSocket inline methods.
*/
inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket_ref)
: DSocket(context, socket_ref)
{
}
inline DOutputSocket::DOutputSocket(const DSocket &base_socket) : DSocket(base_socket)
{
BLI_assert(base_socket->is_output());
}
inline const OutputSocketRef *DOutputSocket::socket_ref() const
{
return (const OutputSocketRef *)socket_ref_;
}
inline const OutputSocketRef *DOutputSocket::operator->() const
{
return (const OutputSocketRef *)socket_ref_;
}
/* --------------------------------------------------------------------
* DerivedNodeTree inline methods.
*/
inline bNodeTree *DerivedNodeTree::btree() const
inline const DTreeContext &DerivedNodeTree::root_context() const
{
return btree_;
}
inline Span<const DNode *> DerivedNodeTree::nodes() const
{
return nodes_by_id_;
}
inline Span<const DNode *> DerivedNodeTree::nodes_by_type(StringRefNull idname) const
{
const bNodeType *nodetype = nodeTypeFind(idname.c_str());
return this->nodes_by_type(nodetype);
}
inline Span<const DNode *> DerivedNodeTree::nodes_by_type(const bNodeType *nodetype) const
{
return nodes_by_type_.lookup(nodetype);
}
inline Span<const DSocket *> DerivedNodeTree::sockets() const
{
return sockets_by_id_;
}
inline Span<const DInputSocket *> DerivedNodeTree::input_sockets() const
{
return input_sockets_;
}
inline Span<const DOutputSocket *> DerivedNodeTree::output_sockets() const
{
return output_sockets_;
}
inline Span<const DGroupInput *> DerivedNodeTree::group_inputs() const
{
return group_inputs_;
return *root_context_;
}
inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const

View File

@ -59,7 +59,7 @@ using fn::GValueMap;
class GeoNodeExecParams {
private:
const DNode &node_;
const DNode node_;
GValueMap<StringRef> &input_values_;
GValueMap<StringRef> &output_values_;
const PersistentDataHandleMap &handle_map_;
@ -68,7 +68,7 @@ class GeoNodeExecParams {
Depsgraph *depsgraph_;
public:
GeoNodeExecParams(const DNode &node,
GeoNodeExecParams(const DNode node,
GValueMap<StringRef> &input_values,
GValueMap<StringRef> &output_values,
const PersistentDataHandleMap &handle_map,
@ -182,7 +182,7 @@ class GeoNodeExecParams {
*/
const bNode &node() const
{
return *node_.bnode();
return *node_->bnode();
}
const PersistentDataHandleMap &handle_map() const

View File

@ -28,14 +28,15 @@
#include "NOD_derived_node_tree.hh"
#include "NOD_type_callbacks.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_resource_collector.hh"
namespace blender::nodes {
/**
* A MFNetworkTreeMap maps various components of a DerivedNodeTree 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.
* 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:
@ -47,15 +48,11 @@ class MFNetworkTreeMap {
*/
const DerivedNodeTree &tree_;
fn::MFNetwork &network_;
Array<Vector<fn::MFSocket *, 1>> sockets_by_dsocket_id_;
Array<fn::MFOutputSocket *> socket_by_group_input_id_;
MultiValueMap<DSocket, fn::MFSocket *> sockets_by_dsocket_;
public:
MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network)
: tree_(tree),
network_(network),
sockets_by_dsocket_id_(tree.sockets().size()),
socket_by_group_input_id_(tree.group_inputs().size(), nullptr)
: tree_(tree), network_(network)
{
}
@ -76,96 +73,95 @@ class MFNetworkTreeMap {
void add(const DSocket &dsocket, fn::MFSocket &socket)
{
BLI_assert(dsocket.is_input() == socket.is_input());
BLI_assert(dsocket.is_input() || sockets_by_dsocket_id_[dsocket.id()].size() == 0);
sockets_by_dsocket_id_[dsocket.id()].append(&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_id_[dsocket.id()].append(&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_id_[dsocket.id()].size() == 0);
sockets_by_dsocket_id_[dsocket.id()].append(&socket);
BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty());
sockets_by_dsocket_.add(dsocket, &socket);
}
void add(Span<const DInputSocket *> dsockets, Span<fn::MFInputSocket *> sockets)
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(*dsockets[i], *sockets[i]);
this->add(DInputSocket(&context, dsockets[i]), *sockets[i]);
}
}
void add(Span<const DOutputSocket *> dsockets, Span<fn::MFOutputSocket *> sockets)
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(*dsockets[i], *sockets[i]);
this->add(DOutputSocket(&context, dsockets[i]), *sockets[i]);
}
}
void add(const DGroupInput &group_input, fn::MFOutputSocket &socket)
{
BLI_assert(socket_by_group_input_id_[group_input.id()] == nullptr);
socket_by_group_input_id_[group_input.id()] = &socket;
}
void add_try_match(const DNode &dnode, fn::MFNode &node)
{
this->add_try_match(dnode.inputs().cast<const DSocket *>(),
this->add_try_match(*dnode.context(),
dnode->inputs().cast<const SocketRef *>(),
node.inputs().cast<fn::MFSocket *>());
this->add_try_match(dnode.outputs().cast<const DSocket *>(),
this->add_try_match(*dnode.context(),
dnode->outputs().cast<const SocketRef *>(),
node.outputs().cast<fn::MFSocket *>());
}
void add_try_match(Span<const DInputSocket *> dsockets, Span<fn::MFInputSocket *> sockets)
void add_try_match(const DTreeContext &context,
Span<const InputSocketRef *> dsockets,
Span<fn::MFInputSocket *> sockets)
{
this->add_try_match(dsockets.cast<const DSocket *>(), sockets.cast<fn::MFSocket *>());
this->add_try_match(
context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>());
}
void add_try_match(Span<const DOutputSocket *> dsockets, Span<fn::MFOutputSocket *> sockets)
void add_try_match(const DTreeContext &context,
Span<const OutputSocketRef *> dsockets,
Span<fn::MFOutputSocket *> sockets)
{
this->add_try_match(dsockets.cast<const DSocket *>(), sockets.cast<fn::MFSocket *>());
this->add_try_match(
context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>());
}
void add_try_match(Span<const DSocket *> dsockets, Span<fn::MFSocket *> sockets)
void add_try_match(const DTreeContext &context,
Span<const SocketRef *> dsockets,
Span<fn::MFSocket *> sockets)
{
int used_sockets = 0;
for (const DSocket *dsocket : dsockets) {
for (const SocketRef *dsocket : dsockets) {
if (!dsocket->is_available()) {
continue;
}
if (!socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) {
if (!socket_is_mf_data_socket(*dsocket->typeinfo())) {
continue;
}
fn::MFSocket *socket = sockets[used_sockets];
this->add(*dsocket, *socket);
this->add(DSocket(&context, dsocket), *socket);
used_sockets++;
}
}
fn::MFOutputSocket &lookup(const DGroupInput &group_input)
{
fn::MFOutputSocket *socket = socket_by_group_input_id_[group_input.id()];
BLI_assert(socket != nullptr);
return *socket;
}
fn::MFOutputSocket &lookup(const DOutputSocket &dsocket)
{
auto &sockets = sockets_by_dsocket_id_[dsocket.id()];
BLI_assert(sockets.size() == 1);
return sockets[0]->as_output();
return sockets_by_dsocket_.lookup(dsocket)[0]->as_output();
}
Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket)
{
return sockets_by_dsocket_id_[dsocket.id()].as_span().cast<fn::MFInputSocket *>();
return sockets_by_dsocket_.lookup(dsocket).cast<fn::MFInputSocket *>();
}
fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket)
@ -186,7 +182,7 @@ class MFNetworkTreeMap {
bool is_mapped(const DSocket &dsocket) const
{
return sockets_by_dsocket_id_[dsocket.id()].size() >= 1;
return !sockets_by_dsocket_.lookup(dsocket).is_empty();
}
};
@ -258,12 +254,7 @@ class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
public:
SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket)
: MFNetworkBuilderBase(common), bsocket_(dsocket.bsocket())
{
}
SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DGroupInput &group_input)
: MFNetworkBuilderBase(common), bsocket_(group_input.bsocket())
: MFNetworkBuilderBase(common), bsocket_(dsocket->bsocket())
{
}
@ -331,10 +322,10 @@ class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
*/
class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
private:
const DNode &dnode_;
DNode dnode_;
public:
NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DNode &dnode)
NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, DNode dnode)
: MFNetworkBuilderBase(common), dnode_(dnode)
{
}
@ -352,7 +343,7 @@ class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
const fn::MultiFunction &get_not_implemented_fn()
{
return this->get_default_fn("Not Implemented (" + dnode_.name() + ")");
return this->get_default_fn("Not Implemented (" + dnode_->name() + ")");
}
const fn::MultiFunction &get_default_fn(StringRef name);
@ -377,7 +368,7 @@ class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
*/
bNode &bnode()
{
return *dnode_.node_ref().bnode();
return *dnode_->bnode();
}
/**
@ -393,7 +384,7 @@ MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
ResourceCollector &resources);
using MultiFunctionByNode = Map<const DNode *, const fn::MultiFunction *>;
using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>;
MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
ResourceCollector &resources);

View File

@ -26,9 +26,10 @@ static void fn_node_group_instance_id_expand_in_mf_network(
{
const blender::nodes::DNode &node = builder.dnode();
std::string id = "/";
for (const blender::nodes::DParentNode *parent = node.parent(); parent;
parent = parent->parent()) {
id = "/" + parent->node_ref().name() + id;
for (const blender::nodes::DTreeContext *context = node.context();
context->parent_node() != nullptr;
context = context->parent_context()) {
id = "/" + context->parent_node()->name() + id;
}
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(
std::move(id));

View File

@ -67,12 +67,13 @@ static void fn_node_random_float_expand_in_mf_network(
blender::nodes::NodeMFNetworkBuilder &builder)
{
uint32_t function_seed = 1746872341u;
const blender::nodes::DNode &node = builder.dnode();
blender::nodes::DNode node = builder.dnode();
const blender::DefaultHash<blender::StringRefNull> hasher;
function_seed = 33 * function_seed + hasher(node.name());
for (const blender::nodes::DParentNode *parent = node.parent(); parent != nullptr;
parent = parent->parent()) {
function_seed = 33 * function_seed + hasher(parent->node_ref().name());
function_seed = 33 * function_seed + hasher(node->name());
for (const blender::nodes::DTreeContext *context = node.context();
context->parent_node() != nullptr;
context = context->parent_context()) {
function_seed = 33 * function_seed + hasher(context->parent_node()->name());
}
builder.construct_and_set_matching_fn<RandomFloatFunction>(function_seed);

View File

@ -16,518 +16,256 @@
#include "NOD_derived_node_tree.hh"
#include "BLI_dot_export.hh"
#define UNINITIALIZED_ID UINT32_MAX
namespace blender::nodes {
DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : btree_(btree)
/* Construct a new derived node tree for a given root node tree. The generated derived node tree
* does not own the used node tree refs (so that those can be used by others as well). The caller
* has to make sure that the node tree refs added to #node_tree_refs live at least as long as the
* derived node tree. */
DerivedNodeTree::DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs)
{
BLI_assert(btree != nullptr);
const NodeTreeRef &main_tree_ref = get_tree_ref_from_map(node_tree_refs, *btree);
used_node_tree_refs_.add_new(&main_tree_ref);
Vector<DNode *> all_nodes;
Vector<DGroupInput *> all_group_inputs;
Vector<DParentNode *> all_parent_nodes;
this->insert_nodes_and_links_in_id_order(main_tree_ref, nullptr, all_nodes);
this->expand_groups(all_nodes, all_group_inputs, all_parent_nodes, node_tree_refs);
this->relink_and_remove_muted_nodes(all_nodes);
this->remove_expanded_group_interfaces(all_nodes);
this->remove_unused_group_inputs(all_group_inputs);
this->store_in_this_and_init_ids(
std::move(all_nodes), std::move(all_group_inputs), std::move(all_parent_nodes));
/* Construct all possible contexts immediately. This is significantly cheaper than inlining all
* node groups. If it still becomes a performance issue in the future, contexts could be
* constructed lazily when they are needed. */
root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree, node_tree_refs);
}
BLI_NOINLINE void DerivedNodeTree::insert_nodes_and_links_in_id_order(const NodeTreeRef &tree_ref,
DParentNode *parent,
Vector<DNode *> &all_nodes)
DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context,
const NodeRef *parent_node,
bNodeTree &btree,
NodeTreeRefMap &node_tree_refs)
{
Array<DSocket *, 64> sockets_map(tree_ref.sockets().size());
DTreeContext &context = *allocator_.construct<DTreeContext>();
context.parent_context_ = parent_context;
context.parent_node_ = parent_node;
context.tree_ = &get_tree_ref_from_map(node_tree_refs, btree);
used_node_tree_refs_.add(context.tree_);
/* Insert nodes. */
for (const NodeRef *node_ref : tree_ref.nodes()) {
DNode &node = this->create_node(*node_ref, parent, sockets_map);
all_nodes.append(&node);
}
/* Insert links. */
for (const NodeRef *node_ref : tree_ref.nodes()) {
for (const InputSocketRef *to_socket_ref : node_ref->inputs()) {
DInputSocket *to_socket = static_cast<DInputSocket *>(sockets_map[to_socket_ref->id()]);
for (const OutputSocketRef *from_socket_ref : to_socket_ref->linked_sockets()) {
DOutputSocket *from_socket = static_cast<DOutputSocket *>(
sockets_map[from_socket_ref->id()]);
to_socket->linked_sockets_.append(from_socket);
from_socket->linked_sockets_.append(to_socket);
}
}
}
}
DNode &DerivedNodeTree::create_node(const NodeRef &node_ref,
DParentNode *parent,
MutableSpan<DSocket *> r_sockets_map)
{
DNode &node = *allocator_.construct<DNode>();
node.node_ref_ = &node_ref;
node.parent_ = parent;
node.id_ = UNINITIALIZED_ID;
node.inputs_ = allocator_.construct_elements_and_pointer_array<DInputSocket>(
node_ref.inputs().size());
node.outputs_ = allocator_.construct_elements_and_pointer_array<DOutputSocket>(
node_ref.outputs().size());
for (int i : node.inputs_.index_range()) {
const InputSocketRef &socket_ref = node_ref.input(i);
DInputSocket &socket = *node.inputs_[i];
socket.is_multi_input_socket_ = socket_ref.bsocket()->flag & SOCK_MULTI_INPUT;
socket.id_ = UNINITIALIZED_ID;
socket.node_ = &node;
socket.socket_ref_ = &socket_ref;
r_sockets_map[socket_ref.id()] = &socket;
}
for (int i : node.outputs_.index_range()) {
const OutputSocketRef &socket_ref = node_ref.output(i);
DOutputSocket &socket = *node.outputs_[i];
socket.id_ = UNINITIALIZED_ID;
socket.node_ = &node;
socket.socket_ref_ = &socket_ref;
r_sockets_map[socket_ref.id()] = &socket;
}
return node;
}
BLI_NOINLINE void DerivedNodeTree::expand_groups(Vector<DNode *> &all_nodes,
Vector<DGroupInput *> &all_group_inputs,
Vector<DParentNode *> &all_parent_nodes,
NodeTreeRefMap &node_tree_refs)
{
for (int i = 0; i < all_nodes.size(); i++) {
DNode &node = *all_nodes[i];
if (node.node_ref_->is_group_node()) {
/* Muted nodes are relinked in a separate step. */
if (!node.node_ref_->is_muted()) {
this->expand_group_node(
node, all_nodes, all_group_inputs, all_parent_nodes, node_tree_refs);
}
}
}
}
BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
Vector<DNode *> &all_nodes,
Vector<DGroupInput *> &all_group_inputs,
Vector<DParentNode *> &all_parent_nodes,
NodeTreeRefMap &node_tree_refs)
{
const NodeRef &group_node_ref = *group_node.node_ref_;
BLI_assert(group_node_ref.is_group_node());
bNodeTree *btree = reinterpret_cast<bNodeTree *>(group_node_ref.bnode()->id);
if (btree == nullptr) {
return;
}
const NodeTreeRef &group_ref = get_tree_ref_from_map(node_tree_refs, *btree);
used_node_tree_refs_.add(&group_ref);
DParentNode &parent = *allocator_.construct<DParentNode>();
parent.id_ = all_parent_nodes.append_and_get_index(&parent);
parent.parent_ = group_node.parent_;
parent.node_ref_ = &group_node_ref;
this->insert_nodes_and_links_in_id_order(group_ref, &parent, all_nodes);
Span<DNode *> new_nodes_by_id = all_nodes.as_span().take_back(group_ref.nodes().size());
this->create_group_inputs_for_unlinked_inputs(group_node, all_group_inputs);
this->relink_group_inputs(group_ref, new_nodes_by_id, group_node);
this->relink_group_outputs(group_ref, new_nodes_by_id, group_node);
}
BLI_NOINLINE void DerivedNodeTree::create_group_inputs_for_unlinked_inputs(
DNode &node, Vector<DGroupInput *> &all_group_inputs)
{
for (DInputSocket *input_socket : node.inputs_) {
if (input_socket->is_linked()) {
continue;
}
DGroupInput &group_input = *allocator_.construct<DGroupInput>();
group_input.id_ = UNINITIALIZED_ID;
group_input.socket_ref_ = &input_socket->socket_ref();
group_input.parent_ = node.parent_;
group_input.linked_sockets_.append(input_socket);
input_socket->linked_group_inputs_.append(&group_input);
all_group_inputs.append(&group_input);
}
}
BLI_NOINLINE void DerivedNodeTree::relink_group_inputs(const NodeTreeRef &group_ref,
Span<DNode *> nodes_by_id,
DNode &group_node)
{
Span<const NodeRef *> node_refs = group_ref.nodes_by_type("NodeGroupInput");
if (node_refs.size() == 0) {
return;
}
int input_amount = group_node.inputs().size();
for (int input_index : IndexRange(input_amount)) {
DInputSocket *outside_group = group_node.inputs_[input_index];
for (DOutputSocket *outside_connected : outside_group->linked_sockets_) {
outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
}
for (DGroupInput *outside_connected : outside_group->linked_group_inputs_) {
outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
}
for (const NodeRef *input_node_ref : node_refs) {
DNode &input_node = *nodes_by_id[input_node_ref->id()];
DOutputSocket *inside_group = input_node.outputs_[input_index];
for (DInputSocket *inside_connected : inside_group->linked_sockets_) {
inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
for (DOutputSocket *outside_connected : outside_group->linked_sockets_) {
inside_connected->linked_sockets_.append(outside_connected);
outside_connected->linked_sockets_.append(inside_connected);
}
for (DGroupInput *outside_connected : outside_group->linked_group_inputs_) {
inside_connected->linked_group_inputs_.append(outside_connected);
outside_connected->linked_sockets_.append(inside_connected);
}
}
inside_group->linked_sockets_.clear();
}
outside_group->linked_sockets_.clear();
outside_group->linked_group_inputs_.clear();
}
}
BLI_NOINLINE void DerivedNodeTree::relink_group_outputs(const NodeTreeRef &group_ref,
Span<DNode *> nodes_by_id,
DNode &group_node)
{
Span<const NodeRef *> node_refs = group_ref.nodes_by_type("NodeGroupOutput");
if (node_refs.size() == 0) {
return;
}
/* TODO: Pick correct group output node if there are more than one. */
const NodeRef &output_node_ref = *node_refs[0];
DNode &output_node = *nodes_by_id[output_node_ref.id()];
int output_amount = group_node.outputs().size();
BLI_assert(output_amount == output_node_ref.inputs().size() - 1);
for (int output_index : IndexRange(output_amount)) {
DOutputSocket *outside_group = group_node.outputs_[output_index];
DInputSocket *inside_group = output_node.inputs_[output_index];
for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
}
for (DOutputSocket *inside_connected : inside_group->linked_sockets_) {
inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
inside_connected->linked_sockets_.append(outside_connected);
outside_connected->linked_sockets_.append(inside_connected);
}
}
for (DGroupInput *inside_connected : inside_group->linked_group_inputs_) {
inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
inside_connected->linked_sockets_.append(outside_connected);
outside_connected->linked_group_inputs_.append(inside_connected);
}
}
outside_group->linked_sockets_.clear();
inside_group->linked_sockets_.clear();
}
}
BLI_NOINLINE void DerivedNodeTree::remove_expanded_group_interfaces(Vector<DNode *> &all_nodes)
{
int index = 0;
while (index < all_nodes.size()) {
DNode &node = *all_nodes[index];
const NodeRef &node_ref = *node.node_ref_;
if (node_ref.is_group_node() ||
(node.parent_ != nullptr &&
(node_ref.is_group_input_node() || node_ref.is_group_output_node()))) {
all_nodes.remove_and_reorder(index);
node.destruct_with_sockets();
}
else {
index++;
}
}
}
BLI_NOINLINE void DerivedNodeTree::remove_unused_group_inputs(
Vector<DGroupInput *> &all_group_inputs)
{
int index = 0;
while (index < all_group_inputs.size()) {
DGroupInput &group_input = *all_group_inputs[index];
if (group_input.linked_sockets_.is_empty()) {
all_group_inputs.remove_and_reorder(index);
group_input.~DGroupInput();
}
else {
index++;
}
}
}
BLI_NOINLINE void DerivedNodeTree::relink_and_remove_muted_nodes(Vector<DNode *> &all_nodes)
{
int index = 0;
while (index < all_nodes.size()) {
DNode &node = *all_nodes[index];
const NodeRef &node_ref = *node.node_ref_;
if (node_ref.is_muted()) {
this->relink_muted_node(node);
all_nodes.remove_and_reorder(index);
node.destruct_with_sockets();
}
else {
index++;
}
}
}
BLI_NOINLINE void DerivedNodeTree::relink_muted_node(DNode &node)
{
const bNode &bnode = *node.bnode();
LISTBASE_FOREACH (const bNodeLink *, internal_link, &bnode.internal_links) {
BLI_assert(internal_link->fromnode == &bnode);
BLI_assert(internal_link->tonode == &bnode);
bNodeSocket *input_bsocket = internal_link->fromsock;
bNodeSocket *output_bsocket = internal_link->tosock;
/* Find internally linked sockets. */
DInputSocket *input_socket = nullptr;
DOutputSocket *output_socket = nullptr;
for (DInputSocket *socket : node.inputs_) {
if (socket->bsocket() == input_bsocket) {
input_socket = socket;
break;
}
}
for (DOutputSocket *socket : node.outputs_) {
if (socket->bsocket() == output_bsocket) {
output_socket = socket;
break;
}
}
BLI_assert(input_socket != nullptr);
BLI_assert(output_socket != nullptr);
/* Link sockets connected to the input to sockets that are connected to the internally linked
* output. */
for (DInputSocket *to_socket : output_socket->linked_sockets_) {
for (DOutputSocket *from_socket : input_socket->linked_sockets_) {
from_socket->linked_sockets_.append_non_duplicates(to_socket);
to_socket->linked_sockets_.append_non_duplicates(from_socket);
}
for (DGroupInput *group_input : input_socket->linked_group_inputs_) {
group_input->linked_sockets_.append_non_duplicates(to_socket);
to_socket->linked_group_inputs_.append_non_duplicates(group_input);
for (const NodeRef *node : context.tree_->nodes()) {
if (node->is_group_node()) {
bNode *bnode = node->bnode();
bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id);
if (child_btree != nullptr) {
DTreeContext &child = this->construct_context_recursively(
&context, node, *child_btree, node_tree_refs);
context.children_.add_new(node, &child);
}
}
}
/* Remove remaining links from muted node. */
for (DInputSocket *to_socket : node.inputs_) {
for (DOutputSocket *from_socket : to_socket->linked_sockets_) {
from_socket->linked_sockets_.remove_first_occurrence_and_reorder(to_socket);
}
for (DGroupInput *from_group_input : to_socket->linked_group_inputs_) {
from_group_input->linked_sockets_.remove_first_occurrence_and_reorder(to_socket);
}
to_socket->linked_sockets_.clear();
to_socket->linked_group_inputs_.clear();
}
for (DOutputSocket *from_socket : node.outputs_) {
for (DInputSocket *to_socket : from_socket->linked_sockets_) {
to_socket->linked_sockets_.remove_first_occurrence_and_reorder(from_socket);
}
from_socket->linked_sockets_.clear();
}
}
void DNode::destruct_with_sockets()
{
for (DInputSocket *socket : inputs_) {
socket->~DInputSocket();
}
for (DOutputSocket *socket : outputs_) {
socket->~DOutputSocket();
}
this->~DNode();
}
BLI_NOINLINE void DerivedNodeTree::store_in_this_and_init_ids(
Vector<DNode *> &&all_nodes,
Vector<DGroupInput *> &&all_group_inputs,
Vector<DParentNode *> &&all_parent_nodes)
{
nodes_by_id_ = std::move(all_nodes);
group_inputs_ = std::move(all_group_inputs);
parent_nodes_ = std::move(all_parent_nodes);
for (int node_index : nodes_by_id_.index_range()) {
DNode *node = nodes_by_id_[node_index];
node->id_ = node_index;
const bNodeType *nodetype = node->node_ref_->bnode()->typeinfo;
nodes_by_type_.add(nodetype, node);
for (DInputSocket *socket : node->inputs_) {
socket->id_ = sockets_by_id_.append_and_get_index(socket);
input_sockets_.append(socket);
}
for (DOutputSocket *socket : node->outputs_) {
socket->id_ = sockets_by_id_.append_and_get_index(socket);
output_sockets_.append(socket);
}
}
for (int i : group_inputs_.index_range()) {
group_inputs_[i]->id_ = i;
}
return context;
}
DerivedNodeTree::~DerivedNodeTree()
{
for (DInputSocket *socket : input_sockets_) {
socket->~DInputSocket();
}
for (DOutputSocket *socket : output_sockets_) {
socket->~DOutputSocket();
}
for (DNode *node : nodes_by_id_) {
node->~DNode();
}
for (DGroupInput *group_input : group_inputs_) {
group_input->~DGroupInput();
}
for (DParentNode *parent : parent_nodes_) {
parent->~DParentNode();
}
/* Has to be destructed manually, because the context info is allocated in a linear allocator. */
this->destruct_context_recursively(root_context_);
}
void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
{
for (DTreeContext *child : context->children_.values()) {
this->destruct_context_recursively(child);
}
context->~DTreeContext();
}
/* Returns true if there are any cycles in the node tree. */
bool DerivedNodeTree::has_link_cycles() const
{
for (const NodeTreeRef *tree : used_node_tree_refs_) {
if (tree->has_link_cycles()) {
for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
if (tree_ref->has_link_cycles()) {
return true;
}
}
return false;
}
static dot::Cluster *get_cluster_for_parent(dot::DirectedGraph &graph,
Map<const DParentNode *, dot::Cluster *> &clusters,
const DParentNode *parent)
/* Calls the given callback on all nodes in the (possibly nested) derived node tree. */
void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
{
if (parent == nullptr) {
return nullptr;
}
return clusters.lookup_or_add_cb(parent, [&]() {
dot::Cluster *parent_cluster = get_cluster_for_parent(graph, clusters, parent->parent());
bNodeTree *btree = reinterpret_cast<bNodeTree *>(parent->node_ref().bnode()->id);
dot::Cluster *new_cluster = &graph.new_cluster(parent->node_ref().name() + " / " +
StringRef(btree->id.name + 2));
new_cluster->set_parent_cluster(parent_cluster);
return new_cluster;
});
this->foreach_node_in_context_recursive(*root_context_, callback);
}
std::string DerivedNodeTree::to_dot() const
void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
FunctionRef<void(DNode)> callback) const
{
dot::DirectedGraph digraph;
digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
Map<const DNode *, dot::NodeWithSocketsRef> dot_nodes;
Map<const DGroupInput *, dot::NodeWithSocketsRef> dot_group_inputs;
Map<const DParentNode *, dot::Cluster *> dot_clusters;
for (const DNode *node : nodes_by_id_) {
dot::Node &dot_node = digraph.new_node("");
dot_node.set_background_color("white");
Vector<std::string> input_names;
for (const DInputSocket *socket : node->inputs()) {
input_names.append(socket->name());
}
Vector<std::string> output_names;
for (const DOutputSocket *socket : node->outputs()) {
output_names.append(socket->name());
}
dot_nodes.add_new(node,
dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, node->parent());
dot_node.set_parent_cluster(cluster);
for (const NodeRef *node_ref : context.tree_->nodes()) {
callback(DNode(&context, node_ref));
}
for (const DGroupInput *group_input : group_inputs_) {
dot::Node &dot_node = digraph.new_node("");
dot_node.set_background_color("white");
std::string group_input_name = group_input->name();
dot_group_inputs.add_new(
group_input, dot::NodeWithSocketsRef(dot_node, "Group Input", {}, {group_input_name}));
dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, group_input->parent());
dot_node.set_parent_cluster(cluster);
for (const DTreeContext *child_context : context.children_.values()) {
this->foreach_node_in_context_recursive(*child_context, callback);
}
}
for (const DNode *to_node : nodes_by_id_) {
dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(to_node);
DOutputSocket DInputSocket::get_corresponding_group_node_output() const
{
BLI_assert(*this);
BLI_assert(socket_ref_->node().is_group_output_node());
BLI_assert(socket_ref_->index() < socket_ref_->node().inputs().size() - 1);
for (const DInputSocket *to_socket : to_node->inputs()) {
for (const DOutputSocket *from_socket : to_socket->linked_sockets()) {
const DNode *from_node = &from_socket->node();
dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(from_node);
const DTreeContext *parent_context = context_->parent_context();
const NodeRef *parent_node = context_->parent_node();
BLI_assert(parent_context != nullptr);
BLI_assert(parent_node != nullptr);
digraph.new_edge(from_dot_node.output(from_socket->index()),
to_dot_node.input(to_socket->index()));
}
for (const DGroupInput *group_input : to_socket->linked_group_inputs()) {
dot::NodeWithSocketsRef &from_dot_node = dot_group_inputs.lookup(group_input);
const int socket_index = socket_ref_->index();
return {parent_context, &parent_node->output(socket_index)};
}
digraph.new_edge(from_dot_node.output(0), to_dot_node.input(to_socket->index()));
Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() const
{
BLI_assert(*this);
BLI_assert(socket_ref_->node().is_group_node());
const DTreeContext *child_context = context_->child_context(socket_ref_->node());
BLI_assert(child_context != nullptr);
const NodeTreeRef &child_tree = child_context->tree();
Span<const NodeRef *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
const int socket_index = socket_ref_->index();
Vector<DOutputSocket> sockets;
for (const NodeRef *group_input_node : group_input_nodes) {
sockets.append(DOutputSocket(child_context, &group_input_node->output(socket_index)));
}
return sockets;
}
DInputSocket DOutputSocket::get_corresponding_group_node_input() const
{
BLI_assert(*this);
BLI_assert(socket_ref_->node().is_group_input_node());
BLI_assert(socket_ref_->index() < socket_ref_->node().outputs().size() - 1);
const DTreeContext *parent_context = context_->parent_context();
const NodeRef *parent_node = context_->parent_node();
BLI_assert(parent_context != nullptr);
BLI_assert(parent_node != nullptr);
const int socket_index = socket_ref_->index();
return {parent_context, &parent_node->input(socket_index)};
}
DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
{
BLI_assert(*this);
BLI_assert(socket_ref_->node().is_group_node());
const DTreeContext *child_context = context_->child_context(socket_ref_->node());
BLI_assert(child_context != nullptr);
const NodeTreeRef &child_tree = child_context->tree();
Span<const NodeRef *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
const int socket_index = socket_ref_->index();
for (const NodeRef *group_output_node : group_output_nodes) {
if (group_output_node->bnode()->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
return {child_context, &group_output_node->input(socket_index)};
}
}
return {};
}
/* Call the given callback for every "real" origin socket. "Real" means that reroutes, muted nodes
* and node groups are handled by this function. Origin sockets are ones where a node gets its
* inputs from. */
void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) const
{
BLI_assert(*this);
for (const OutputSocketRef *linked_socket : socket_ref_->as_input().linked_sockets()) {
const NodeRef &linked_node = linked_socket->node();
DOutputSocket linked_dsocket{context_, linked_socket};
if (linked_node.is_muted()) {
/* If the node is muted, follow the internal links of the node. */
for (const InternalLinkRef *internal_link : linked_node.internal_links()) {
if (&internal_link->to() == linked_socket) {
DInputSocket input_of_muted_node{context_, &internal_link->from()};
input_of_muted_node.foreach_origin_socket(callback);
}
}
}
else if (linked_node.is_group_input_node()) {
if (context_->is_root()) {
/* This is a group input in the root node group. */
callback(linked_dsocket);
}
else {
DInputSocket socket_in_parent_group = linked_dsocket.get_corresponding_group_node_input();
if (socket_in_parent_group->is_linked()) {
/* Follow the links coming into the corresponding socket on the parent group node. */
socket_in_parent_group.foreach_origin_socket(callback);
}
else {
/* The corresponding input on the parent group node is not connected. Therefore, we use
* the value of that input socket directly. */
callback(socket_in_parent_group);
}
}
}
else if (linked_node.is_group_node()) {
DInputSocket socket_in_group = linked_dsocket.get_active_corresponding_group_output_socket();
if (socket_in_group) {
if (socket_in_group->is_linked()) {
/* Follow the links coming into the group output node of the child node group. */
socket_in_group.foreach_origin_socket(callback);
}
else {
/* The output of the child node group is not connected, so we have to get the value from
* that socket. */
callback(socket_in_group);
}
}
}
else {
/* The normal case: just use the value of a linked output socket. */
callback(linked_dsocket);
}
}
}
digraph.set_random_cluster_bgcolors();
return digraph.to_dot_string();
/* Calls the given callback for every "real" target socket. "Real" means that reroutes, muted nodes
* and node groups are handled by this function. Target sockets are on the nodes that use the value
* from this socket. */
void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const
{
for (const InputSocketRef *linked_socket : socket_ref_->as_output().linked_sockets()) {
const NodeRef &linked_node = linked_socket->node();
DInputSocket linked_dsocket{context_, linked_socket};
if (linked_node.is_muted()) {
/* If the target node is muted, follow its internal links. */
for (const InternalLinkRef *internal_link : linked_node.internal_links()) {
if (&internal_link->from() == linked_socket) {
DOutputSocket output_of_muted_node{context_, &internal_link->to()};
output_of_muted_node.foreach_target_socket(callback);
}
}
}
else if (linked_node.is_group_output_node()) {
if (context_->is_root()) {
/* This is a group output in the root node group. */
callback(linked_dsocket);
}
else {
/* Follow the links going out of the group node in the parent node group. */
DOutputSocket socket_in_parent_group =
linked_dsocket.get_corresponding_group_node_output();
socket_in_parent_group.foreach_target_socket(callback);
}
}
else if (linked_node.is_group_node()) {
/* Follow the links within the nested node group. */
Vector<DOutputSocket> sockets_in_group =
linked_dsocket.get_corresponding_group_input_sockets();
for (DOutputSocket socket_in_group : sockets_in_group) {
socket_in_group.foreach_target_socket(callback);
}
}
else {
/* The normal case: just use the linked input socket as target. */
callback(linked_dsocket);
}
}
}
} // namespace blender::nodes

View File

@ -20,7 +20,6 @@
#include "DEG_depsgraph_query.h"
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry_exec.hh"
#include "NOD_type_callbacks.hh"
@ -30,7 +29,7 @@ namespace blender::nodes {
void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
{
bNodeTree *btree_cow = node_.node_ref().tree().btree();
bNodeTree *btree_cow = node_->btree();
BLI_assert(btree_cow != nullptr);
if (btree_cow == nullptr) {
return;
@ -40,12 +39,12 @@ void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::strin
const NodeTreeEvaluationContext context(*self_object_, *modifier_);
BKE_nodetree_error_message_add(
*btree_original, context, *node_.bnode(), type, std::move(message));
*btree_original, context, *node_->bnode(), type, std::move(message));
}
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
for (const DSocket *socket : node_.inputs()) {
for (const InputSocketRef *socket : node_->inputs()) {
if (socket->is_available() && socket->name() == name) {
return socket->bsocket();
}
@ -176,7 +175,7 @@ void GeoNodeExecParams::check_extract_input(StringRef identifier,
const CPPType *requested_type) const
{
bNodeSocket *found_socket = nullptr;
for (const DSocket *socket : node_.inputs()) {
for (const InputSocketRef *socket : node_->inputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@ -186,7 +185,7 @@ void GeoNodeExecParams::check_extract_input(StringRef identifier,
if (found_socket == nullptr) {
std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
for (const DSocket *socket : node_.inputs()) {
for (const InputSocketRef *socket : node_->inputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
@ -218,7 +217,7 @@ void GeoNodeExecParams::check_extract_input(StringRef identifier,
void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const
{
bNodeSocket *found_socket = nullptr;
for (const DSocket *socket : node_.outputs()) {
for (const OutputSocketRef *socket : node_->outputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@ -228,7 +227,7 @@ void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &va
if (found_socket == nullptr) {
std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
for (const DSocket *socket : node_.outputs()) {
for (const OutputSocketRef *socket : node_->outputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}

View File

@ -29,17 +29,17 @@ const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
Vector<fn::MFDataType, 10> input_types;
Vector<fn::MFDataType, 10> output_types;
for (const DInputSocket *dsocket : dnode_.inputs()) {
for (const InputSocketRef *dsocket : dnode_->inputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo());
if (data_type.has_value()) {
input_types.append(*data_type);
}
}
}
for (const DOutputSocket *dsocket : dnode_.outputs()) {
for (const OutputSocketRef *dsocket : dnode_->outputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo());
if (data_type.has_value()) {
output_types.append(*data_type);
}
@ -57,9 +57,9 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
Vector<fn::MFDataType, stack_capacity> input_types;
Vector<StringRef, stack_capacity> input_names;
Vector<const DInputSocket *, stack_capacity> input_dsockets;
Vector<const InputSocketRef *, stack_capacity> input_dsockets;
for (const DInputSocket *dsocket : dnode.inputs()) {
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()) {
@ -72,9 +72,9 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
Vector<fn::MFDataType, stack_capacity> output_types;
Vector<StringRef, stack_capacity> output_names;
Vector<const DOutputSocket *, stack_capacity> output_dsockets;
Vector<const OutputSocketRef *, stack_capacity> output_dsockets;
for (const DOutputSocket *dsocket : dnode.outputs()) {
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()) {
@ -86,20 +86,20 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
}
fn::MFDummyNode &dummy_node = common.network.add_dummy(
dnode.name(), input_types, output_types, input_names, output_names);
dnode->name(), input_types, output_types, input_names, output_names);
common.network_map.add(input_dsockets, dummy_node.inputs());
common.network_map.add(output_dsockets, dummy_node.outputs());
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 DInputSocket *socket : dnode.inputs()) {
for (const InputSocketRef *socket : dnode->inputs()) {
if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
return true;
}
}
for (const DOutputSocket *socket : dnode.outputs()) {
for (const OutputSocketRef *socket : dnode->outputs()) {
if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
return true;
}
@ -107,70 +107,39 @@ static bool has_data_sockets(const DNode &dnode)
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)
{
for (const DNode *dnode : common.tree.nodes()) {
const bNodeType *node_type = dnode->node_ref().bnode()->typeinfo;
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};
NodeMFNetworkBuilder builder{common, dnode};
node_type->expand_in_mf_network(builder);
}
else if (has_data_sockets(*dnode)) {
insert_dummy_node(common, *dnode);
else if (has_data_sockets(dnode)) {
insert_dummy_node(common, dnode);
}
}
}
static void insert_group_inputs(CommonMFNetworkBuilderData &common)
{
for (const DGroupInput *group_input : common.tree.group_inputs()) {
bNodeSocket *bsocket = group_input->bsocket();
if (socket_is_mf_data_socket(*bsocket->typeinfo)) {
bNodeSocketType *socktype = bsocket->typeinfo;
BLI_assert(socktype->expand_in_mf_network != nullptr);
SocketMFNetworkBuilder builder{common, *group_input};
socktype->expand_in_mf_network(builder);
fn::MFOutputSocket *from_socket = builder.built_socket();
BLI_assert(from_socket != nullptr);
common.network_map.add(*group_input, *from_socket);
}
}
}
static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common,
const DInputSocket &to_dsocket)
{
Span<const DOutputSocket *> from_dsockets = to_dsocket.linked_sockets();
Span<const DGroupInput *> from_group_inputs = to_dsocket.linked_group_inputs();
int total_linked_amount = from_dsockets.size() + from_group_inputs.size();
BLI_assert(total_linked_amount <= 1);
if (total_linked_amount == 0) {
return nullptr;
}
if (from_dsockets.size() == 1) {
const DOutputSocket &from_dsocket = *from_dsockets[0];
if (!from_dsocket.is_available()) {
return nullptr;
}
if (socket_is_mf_data_socket(*from_dsocket.bsocket()->typeinfo)) {
return &common.network_map.lookup(from_dsocket);
}
return nullptr;
}
const DGroupInput &from_group_input = *from_group_inputs[0];
if (socket_is_mf_data_socket(*from_group_input.bsocket()->typeinfo)) {
return &common.network_map.lookup(from_group_input);
}
return nullptr;
});
}
template<typename From, typename To>
@ -286,78 +255,82 @@ static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderD
return node.output(0);
}
static void insert_links(CommonMFNetworkBuilderData &common)
static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common,
const DInputSocket &dsocket)
{
for (const DInputSocket *to_dsocket : common.tree.input_sockets()) {
if (!to_dsocket->is_available()) {
continue;
}
if (!to_dsocket->is_linked()) {
continue;
}
if (!socket_is_mf_data_socket(*to_dsocket->bsocket()->typeinfo)) {
continue;
}
Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(*to_dsocket);
BLI_assert(to_sockets.size() >= 1);
fn::MFDataType to_type = to_sockets[0]->data_type();
fn::MFOutputSocket *from_socket = try_find_origin(common, *to_dsocket);
if (from_socket == nullptr) {
from_socket = &insert_default_value_for_type(common, to_type);
}
fn::MFDataType from_type = from_socket->data_type();
if (from_type != to_type) {
const fn::MultiFunction *conversion_fn = get_implicit_type_conversions().get_conversion(
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);
}
}
}
static void insert_unlinked_input(CommonMFNetworkBuilderData &common, const DInputSocket &dsocket)
{
bNodeSocket *bsocket = dsocket.bsocket();
bNodeSocketType *socktype = bsocket->typeinfo;
BLI_assert(socktype->expand_in_mf_network != nullptr);
BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo()));
SocketMFNetworkBuilder builder{common, dsocket};
socktype->expand_in_mf_network(builder);
socket_expand_in_mf_network(builder);
fn::MFOutputSocket *from_socket = builder.built_socket();
BLI_assert(from_socket != nullptr);
for (fn::MFInputSocket *to_socket : common.network_map.lookup(dsocket)) {
common.network.add_link(*from_socket, *to_socket);
}
fn::MFOutputSocket *built_socket = builder.built_socket();
BLI_assert(built_socket != nullptr);
return built_socket;
}
static void insert_unlinked_inputs(CommonMFNetworkBuilderData &common)
static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
{
Vector<const DInputSocket *> unlinked_data_inputs;
for (const DInputSocket *dsocket : common.tree.input_sockets()) {
if (dsocket->is_available()) {
if (socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) {
if (!dsocket->is_linked()) {
insert_unlinked_input(common, *dsocket);
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(
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);
}
}
}
});
}
/**
@ -376,9 +349,7 @@ MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
CommonMFNetworkBuilderData common{resources, network, network_map, tree};
insert_nodes(common);
insert_group_inputs(common);
insert_links(common);
insert_unlinked_inputs(common);
insert_links_and_unlinked_inputs(common);
return network_map;
}
@ -420,16 +391,17 @@ static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map,
}
};
for (const DInputSocket *dsocket : dnode.inputs()) {
for (const InputSocketRef *dsocket : dnode->inputs()) {
if (dsocket->is_available()) {
for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) {
for (fn::MFInputSocket *mf_input :
network_map.lookup(DInputSocket(dnode.context(), dsocket))) {
check_mf_node(mf_input->node());
}
}
}
for (const DOutputSocket *dsocket : dnode.outputs()) {
for (const OutputSocketRef *dsocket : dnode->outputs()) {
if (dsocket->is_available()) {
fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket);
fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket));
check_mf_node(mf_output.node());
}
}
@ -451,20 +423,21 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
ResourceCollector &resources)
{
Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
for (const DInputSocket *dsocket : dnode.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(*dsocket)) {
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 DOutputSocket *dsocket : dnode.outputs()) {
for (const OutputSocketRef *dsocket : dnode->outputs()) {
if (dsocket->is_available()) {
fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket);
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);
@ -492,18 +465,18 @@ MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
CommonMFNetworkBuilderData common{resources, network, network_map, tree};
for (const DNode *dnode : tree.nodes()) {
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. */
continue;
return;
}
NodeMFNetworkBuilder builder{common, *dnode};
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);
const NodeExpandType expand_type = get_node_expand_type(network_map, dnode, &single_function);
switch (expand_type) {
case NodeExpandType::HasDummyNodes: {
@ -519,12 +492,12 @@ MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
/* 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, resources);
dnode, network, network_map, resources);
functions_by_node.add_new(dnode, &fn);
break;
}
}
}
});
return functions_by_node;
}

View File

@ -116,7 +116,7 @@ static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuild
blender::fn::MFNetwork &network = builder.network();
blender::fn::MFFunctionNode &base_node = network.add_function(base_function);
builder.network_map().add_try_match(dnode.inputs(), base_node.inputs());
builder.network_map().add_try_match(*dnode.context(), dnode->inputs(), base_node.inputs());
const bool clamp_output = builder.bnode().custom2 != 0;
if (clamp_output) {
@ -126,10 +126,12 @@ static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuild
}};
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(dnode.output(0), clamp_node.output(0));
builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)),
clamp_node.output(0));
}
else {
builder.network_map().add(dnode.output(0), base_node.output(0));
builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)),
base_node.output(0));
}
}

View File

@ -41,7 +41,7 @@ static int gpu_shader_value(GPUMaterial *mat,
static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
const bNodeSocket *bsocket = builder.dnode().output(0).bsocket();
const bNodeSocket *bsocket = builder.dnode()->output(0).bsocket();
const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value;
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value);
}