Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2021-03-06 18:51:06 +0300
committerJacques Lucke <jacques@blender.org>2021-03-06 18:51:06 +0300
commitd2869943d2c02f1535270f0e206a67aab78c8ebb (patch)
tree0ff71fcf21165f21747c284aaf52c027535d2d10 /source/blender/modifiers
parentcfd766cebdf6f65e3184229277985b95128f9ad2 (diff)
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
Diffstat (limited to 'source/blender/modifiers')
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc261
1 files changed, 137 insertions, 124 deletions
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index b47f5806c9c..4d1823b8951 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -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)
{
+ Vector<DSocket> from_sockets;
+ socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); });
- 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();
+ /* Multi-input sockets contain a vector of inputs. */
+ if (socket_to_compute->is_multi_input_socket()) {
+ Vector<GMutablePointer> values;
+ for (const DSocket from_socket : from_sockets) {
+ GMutablePointer value = get_input_from_incoming_link(socket_to_compute, from_socket);
+ values.append(value);
+ }
+ return values;
+ }
- if (total_inputs == 0) {
+ if (from_sockets.is_empty()) {
/* The input is not connected, use the value from the socket itself. */
- return {get_unlinked_input_value(socket_to_compute)};
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
+ return {get_unlinked_input_value(socket_to_compute, type)};
}
- if (from_group_inputs.size() == 1) {
- return {get_unlinked_input_value(socket_to_compute)};
- }
+ const DSocket from_socket = from_sockets[0];
+ GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket);
+ return {value};
+ }
- /* Multi-input sockets contain a vector of inputs. */
- 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);
- }
+ GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute,
+ const DSocket from_socket)
+ {
+ if (from_socket->is_output()) {
+ const DOutputSocket from_output_socket{from_socket};
+ const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute,
+ from_output_socket);
+ std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
+ if (value.has_value()) {
+ /* This input has been computed before, return it directly. */
+ return {*value};
}
- 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};
+ /* Compute the socket now. */
+ this->compute_output_and_forward(from_output_socket);
+ return {value_by_input_.pop(key)};
}
- /* Compute the socket now. */
- this->compute_output_and_forward(from_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)
+ void compute_output_and_forward(const DOutputSocket socket_to_compute)
{
- const DNode &node = socket_to_compute.node();
+ const DNode node{socket_to_compute.context(), &socket_to_compute->node()};
- if (!socket_to_compute.is_available()) {
+ 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;
}