This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc

477 lines
17 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "NOD_node_declaration.hh"
#include "BKE_node_runtime.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_resource_scope.hh"
#include "BLI_set.hh"
#include "BLI_stack.hh"
#include "BLI_timeit.hh"
namespace blender::bke::anonymous_attribute_inferencing {
namespace aal = nodes::aal;
using nodes::NodeDeclaration;
static bool socket_is_field(const bNodeSocket &socket)
{
return socket.display_shape == SOCK_DISPLAY_SHAPE_DIAMOND;
}
static const aal::RelationsInNode &get_relations_in_node(const bNode &node, ResourceScope &scope)
{
if (node.is_group()) {
if (const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node.id)) {
/* Undefined tree types have no relations. */
if (!ntreeIsRegistered(group)) {
return scope.construct<aal::RelationsInNode>();
}
BLI_assert(group->runtime->anonymous_attribute_relations);
return *group->runtime->anonymous_attribute_relations;
}
}
if (node.is_reroute()) {
const bNodeSocket &socket = node.input_socket(0);
if (socket_is_field(socket)) {
static const aal::RelationsInNode field_relations = []() {
aal::RelationsInNode relations;
relations.reference_relations.append({0, 0});
return relations;
}();
return field_relations;
}
if (socket.type == SOCK_GEOMETRY) {
static const aal::RelationsInNode geometry_relations = []() {
aal::RelationsInNode relations;
relations.propagate_relations.append({0, 0});
return relations;
}();
return geometry_relations;
}
}
if (const NodeDeclaration *node_decl = node.declaration()) {
if (const aal::RelationsInNode *relations = node_decl->anonymous_attribute_relations()) {
return *relations;
}
}
return scope.construct<aal::RelationsInNode>();
}
Array<const aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
ResourceScope &scope)
{
const Span<const bNode *> nodes = tree.all_nodes();
Array<const aal::RelationsInNode *> relations_by_node(nodes.size());
for (const int i : nodes.index_range()) {
relations_by_node[i] = &get_relations_in_node(*nodes[i], scope);
}
return relations_by_node;
}
/**
* Start at a group output socket and find all linked group inputs.
*/
static Vector<int> find_linked_group_inputs(
const bNodeTree &tree,
const bNodeSocket &group_output_socket,
const FunctionRef<Vector<int>(const bNodeSocket &)> get_linked_node_inputs)
{
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
Vector<int> input_indices;
found_sockets.add_new(&group_output_socket);
sockets_to_check.push(&group_output_socket);
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
for (const int input_index : get_linked_node_inputs(socket)) {
const bNodeSocket &input_socket = node.input_socket(input_index);
if (input_socket.is_available()) {
if (found_sockets.add(&input_socket)) {
sockets_to_check.push(&input_socket);
}
}
}
}
}
for (const bNode *node : tree.group_input_nodes()) {
for (const bNodeSocket *socket : node->output_sockets()) {
if (found_sockets.contains(socket)) {
input_indices.append_non_duplicates(socket->index());
}
}
}
return input_indices;
}
static void infer_propagate_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (group_output_socket->type != SOCK_GEOMETRY) {
continue;
}
const Vector<int> input_indices = find_linked_group_inputs(
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
Vector<int> indices;
for (const aal::PropagateRelation &relation :
relations_by_node[output_socket.owner_node().index()]->propagate_relations) {
if (relation.to_geometry_output == output_socket.index()) {
indices.append(relation.from_geometry_input);
}
}
return indices;
});
for (const int input_index : input_indices) {
aal::PropagateRelation relation;
relation.from_geometry_input = input_index;
relation.to_geometry_output = group_output_socket->index();
r_relations.propagate_relations.append(relation);
}
}
}
static void infer_reference_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (!socket_is_field(*group_output_socket)) {
continue;
}
const Vector<int> input_indices = find_linked_group_inputs(
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
Vector<int> indices;
for (const aal::ReferenceRelation &relation :
relations_by_node[output_socket.owner_node().index()]->reference_relations) {
if (relation.to_field_output == output_socket.index()) {
indices.append(relation.from_field_input);
}
}
return indices;
});
for (const int input_index : input_indices) {
if (tree.runtime->field_inferencing_interface->inputs[input_index] !=
nodes::InputSocketFieldType::None) {
aal::ReferenceRelation relation;
relation.from_field_input = input_index;
relation.to_field_output = group_output_socket->index();
r_relations.reference_relations.append(relation);
}
}
}
}
/**
* Find group output geometries that contain anonymous attributes referenced by the field.
* If `nullopt` is returned, the field does not depend on any anonymous attribute created in this
* node tree.
*/
static std::optional<Vector<int>> find_available_on_outputs(
const bNodeSocket &initial_group_output_socket,
const bNode &group_output_node,
const Span<const aal::RelationsInNode *> relations_by_node)
{
Set<const bNodeSocket *> geometry_sockets;
{
/* Find the nodes that added anonymous attributes to the field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
found_sockets.add_new(&initial_group_output_socket);
sockets_to_check.push(&initial_group_output_socket);
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::AvailableRelation &relation : relations.available_relations) {
if (socket.index() == relation.field_output) {
const bNodeSocket &geometry_output = node.output_socket(relation.geometry_output);
if (geometry_output.is_available()) {
geometry_sockets.add(&geometry_output);
}
}
}
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
if (socket.index() == relation.to_field_output) {
const bNodeSocket &field_input = node.input_socket(relation.from_field_input);
if (field_input.is_available()) {
if (found_sockets.add(&field_input)) {
sockets_to_check.push(&field_input);
}
}
}
}
}
}
}
if (geometry_sockets.is_empty()) {
/* The field does not depend on any anonymous attribute created within this node tree. */
return std::nullopt;
}
/* Find the group output geometries that contain the anonymous attribute referenced by the field
* output. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
for (const bNodeSocket *socket : geometry_sockets) {
found_sockets.add_new(socket);
sockets_to_check.push(socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
if (socket.index() == relation.from_geometry_input) {
const bNodeSocket &output_socket = node.output_socket(relation.to_geometry_output);
if (output_socket.is_available()) {
if (found_sockets.add(&output_socket)) {
sockets_to_check.push(&output_socket);
}
}
}
}
}
else {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &to_socket = *link->tosock;
if (found_sockets.add(&to_socket)) {
sockets_to_check.push(&to_socket);
}
}
}
}
}
Vector<int> output_indices;
for (const bNodeSocket *socket : group_output_node.input_sockets().drop_back(1)) {
if (found_sockets.contains(socket)) {
output_indices.append(socket->index());
}
}
return output_indices;
}
static void infer_available_relations(const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (!socket_is_field(*group_output_socket)) {
continue;
}
const std::optional<Vector<int>> output_indices = find_available_on_outputs(
*group_output_socket, group_output_node, relations_by_node);
if (output_indices.has_value()) {
if (output_indices->is_empty()) {
r_relations.available_on_none.append(group_output_socket->index());
}
else {
for (const int output_index : *output_indices) {
aal::AvailableRelation relation;
relation.field_output = group_output_socket->index();
relation.geometry_output = output_index;
r_relations.available_relations.append(relation);
}
}
}
}
}
/**
* Returns a list of all the geometry inputs that the field input may be evaluated on.
*/
static Vector<int> find_eval_on_inputs(const bNodeTree &tree,
const int field_input_index,
const Span<const aal::RelationsInNode *> relations_by_node)
{
const Span<const bNode *> group_input_nodes = tree.group_input_nodes();
Set<const bNodeSocket *> geometry_sockets;
{
/* Find all the nodes that evaluate the input field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
for (const bNode *node : group_input_nodes) {
const bNodeSocket &socket = node->output_socket(field_input_index);
found_sockets.add_new(&socket);
sockets_to_check.push(&socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::EvalRelation &relation : relations.eval_relations) {
if (socket.index() == relation.field_input) {
const bNodeSocket &geometry_input = node.input_socket(relation.geometry_input);
if (geometry_input.is_available()) {
geometry_sockets.add(&geometry_input);
}
}
}
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
if (socket.index() == relation.from_field_input) {
const bNodeSocket &field_output = node.output_socket(relation.to_field_output);
if (field_output.is_available()) {
if (found_sockets.add(&field_output)) {
sockets_to_check.push(&field_output);
}
}
}
}
}
else {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &to_socket = *link->tosock;
if (found_sockets.add(&to_socket)) {
sockets_to_check.push(&to_socket);
}
}
}
}
}
}
if (geometry_sockets.is_empty()) {
return {};
}
/* Find the group input geometries whose attributes are propagated to the nodes that evaluate the
* field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
Vector<int> geometry_input_indices;
for (const bNodeSocket *socket : geometry_sockets) {
found_sockets.add_new(socket);
sockets_to_check.push(socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
if (node.is_group_input()) {
geometry_input_indices.append_non_duplicates(socket.index());
}
else {
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
if (socket.index() == relation.to_geometry_output) {
const bNodeSocket &input_socket = node.input_socket(relation.from_geometry_input);
if (input_socket.is_available()) {
if (found_sockets.add(&input_socket)) {
sockets_to_check.push(&input_socket);
}
}
}
}
}
}
}
return geometry_input_indices;
}
static void infer_eval_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
aal::RelationsInNode &r_relations)
{
for (const int input_index : tree.interface_inputs().index_range()) {
if (tree.runtime->field_inferencing_interface->inputs[input_index] ==
nodes::InputSocketFieldType::None) {
continue;
}
const Vector<int> geometry_input_indices = find_eval_on_inputs(
tree, input_index, relations_by_node);
for (const int geometry_input : geometry_input_indices) {
aal::EvalRelation relation;
relation.field_input = input_index;
relation.geometry_input = geometry_input;
r_relations.eval_relations.append(std::move(relation));
}
}
}
bool update_anonymous_attribute_relations(bNodeTree &tree)
{
tree.ensure_topology_cache();
ResourceScope scope;
Array<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
std::unique_ptr<aal::RelationsInNode> new_relations = std::make_unique<aal::RelationsInNode>();
if (!tree.has_available_link_cycle()) {
if (const bNode *group_output_node = tree.group_output_node()) {
infer_propagate_relations(tree, relations_by_node, *group_output_node, *new_relations);
infer_reference_relations(tree, relations_by_node, *group_output_node, *new_relations);
infer_available_relations(relations_by_node, *group_output_node, *new_relations);
}
infer_eval_relations(tree, relations_by_node, *new_relations);
}
const bool group_interface_changed = !tree.runtime->anonymous_attribute_relations ||
*tree.runtime->anonymous_attribute_relations !=
*new_relations;
tree.runtime->anonymous_attribute_relations = std::move(new_relations);
return group_interface_changed;
}
} // namespace blender::bke::anonymous_attribute_inferencing