WIP: Field type inferencing using a constraint solver method #120420
|
@ -381,6 +381,32 @@ inline bool topology_cache_is_available(const bNodeSocket &socket)
|
|||
namespace node_field_inferencing {
|
||||
bool update_field_inferencing(const bNodeTree &tree);
|
||||
bool dump_field_inferencing_debug_data(const bNodeTree &tree, StringRef filepath);
|
||||
|
||||
/**
|
||||
* This struct contains information for every socket. The values are propagated through the
|
||||
* network.
|
||||
*/
|
||||
struct SocketFieldStateLegacy {
|
||||
/* This socket starts a new field. */
|
||||
bool is_field_source = false;
|
||||
/* This socket can never become a field, because the node itself does not support it. */
|
||||
bool is_always_single = false;
|
||||
/* This socket is currently a single value. It could become a field though. */
|
||||
bool is_single = true;
|
||||
/* This socket is required to be a single value. This can be because the node itself only
|
||||
* supports this socket to be a single value, or because a node afterwards requires this to be a
|
||||
* single value. */
|
||||
bool requires_single = false;
|
||||
};
|
||||
Array<SocketFieldStateLegacy> solve_field_types_legacy(
|
||||
const bNodeTree &tree,
|
||||
const Span<const nodes::FieldInferencingInterface *> interface_by_node,
|
||||
nodes::FieldInferencingInterface &inferencing_interface);
|
||||
void update_field_inferencing_legacy(
|
||||
const bNodeTree &tree,
|
||||
Span<const nodes::FieldInferencingInterface *> interface_by_node,
|
||||
nodes::FieldInferencingInterface &inferencing_interface);
|
||||
|
||||
} // namespace node_field_inferencing
|
||||
} // namespace blender::bke
|
||||
|
||||
|
|
|
@ -240,6 +240,7 @@ set(SRC
|
|||
intern/node_tree_anonymous_attributes.cc
|
||||
intern/node_tree_dot_export.cc
|
||||
intern/node_tree_field_inferencing.cc
|
||||
intern/node_tree_field_inferencing_legacy.cc
|
||||
intern/node_tree_interface.cc
|
||||
intern/node_tree_update.cc
|
||||
intern/node_tree_zones.cc
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
|
@ -13,13 +11,9 @@
|
|||
#include "NOD_socket.hh"
|
||||
|
||||
#include "BLI_constraint_satisfaction.hh"
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_resource_scope.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_stack.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_tempfile.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
@ -150,23 +144,6 @@ static const FieldInferencingInterface &get_node_field_inferencing_interface(con
|
|||
return inferencing_interface;
|
||||
}
|
||||
|
||||
/**
|
||||
* This struct contains information for every socket. The values are propagated through the
|
||||
* network.
|
||||
*/
|
||||
struct SocketFieldState {
|
||||
/* This socket starts a new field. */
|
||||
bool is_field_source = false;
|
||||
/* This socket can never become a field, because the node itself does not support it. */
|
||||
bool is_always_single = false;
|
||||
/* This socket is currently a single value. It could become a field though. */
|
||||
bool is_single = true;
|
||||
/* This socket is required to be a single value. This can be because the node itself only
|
||||
* supports this socket to be a single value, or because a node afterwards requires this to be a
|
||||
* single value. */
|
||||
bool requires_single = false;
|
||||
};
|
||||
|
||||
static Vector<const bNodeSocket *> gather_input_socket_dependencies(
|
||||
const OutputFieldDependency &field_dependency, const bNode &node)
|
||||
{
|
||||
|
@ -193,512 +170,6 @@ static Vector<const bNodeSocket *> gather_input_socket_dependencies(
|
|||
return input_sockets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check what the group output socket depends on. Potentially traverses the node tree
|
||||
* to figure out if it is always a field or if it depends on any group inputs.
|
||||
*/
|
||||
static OutputFieldDependency find_group_output_dependencies(
|
||||
const bNodeSocket &group_output_socket,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const Span<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
if (!is_field_socket_type(group_output_socket)) {
|
||||
return OutputFieldDependency::ForDataSource();
|
||||
}
|
||||
|
||||
/* Use a Set here instead of an array indexed by socket id, because we my only need to look at
|
||||
* very few sockets. */
|
||||
Set<const bNodeSocket *> handled_sockets;
|
||||
Stack<const bNodeSocket *> sockets_to_check;
|
||||
|
||||
handled_sockets.add(&group_output_socket);
|
||||
sockets_to_check.push(&group_output_socket);
|
||||
|
||||
/* Keeps track of group input indices that are (indirectly) connected to the output. */
|
||||
Vector<int> linked_input_indices;
|
||||
|
||||
while (!sockets_to_check.is_empty()) {
|
||||
const bNodeSocket *input_socket = sockets_to_check.pop();
|
||||
|
||||
if (!input_socket->is_directly_linked() &&
|
||||
!field_state_by_socket_id[input_socket->index_in_tree()].is_single)
|
||||
{
|
||||
/* This socket uses a field as input by default. */
|
||||
return OutputFieldDependency::ForFieldSource();
|
||||
}
|
||||
|
||||
for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
|
||||
const bNode &origin_node = origin_socket->owner_node();
|
||||
const SocketFieldState &origin_state =
|
||||
field_state_by_socket_id[origin_socket->index_in_tree()];
|
||||
|
||||
if (origin_state.is_field_source) {
|
||||
if (origin_node.type == NODE_GROUP_INPUT) {
|
||||
/* Found a group input that the group output depends on. */
|
||||
linked_input_indices.append_non_duplicates(origin_socket->index());
|
||||
}
|
||||
else {
|
||||
/* Found a field source that is not the group input. So the output is always a field. */
|
||||
return OutputFieldDependency::ForFieldSource();
|
||||
}
|
||||
}
|
||||
else if (!origin_state.is_single) {
|
||||
const FieldInferencingInterface &inferencing_interface =
|
||||
*interface_by_node[origin_node.index()];
|
||||
const OutputFieldDependency &field_dependency =
|
||||
inferencing_interface.outputs[origin_socket->index()];
|
||||
|
||||
/* Propagate search further to the left. */
|
||||
for (const bNodeSocket *origin_input_socket :
|
||||
gather_input_socket_dependencies(field_dependency, origin_node))
|
||||
{
|
||||
if (!origin_input_socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (!field_state_by_socket_id[origin_input_socket->index_in_tree()].is_single) {
|
||||
if (handled_sockets.add(origin_input_socket)) {
|
||||
sockets_to_check.push(origin_input_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices));
|
||||
}
|
||||
|
||||
/** Result of syncing two field states. */
|
||||
enum class eFieldStateSyncResult : char {
|
||||
/* Nothing changed. */
|
||||
NONE = 0,
|
||||
/* State A has been modified. */
|
||||
CHANGED_A = (1 << 0),
|
||||
/* State B has been modified. */
|
||||
CHANGED_B = (1 << 1),
|
||||
};
|
||||
ENUM_OPERATORS(eFieldStateSyncResult, eFieldStateSyncResult::CHANGED_B)
|
||||
|
||||
/**
|
||||
* Compare both field states and select the most compatible.
|
||||
* Afterwards both field states will be the same.
|
||||
* \return eFieldStateSyncResult flags indicating which field states have changed.
|
||||
*/
|
||||
static eFieldStateSyncResult sync_field_states(SocketFieldState &a, SocketFieldState &b)
|
||||
{
|
||||
const bool requires_single = a.requires_single || b.requires_single;
|
||||
const bool is_single = a.is_single && b.is_single;
|
||||
|
||||
eFieldStateSyncResult res = eFieldStateSyncResult::NONE;
|
||||
if (a.requires_single != requires_single || a.is_single != is_single) {
|
||||
res |= eFieldStateSyncResult::CHANGED_A;
|
||||
}
|
||||
if (b.requires_single != requires_single || b.is_single != is_single) {
|
||||
res |= eFieldStateSyncResult::CHANGED_B;
|
||||
}
|
||||
|
||||
a.requires_single = requires_single;
|
||||
b.requires_single = requires_single;
|
||||
a.is_single = is_single;
|
||||
b.is_single = is_single;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare field states of simulation nodes sockets and select the most compatible.
|
||||
* Afterwards all field states will be the same.
|
||||
* \return eFieldStateSyncResult flags indicating which field states have changed.
|
||||
*/
|
||||
static eFieldStateSyncResult simulation_nodes_field_state_sync(
|
||||
const bNode &input_node,
|
||||
const bNode &output_node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
eFieldStateSyncResult res = eFieldStateSyncResult::NONE;
|
||||
for (const int i : output_node.output_sockets().index_range()) {
|
||||
/* First input node output is Delta Time which does not appear in the output node outputs. */
|
||||
const bNodeSocket &input_socket = input_node.output_socket(i + 1);
|
||||
const bNodeSocket &output_socket = output_node.output_socket(i);
|
||||
SocketFieldState &input_state = field_state_by_socket_id[input_socket.index_in_tree()];
|
||||
SocketFieldState &output_state = field_state_by_socket_id[output_socket.index_in_tree()];
|
||||
res |= sync_field_states(input_state, output_state);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static eFieldStateSyncResult repeat_field_state_sync(
|
||||
const bNode &input_node,
|
||||
const bNode &output_node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
eFieldStateSyncResult res = eFieldStateSyncResult::NONE;
|
||||
for (const int i : output_node.output_sockets().index_range()) {
|
||||
const bNodeSocket &input_socket = input_node.output_socket(i);
|
||||
const bNodeSocket &output_socket = output_node.output_socket(i);
|
||||
SocketFieldState &input_state = field_state_by_socket_id[input_socket.index_in_tree()];
|
||||
SocketFieldState &output_state = field_state_by_socket_id[output_socket.index_in_tree()];
|
||||
res |= sync_field_states(input_state, output_state);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool propagate_special_data_requirements(
|
||||
const bNodeTree &tree,
|
||||
const bNode &node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
tree.ensure_topology_cache();
|
||||
|
||||
bool need_update = false;
|
||||
|
||||
/* Sync field state between zone nodes and schedule another pass if necessary. */
|
||||
switch (node.type) {
|
||||
case GEO_NODE_SIMULATION_INPUT: {
|
||||
const NodeGeometrySimulationInput &data = *static_cast<const NodeGeometrySimulationInput *>(
|
||||
node.storage);
|
||||
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
|
||||
const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync(
|
||||
node, *output_node, field_state_by_socket_id);
|
||||
if (bool(sync_result & eFieldStateSyncResult::CHANGED_B)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SIMULATION_OUTPUT: {
|
||||
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) {
|
||||
const NodeGeometrySimulationInput &data =
|
||||
*static_cast<const NodeGeometrySimulationInput *>(input_node->storage);
|
||||
if (node.identifier == data.output_node_id) {
|
||||
const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync(
|
||||
*input_node, node, field_state_by_socket_id);
|
||||
if (bool(sync_result & eFieldStateSyncResult::CHANGED_A)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_REPEAT_INPUT: {
|
||||
const NodeGeometryRepeatInput &data = *static_cast<const NodeGeometryRepeatInput *>(
|
||||
node.storage);
|
||||
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
|
||||
const eFieldStateSyncResult sync_result = repeat_field_state_sync(
|
||||
node, *output_node, field_state_by_socket_id);
|
||||
if (bool(sync_result & eFieldStateSyncResult::CHANGED_B)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_REPEAT_OUTPUT: {
|
||||
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeRepeatInput")) {
|
||||
const NodeGeometryRepeatInput &data = *static_cast<const NodeGeometryRepeatInput *>(
|
||||
input_node->storage);
|
||||
if (node.identifier == data.output_node_id) {
|
||||
const eFieldStateSyncResult sync_result = repeat_field_state_sync(
|
||||
*input_node, node, field_state_by_socket_id);
|
||||
if (bool(sync_result & eFieldStateSyncResult::CHANGED_A)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return need_update;
|
||||
}
|
||||
|
||||
static void propagate_data_requirements_from_right_to_left(
|
||||
const bNodeTree &tree,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
const Span<const bNode *> toposort_result = tree.toposort_right_to_left();
|
||||
|
||||
while (true) {
|
||||
/* Node updates may require several passes due to cyclic dependencies caused by simulation or
|
||||
* repeat input/output nodes. */
|
||||
bool need_update = false;
|
||||
|
||||
for (const bNode *node : toposort_result) {
|
||||
const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()];
|
||||
|
||||
for (const bNodeSocket *output_socket : node->output_sockets()) {
|
||||
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
|
||||
|
||||
const OutputFieldDependency &field_dependency =
|
||||
inferencing_interface.outputs[output_socket->index()];
|
||||
|
||||
if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) {
|
||||
continue;
|
||||
}
|
||||
if (field_dependency.field_type() == OutputSocketFieldType::None) {
|
||||
state.requires_single = true;
|
||||
state.is_always_single = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The output is required to be a single value when it is connected to any input that does
|
||||
* not support fields. */
|
||||
for (const bNodeSocket *target_socket : output_socket->directly_linked_sockets()) {
|
||||
if (target_socket->is_available()) {
|
||||
state.requires_single |=
|
||||
field_state_by_socket_id[target_socket->index_in_tree()].requires_single;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.requires_single) {
|
||||
bool any_input_is_field_implicitly = false;
|
||||
const Vector<const bNodeSocket *> connected_inputs = gather_input_socket_dependencies(
|
||||
field_dependency, *node);
|
||||
for (const bNodeSocket *input_socket : connected_inputs) {
|
||||
if (!input_socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (inferencing_interface.inputs[input_socket->index()] ==
|
||||
InputSocketFieldType::Implicit)
|
||||
{
|
||||
if (!input_socket->is_logically_linked()) {
|
||||
any_input_is_field_implicitly = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (any_input_is_field_implicitly) {
|
||||
/* This output isn't a single value actually. */
|
||||
state.requires_single = false;
|
||||
}
|
||||
else {
|
||||
/* If the output is required to be a single value, the connected inputs in the same
|
||||
* node must not be fields as well. */
|
||||
for (const bNodeSocket *input_socket : connected_inputs) {
|
||||
field_state_by_socket_id[input_socket->index_in_tree()].requires_single = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Some inputs do not require fields independent of what the outputs are connected to. */
|
||||
for (const bNodeSocket *input_socket : node->input_sockets()) {
|
||||
SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
|
||||
if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) {
|
||||
state.requires_single = true;
|
||||
state.is_always_single = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find reverse dependencies and resolve conflicts, which may require another pass. */
|
||||
if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!need_update) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void determine_group_input_states(
|
||||
const bNodeTree &tree,
|
||||
FieldInferencingInterface &new_inferencing_interface,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
{
|
||||
/* Non-field inputs never support fields. */
|
||||
for (const int index : tree.interface_inputs().index_range()) {
|
||||
const bNodeTreeInterfaceSocket *group_input = tree.interface_inputs()[index];
|
||||
const bNodeSocketType *typeinfo = group_input->socket_typeinfo();
|
||||
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
|
||||
SOCK_CUSTOM;
|
||||
if (!nodes::socket_type_supports_fields(type)) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::None;
|
||||
}
|
||||
else if (group_input->default_input != NODE_INPUT_DEFAULT_VALUE) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::Implicit;
|
||||
}
|
||||
else if (is_layer_selection_field(*group_input)) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::Implicit;
|
||||
}
|
||||
else if (group_input->flag & NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Check if group inputs are required to be single values, because they are (indirectly)
|
||||
* connected to some socket that does not support fields. */
|
||||
for (const bNode *node : tree.group_input_nodes()) {
|
||||
for (const bNodeSocket *output_socket : node->output_sockets().drop_back(1)) {
|
||||
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
|
||||
const int output_index = output_socket->index();
|
||||
if (state.requires_single) {
|
||||
if (new_inferencing_interface.inputs[output_index] == InputSocketFieldType::Implicit) {
|
||||
/* Don't override hard-coded implicit fields. */
|
||||
continue;
|
||||
}
|
||||
new_inferencing_interface.inputs[output_index] = InputSocketFieldType::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If an input does not support fields, this should be reflected in all Group Input nodes. */
|
||||
for (const bNode *node : tree.group_input_nodes()) {
|
||||
for (const bNodeSocket *output_socket : node->output_sockets().drop_back(1)) {
|
||||
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
|
||||
const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] !=
|
||||
InputSocketFieldType::None;
|
||||
if (supports_field) {
|
||||
state.is_single = false;
|
||||
state.is_field_source = true;
|
||||
}
|
||||
else {
|
||||
state.requires_single = true;
|
||||
}
|
||||
}
|
||||
SocketFieldState &dummy_socket_state =
|
||||
field_state_by_socket_id[node->output_sockets().last()->index_in_tree()];
|
||||
dummy_socket_state.requires_single = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void propagate_field_status_from_left_to_right(
|
||||
const bNodeTree &tree,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
const Span<const bNode *> toposort_result = tree.toposort_left_to_right();
|
||||
|
||||
while (true) {
|
||||
/* Node updates may require several passes due to cyclic dependencies. */
|
||||
bool need_update = false;
|
||||
|
||||
for (const bNode *node : toposort_result) {
|
||||
if (node->type == NODE_GROUP_INPUT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()];
|
||||
|
||||
/* Update field state of input sockets, also taking into account linked origin sockets. */
|
||||
for (const bNodeSocket *input_socket : node->input_sockets()) {
|
||||
SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
|
||||
if (state.is_always_single) {
|
||||
state.is_single = true;
|
||||
continue;
|
||||
}
|
||||
state.is_single = true;
|
||||
if (!input_socket->is_directly_linked()) {
|
||||
if (inferencing_interface.inputs[input_socket->index()] ==
|
||||
InputSocketFieldType::Implicit)
|
||||
{
|
||||
state.is_single = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
|
||||
if (!field_state_by_socket_id[origin_socket->index_in_tree()].is_single) {
|
||||
state.is_single = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update field state of output sockets, also taking into account input sockets. */
|
||||
for (const bNodeSocket *output_socket : node->output_sockets()) {
|
||||
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
|
||||
const OutputFieldDependency &field_dependency =
|
||||
inferencing_interface.outputs[output_socket->index()];
|
||||
|
||||
switch (field_dependency.field_type()) {
|
||||
case OutputSocketFieldType::None: {
|
||||
state.is_single = true;
|
||||
break;
|
||||
}
|
||||
case OutputSocketFieldType::FieldSource: {
|
||||
state.is_single = false;
|
||||
state.is_field_source = true;
|
||||
break;
|
||||
}
|
||||
case OutputSocketFieldType::PartiallyDependent:
|
||||
case OutputSocketFieldType::DependentField: {
|
||||
for (const bNodeSocket *input_socket :
|
||||
gather_input_socket_dependencies(field_dependency, *node))
|
||||
{
|
||||
if (!input_socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (!field_state_by_socket_id[input_socket->index_in_tree()].is_single) {
|
||||
state.is_single = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find reverse dependencies and resolve conflicts, which may require another pass. */
|
||||
if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!need_update) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void determine_group_output_states(
|
||||
const bNodeTree &tree,
|
||||
FieldInferencingInterface &new_inferencing_interface,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const Span<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
const bNode *group_output_node = tree.group_output_node();
|
||||
if (!group_output_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const bNodeSocket *group_output_socket : group_output_node->input_sockets().drop_back(1)) {
|
||||
OutputFieldDependency field_dependency = find_group_output_dependencies(
|
||||
*group_output_socket, interface_by_node, field_state_by_socket_id);
|
||||
new_inferencing_interface.outputs[group_output_socket->index()] = std::move(field_dependency);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_socket_shapes(const bNodeTree &tree,
|
||||
const Span<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT;
|
||||
const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND;
|
||||
|
||||
auto get_shape_for_state = [&](const SocketFieldState &state) {
|
||||
if (state.is_always_single) {
|
||||
return requires_data_shape;
|
||||
}
|
||||
if (!state.is_single) {
|
||||
return is_field_shape;
|
||||
}
|
||||
if (state.requires_single) {
|
||||
return requires_data_shape;
|
||||
}
|
||||
return data_but_can_be_field_shape;
|
||||
};
|
||||
|
||||
for (const bNodeSocket *socket : tree.all_input_sockets()) {
|
||||
const SocketFieldState &state = field_state_by_socket_id[socket->index_in_tree()];
|
||||
const_cast<bNodeSocket *>(socket)->display_shape = get_shape_for_state(state);
|
||||
}
|
||||
for (const bNodeSocket *socket : tree.all_sockets()) {
|
||||
const SocketFieldState &state = field_state_by_socket_id[socket->index_in_tree()];
|
||||
const_cast<bNodeSocket *>(socket)->display_shape = get_shape_for_state(state);
|
||||
}
|
||||
}
|
||||
|
||||
static void prepare_inferencing_interfaces(
|
||||
const Span<const bNode *> nodes,
|
||||
MutableSpan<const FieldInferencingInterface *> interface_by_node,
|
||||
|
@ -803,12 +274,9 @@ static bool verify_field_inferencing_csp_result(
|
|||
|
||||
NodeTreeVariables variables(tree);
|
||||
|
||||
Array<SocketFieldState> field_state_by_socket_id;
|
||||
Array<SocketFieldStateLegacy> field_state_by_socket_id;
|
||||
std::unique_ptr<FieldInferencingInterface> tmp_inferencing_interface;
|
||||
if (use_propagation_result) {
|
||||
/* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */
|
||||
field_state_by_socket_id.reinitialize(tree.all_sockets().size());
|
||||
|
||||
/* Temp local inferencing interface to avoid overwriting the actual interface.
|
||||
* The propagation method directly writes to the interface. */
|
||||
tmp_inferencing_interface = std::make_unique<FieldInferencingInterface>();
|
||||
|
@ -817,12 +285,8 @@ static bool verify_field_inferencing_csp_result(
|
|||
tmp_inferencing_interface->outputs.resize(tree.interface_outputs().size(),
|
||||
OutputFieldDependency::ForDataSource());
|
||||
|
||||
propagate_data_requirements_from_right_to_left(
|
||||
tree, interface_by_node, field_state_by_socket_id);
|
||||
determine_group_input_states(tree, *tmp_inferencing_interface, field_state_by_socket_id);
|
||||
propagate_field_status_from_left_to_right(tree, interface_by_node, field_state_by_socket_id);
|
||||
determine_group_output_states(
|
||||
tree, *tmp_inferencing_interface, interface_by_node, field_state_by_socket_id);
|
||||
field_state_by_socket_id = solve_field_types_legacy(
|
||||
tree, interface_by_node, *tmp_inferencing_interface);
|
||||
}
|
||||
|
||||
std::cout << "Verify field type inferencing for tree " << tree.id.name << std::endl;
|
||||
|
@ -840,37 +304,33 @@ static bool verify_field_inferencing_csp_result(
|
|||
const int var = variables.get_socket_variable(*socket);
|
||||
const BitSpan state = csp_result[var];
|
||||
const int num_values = int(state[DomainValue::Single]) + int(state[DomainValue::Field]);
|
||||
if (num_values == 0)
|
||||
{
|
||||
if (num_values == 0) {
|
||||
log_error("No valid result");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_propagation_result) {
|
||||
const SocketFieldState &old_state = field_state_by_socket_id[socket->index_in_tree()];
|
||||
if (old_state.is_always_single) {
|
||||
if (!state[DomainValue::Single] || state[DomainValue::Field])
|
||||
{
|
||||
const SocketFieldStateLegacy &legacy_state =
|
||||
field_state_by_socket_id[socket->index_in_tree()];
|
||||
if (legacy_state.is_always_single) {
|
||||
if (!state[DomainValue::Single] || state[DomainValue::Field]) {
|
||||
log_error("Should only be single value");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!old_state.is_single) {
|
||||
if (state[DomainValue::Single] || !state[DomainValue::Field])
|
||||
{
|
||||
if (!legacy_state.is_single) {
|
||||
if (state[DomainValue::Single] || !state[DomainValue::Field]) {
|
||||
log_error("Should only be field");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (old_state.requires_single) {
|
||||
if (!state[DomainValue::Single] || state[DomainValue::Field])
|
||||
{
|
||||
if (legacy_state.requires_single) {
|
||||
if (!state[DomainValue::Single] || state[DomainValue::Field]) {
|
||||
log_error("Should only be single value");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!state[DomainValue::Single] || !state[DomainValue::Field])
|
||||
{
|
||||
if (!state[DomainValue::Single] || !state[DomainValue::Field]) {
|
||||
log_error("Should be single value or field");
|
||||
}
|
||||
}
|
||||
|
@ -914,6 +374,46 @@ static bool verify_field_inferencing_csp_result(
|
|||
return error;
|
||||
}
|
||||
|
||||
static InputSocketFieldType group_input_field_type(const bNodeTreeInterfaceSocket &group_input)
|
||||
{
|
||||
const bNodeSocketType *typeinfo = group_input.socket_typeinfo();
|
||||
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
|
||||
if (!nodes::socket_type_supports_fields(type)) {
|
||||
return InputSocketFieldType::None;
|
||||
}
|
||||
if (group_input.default_input != NODE_INPUT_DEFAULT_VALUE) {
|
||||
return InputSocketFieldType::Implicit;
|
||||
}
|
||||
if (is_layer_selection_field(group_input)) {
|
||||
return InputSocketFieldType::Implicit;
|
||||
}
|
||||
if (group_input.flag & NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY) {
|
||||
return InputSocketFieldType::None;
|
||||
}
|
||||
return InputSocketFieldType::IsSupported;
|
||||
}
|
||||
|
||||
static void add_group_constraints(const bNodeTree &tree, csp::ConstraintSet &constraints)
|
||||
{
|
||||
NodeTreeVariables variables(tree);
|
||||
|
||||
/* If an input does not support fields, this should be reflected in all Group Input nodes. */
|
||||
for (const int index : tree.interface_inputs().index_range()) {
|
||||
const bNodeTreeInterfaceSocket &group_input = *tree.interface_inputs()[index];
|
||||
const bool supports_field = group_input_field_type(group_input) != InputSocketFieldType::None;
|
||||
|
||||
const int var = variables.tree_input_vars[index];
|
||||
/* If the group input supports fields assume the input is a field, otherwise only support
|
||||
* single values. */
|
||||
if (supports_field) {
|
||||
constraints.add_unary(var, [](const int value) { return value == DomainValue::Field; });
|
||||
}
|
||||
else {
|
||||
constraints.add_unary(var, [](const int value) { return value == DomainValue::Single; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_node_constraints(const bNodeTree &tree,
|
||||
const bNode &node,
|
||||
const FieldInferencingInterface &inferencing_interface,
|
||||
|
@ -1202,6 +702,54 @@ static OutputFieldDependency find_group_output_dependencies(
|
|||
return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices));
|
||||
}
|
||||
|
||||
/* Setup inferencing interface for the tree after internal field types have been resolved. */
|
||||
static void determine_group_interface(
|
||||
const bNodeTree &tree,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const BitGroupVector<> &field_state_by_socket_id,
|
||||
FieldInferencingInterface &inferencing_interface)
|
||||
{
|
||||
NodeTreeVariables variables(tree);
|
||||
|
||||
const bNode *group_output_node = tree.group_output_node();
|
||||
if (!group_output_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const int index : tree.interface_inputs().index_range()) {
|
||||
const bNodeTreeInterfaceSocket &group_input = *tree.interface_inputs()[index];
|
||||
InputSocketFieldType &field_type = inferencing_interface.inputs[index];
|
||||
|
||||
field_type = group_input_field_type(group_input);
|
||||
if (field_type == InputSocketFieldType::IsSupported) {
|
||||
const int var = variables.tree_input_vars[index];
|
||||
const BitSpan state = field_state_by_socket_id[var];
|
||||
if (state[DomainValue::Single]) {
|
||||
/* Check if group inputs are required to be single values, because they are (indirectly)
|
||||
* connected to some socket that does not support fields. */
|
||||
if (!state[DomainValue::Field]) {
|
||||
field_type = InputSocketFieldType::None;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state[DomainValue::Field]) {
|
||||
field_type = InputSocketFieldType::Implicit;
|
||||
}
|
||||
else {
|
||||
/* Error: No supported field type. */
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const bNodeSocket *group_output_socket : group_output_node->input_sockets().drop_back(1)) {
|
||||
OutputFieldDependency field_dependency = find_group_output_dependencies(
|
||||
*group_output_socket, interface_by_node, field_state_by_socket_id);
|
||||
inferencing_interface.outputs[group_output_socket->index()] = std::move(field_dependency);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Logger>
|
||||
static void solve_field_inferencing_constraints(
|
||||
const bNodeTree &tree,
|
||||
|
@ -1209,11 +757,6 @@ static void solve_field_inferencing_constraints(
|
|||
FieldInferencingInterface &inferencing_interface,
|
||||
Logger &logger)
|
||||
{
|
||||
const bNode *group_output_node = tree.group_output_node();
|
||||
if (!group_output_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
tree.ensure_topology_cache();
|
||||
NodeTreeVariables variables(tree);
|
||||
|
||||
|
@ -1240,6 +783,7 @@ static void solve_field_inferencing_constraints(
|
|||
const Span<const bNode *> nodes = tree.toposort_right_to_left();
|
||||
|
||||
csp::ConstraintSet constraints;
|
||||
add_group_constraints(tree, constraints);
|
||||
for (const bNode *node : nodes) {
|
||||
add_node_constraints(tree, *node, *interface_by_node[node->index()], constraints);
|
||||
}
|
||||
|
@ -1248,34 +792,7 @@ static void solve_field_inferencing_constraints(
|
|||
BitGroupVector<> result = csp::solve_constraints_with_logger(
|
||||
constraints, variables.num_vars(), NumDomainValues, logger);
|
||||
|
||||
/* Setup inferencing interface for the tree. */
|
||||
for (const int i : tree.interface_inputs().index_range()) {
|
||||
const int var = variables.tree_input_vars[i];
|
||||
const BitSpan state = result[var];
|
||||
if (state[DomainValue::Single]) {
|
||||
if (state[DomainValue::Field]) {
|
||||
inferencing_interface.inputs[i] = InputSocketFieldType::IsSupported;
|
||||
}
|
||||
else {
|
||||
inferencing_interface.inputs[i] = InputSocketFieldType::None;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state[DomainValue::Field]) {
|
||||
inferencing_interface.inputs[i] = InputSocketFieldType::Implicit;
|
||||
}
|
||||
else {
|
||||
/* Error: No supported field type. */
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const bNodeSocket *group_output_socket : group_output_node->input_sockets().drop_back(1)) {
|
||||
OutputFieldDependency field_dependency = find_group_output_dependencies(
|
||||
*group_output_socket, interface_by_node, result);
|
||||
inferencing_interface.outputs[group_output_socket->index()] = std::move(field_dependency);
|
||||
}
|
||||
determine_group_interface(tree, interface_by_node, result, inferencing_interface);
|
||||
|
||||
/* Verify the result. */
|
||||
verify_field_inferencing_csp_result(tree, interface_by_node, result, inferencing_interface);
|
||||
|
@ -1283,7 +800,7 @@ static void solve_field_inferencing_constraints(
|
|||
update_socket_shapes(tree, variables, result);
|
||||
}
|
||||
|
||||
template <typename Logger>
|
||||
template<typename Logger>
|
||||
static bool update_field_inferencing_ex(const bNodeTree &tree,
|
||||
const bool use_constraint_solver,
|
||||
Logger &logger)
|
||||
|
@ -1310,16 +827,7 @@ static bool update_field_inferencing_ex(const bNodeTree &tree,
|
|||
tree, interface_by_node, *new_inferencing_interface, logger);
|
||||
}
|
||||
else {
|
||||
/* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */
|
||||
Array<SocketFieldState> field_state_by_socket_id(tree.all_sockets().size());
|
||||
|
||||
propagate_data_requirements_from_right_to_left(
|
||||
tree, interface_by_node, field_state_by_socket_id);
|
||||
determine_group_input_states(tree, *new_inferencing_interface, field_state_by_socket_id);
|
||||
propagate_field_status_from_left_to_right(tree, interface_by_node, field_state_by_socket_id);
|
||||
determine_group_output_states(
|
||||
tree, *new_inferencing_interface, interface_by_node, field_state_by_socket_id);
|
||||
update_socket_shapes(tree, field_state_by_socket_id);
|
||||
update_field_inferencing_legacy(tree, interface_by_node, *new_inferencing_interface);
|
||||
}
|
||||
|
||||
/* Update the previous group interface. */
|
||||
|
@ -1331,7 +839,8 @@ static bool update_field_inferencing_ex(const bNodeTree &tree,
|
|||
return group_interface_changed;
|
||||
}
|
||||
|
||||
bool update_field_inferencing(const bNodeTree &tree) {
|
||||
bool update_field_inferencing(const bNodeTree &tree)
|
||||
{
|
||||
csp::NullLogger logger;
|
||||
return update_field_inferencing_ex(
|
||||
tree, U.experimental.use_node_field_inferencing_constraint_solver, logger);
|
||||
|
|
|
@ -0,0 +1,593 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_stack.hh"
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "NOD_geometry.hh"
|
||||
#include "NOD_node_declaration.hh"
|
||||
#include "NOD_socket.hh"
|
||||
|
||||
namespace blender::bke::node_field_inferencing {
|
||||
|
||||
using nodes::FieldInferencingInterface;
|
||||
using nodes::InputSocketFieldType;
|
||||
using nodes::NodeDeclaration;
|
||||
using nodes::OutputFieldDependency;
|
||||
using nodes::OutputSocketFieldType;
|
||||
using nodes::SocketDeclaration;
|
||||
|
||||
using SocketFieldState = SocketFieldStateLegacy;
|
||||
|
||||
static bool is_field_socket_type(const bNodeSocket &socket)
|
||||
{
|
||||
return nodes::socket_type_supports_fields((eNodeSocketDatatype)socket.typeinfo->type);
|
||||
}
|
||||
|
||||
static Vector<const bNodeSocket *> gather_input_socket_dependencies(
|
||||
const OutputFieldDependency &field_dependency, const bNode &node)
|
||||
{
|
||||
const OutputSocketFieldType type = field_dependency.field_type();
|
||||
Vector<const bNodeSocket *> input_sockets;
|
||||
switch (type) {
|
||||
case OutputSocketFieldType::FieldSource:
|
||||
case OutputSocketFieldType::None: {
|
||||
break;
|
||||
}
|
||||
case OutputSocketFieldType::DependentField: {
|
||||
/* This output depends on all inputs. */
|
||||
input_sockets.extend(node.input_sockets());
|
||||
break;
|
||||
}
|
||||
case OutputSocketFieldType::PartiallyDependent: {
|
||||
/* This output depends only on a few inputs. */
|
||||
for (const int i : field_dependency.linked_input_indices()) {
|
||||
input_sockets.append(&node.input_socket(i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return input_sockets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check what the group output socket depends on. Potentially traverses the node tree
|
||||
* to figure out if it is always a field or if it depends on any group inputs.
|
||||
*/
|
||||
static OutputFieldDependency find_group_output_dependencies(
|
||||
const bNodeSocket &group_output_socket,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const Span<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
if (!is_field_socket_type(group_output_socket)) {
|
||||
return OutputFieldDependency::ForDataSource();
|
||||
}
|
||||
|
||||
/* Use a Set here instead of an array indexed by socket id, because we my only need to look at
|
||||
* very few sockets. */
|
||||
Set<const bNodeSocket *> handled_sockets;
|
||||
Stack<const bNodeSocket *> sockets_to_check;
|
||||
|
||||
handled_sockets.add(&group_output_socket);
|
||||
sockets_to_check.push(&group_output_socket);
|
||||
|
||||
/* Keeps track of group input indices that are (indirectly) connected to the output. */
|
||||
Vector<int> linked_input_indices;
|
||||
|
||||
while (!sockets_to_check.is_empty()) {
|
||||
const bNodeSocket *input_socket = sockets_to_check.pop();
|
||||
|
||||
if (!input_socket->is_directly_linked() &&
|
||||
!field_state_by_socket_id[input_socket->index_in_tree()].is_single)
|
||||
{
|
||||
/* This socket uses a field as input by default. */
|
||||
return OutputFieldDependency::ForFieldSource();
|
||||
}
|
||||
|
||||
for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
|
||||
const bNode &origin_node = origin_socket->owner_node();
|
||||
const SocketFieldState &origin_state =
|
||||
field_state_by_socket_id[origin_socket->index_in_tree()];
|
||||
|
||||
if (origin_state.is_field_source) {
|
||||
if (origin_node.type == NODE_GROUP_INPUT) {
|
||||
/* Found a group input that the group output depends on. */
|
||||
linked_input_indices.append_non_duplicates(origin_socket->index());
|
||||
}
|
||||
else {
|
||||
/* Found a field source that is not the group input. So the output is always a field. */
|
||||
return OutputFieldDependency::ForFieldSource();
|
||||
}
|
||||
}
|
||||
else if (!origin_state.is_single) {
|
||||
const FieldInferencingInterface &inferencing_interface =
|
||||
*interface_by_node[origin_node.index()];
|
||||
const OutputFieldDependency &field_dependency =
|
||||
inferencing_interface.outputs[origin_socket->index()];
|
||||
|
||||
/* Propagate search further to the left. */
|
||||
for (const bNodeSocket *origin_input_socket :
|
||||
gather_input_socket_dependencies(field_dependency, origin_node))
|
||||
{
|
||||
if (!origin_input_socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (!field_state_by_socket_id[origin_input_socket->index_in_tree()].is_single) {
|
||||
if (handled_sockets.add(origin_input_socket)) {
|
||||
sockets_to_check.push(origin_input_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices));
|
||||
}
|
||||
|
||||
/** Result of syncing two field states. */
|
||||
enum class eFieldStateSyncResult : char {
|
||||
/* Nothing changed. */
|
||||
NONE = 0,
|
||||
/* State A has been modified. */
|
||||
CHANGED_A = (1 << 0),
|
||||
/* State B has been modified. */
|
||||
CHANGED_B = (1 << 1),
|
||||
};
|
||||
ENUM_OPERATORS(eFieldStateSyncResult, eFieldStateSyncResult::CHANGED_B)
|
||||
|
||||
/**
|
||||
* Compare both field states and select the most compatible.
|
||||
* Afterwards both field states will be the same.
|
||||
* \return eFieldStateSyncResult flags indicating which field states have changed.
|
||||
*/
|
||||
static eFieldStateSyncResult sync_field_states(SocketFieldState &a, SocketFieldState &b)
|
||||
{
|
||||
const bool requires_single = a.requires_single || b.requires_single;
|
||||
const bool is_single = a.is_single && b.is_single;
|
||||
|
||||
eFieldStateSyncResult res = eFieldStateSyncResult::NONE;
|
||||
if (a.requires_single != requires_single || a.is_single != is_single) {
|
||||
res |= eFieldStateSyncResult::CHANGED_A;
|
||||
}
|
||||
if (b.requires_single != requires_single || b.is_single != is_single) {
|
||||
res |= eFieldStateSyncResult::CHANGED_B;
|
||||
}
|
||||
|
||||
a.requires_single = requires_single;
|
||||
b.requires_single = requires_single;
|
||||
a.is_single = is_single;
|
||||
b.is_single = is_single;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare field states of simulation nodes sockets and select the most compatible.
|
||||
* Afterwards all field states will be the same.
|
||||
* \return eFieldStateSyncResult flags indicating which field states have changed.
|
||||
*/
|
||||
static eFieldStateSyncResult simulation_nodes_field_state_sync(
|
||||
const bNode &input_node,
|
||||
const bNode &output_node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
eFieldStateSyncResult res = eFieldStateSyncResult::NONE;
|
||||
for (const int i : output_node.output_sockets().index_range()) {
|
||||
/* First input node output is Delta Time which does not appear in the output node outputs. */
|
||||
const bNodeSocket &input_socket = input_node.output_socket(i + 1);
|
||||
const bNodeSocket &output_socket = output_node.output_socket(i);
|
||||
SocketFieldState &input_state = field_state_by_socket_id[input_socket.index_in_tree()];
|
||||
SocketFieldState &output_state = field_state_by_socket_id[output_socket.index_in_tree()];
|
||||
res |= sync_field_states(input_state, output_state);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static eFieldStateSyncResult repeat_field_state_sync(
|
||||
const bNode &input_node,
|
||||
const bNode &output_node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
eFieldStateSyncResult res = eFieldStateSyncResult::NONE;
|
||||
for (const int i : output_node.output_sockets().index_range()) {
|
||||
const bNodeSocket &input_socket = input_node.output_socket(i);
|
||||
const bNodeSocket &output_socket = output_node.output_socket(i);
|
||||
SocketFieldState &input_state = field_state_by_socket_id[input_socket.index_in_tree()];
|
||||
SocketFieldState &output_state = field_state_by_socket_id[output_socket.index_in_tree()];
|
||||
res |= sync_field_states(input_state, output_state);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool propagate_special_data_requirements(
|
||||
const bNodeTree &tree,
|
||||
const bNode &node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
tree.ensure_topology_cache();
|
||||
|
||||
bool need_update = false;
|
||||
|
||||
/* Sync field state between zone nodes and schedule another pass if necessary. */
|
||||
switch (node.type) {
|
||||
case GEO_NODE_SIMULATION_INPUT: {
|
||||
const NodeGeometrySimulationInput &data = *static_cast<const NodeGeometrySimulationInput *>(
|
||||
node.storage);
|
||||
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
|
||||
const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync(
|
||||
node, *output_node, field_state_by_socket_id);
|
||||
if (bool(sync_result & eFieldStateSyncResult::CHANGED_B)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SIMULATION_OUTPUT: {
|
||||
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) {
|
||||
const NodeGeometrySimulationInput &data =
|
||||
*static_cast<const NodeGeometrySimulationInput *>(input_node->storage);
|
||||
if (node.identifier == data.output_node_id) {
|
||||
const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync(
|
||||
*input_node, node, field_state_by_socket_id);
|
||||
if (bool(sync_result & eFieldStateSyncResult::CHANGED_A)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_REPEAT_INPUT: {
|
||||
const NodeGeometryRepeatInput &data = *static_cast<const NodeGeometryRepeatInput *>(
|
||||
node.storage);
|
||||
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
|
||||
const eFieldStateSyncResult sync_result = repeat_field_state_sync(
|
||||
node, *output_node, field_state_by_socket_id);
|
||||
if (bool(sync_result & eFieldStateSyncResult::CHANGED_B)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_REPEAT_OUTPUT: {
|
||||
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeRepeatInput")) {
|
||||
const NodeGeometryRepeatInput &data = *static_cast<const NodeGeometryRepeatInput *>(
|
||||
input_node->storage);
|
||||
if (node.identifier == data.output_node_id) {
|
||||
const eFieldStateSyncResult sync_result = repeat_field_state_sync(
|
||||
*input_node, node, field_state_by_socket_id);
|
||||
if (bool(sync_result & eFieldStateSyncResult::CHANGED_A)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return need_update;
|
||||
}
|
||||
|
||||
static void propagate_data_requirements_from_right_to_left(
|
||||
const bNodeTree &tree,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
const Span<const bNode *> toposort_result = tree.toposort_right_to_left();
|
||||
|
||||
while (true) {
|
||||
/* Node updates may require several passes due to cyclic dependencies caused by simulation or
|
||||
* repeat input/output nodes. */
|
||||
bool need_update = false;
|
||||
|
||||
for (const bNode *node : toposort_result) {
|
||||
const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()];
|
||||
|
||||
for (const bNodeSocket *output_socket : node->output_sockets()) {
|
||||
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
|
||||
|
||||
const OutputFieldDependency &field_dependency =
|
||||
inferencing_interface.outputs[output_socket->index()];
|
||||
|
||||
if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) {
|
||||
continue;
|
||||
}
|
||||
if (field_dependency.field_type() == OutputSocketFieldType::None) {
|
||||
state.requires_single = true;
|
||||
state.is_always_single = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The output is required to be a single value when it is connected to any input that does
|
||||
* not support fields. */
|
||||
for (const bNodeSocket *target_socket : output_socket->directly_linked_sockets()) {
|
||||
if (target_socket->is_available()) {
|
||||
state.requires_single |=
|
||||
field_state_by_socket_id[target_socket->index_in_tree()].requires_single;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.requires_single) {
|
||||
bool any_input_is_field_implicitly = false;
|
||||
const Vector<const bNodeSocket *> connected_inputs = gather_input_socket_dependencies(
|
||||
field_dependency, *node);
|
||||
for (const bNodeSocket *input_socket : connected_inputs) {
|
||||
if (!input_socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (inferencing_interface.inputs[input_socket->index()] ==
|
||||
InputSocketFieldType::Implicit)
|
||||
{
|
||||
if (!input_socket->is_logically_linked()) {
|
||||
any_input_is_field_implicitly = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (any_input_is_field_implicitly) {
|
||||
/* This output isn't a single value actually. */
|
||||
state.requires_single = false;
|
||||
}
|
||||
else {
|
||||
/* If the output is required to be a single value, the connected inputs in the same
|
||||
* node must not be fields as well. */
|
||||
for (const bNodeSocket *input_socket : connected_inputs) {
|
||||
field_state_by_socket_id[input_socket->index_in_tree()].requires_single = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Some inputs do not require fields independent of what the outputs are connected to. */
|
||||
for (const bNodeSocket *input_socket : node->input_sockets()) {
|
||||
SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
|
||||
if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) {
|
||||
state.requires_single = true;
|
||||
state.is_always_single = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find reverse dependencies and resolve conflicts, which may require another pass. */
|
||||
if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!need_update) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void determine_group_input_states(
|
||||
const bNodeTree &tree,
|
||||
FieldInferencingInterface &new_inferencing_interface,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
{
|
||||
/* Non-field inputs never support fields. */
|
||||
for (const int index : tree.interface_inputs().index_range()) {
|
||||
const bNodeTreeInterfaceSocket *group_input = tree.interface_inputs()[index];
|
||||
const bNodeSocketType *typeinfo = group_input->socket_typeinfo();
|
||||
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
|
||||
SOCK_CUSTOM;
|
||||
if (!nodes::socket_type_supports_fields(type)) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::None;
|
||||
}
|
||||
else if (group_input->default_input != NODE_INPUT_DEFAULT_VALUE) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::Implicit;
|
||||
}
|
||||
else if (is_layer_selection_field(*group_input)) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::Implicit;
|
||||
}
|
||||
else if (group_input->flag & NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Check if group inputs are required to be single values, because they are (indirectly)
|
||||
* connected to some socket that does not support fields. */
|
||||
for (const bNode *node : tree.group_input_nodes()) {
|
||||
for (const bNodeSocket *output_socket : node->output_sockets().drop_back(1)) {
|
||||
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
|
||||
const int output_index = output_socket->index();
|
||||
if (state.requires_single) {
|
||||
if (new_inferencing_interface.inputs[output_index] == InputSocketFieldType::Implicit) {
|
||||
/* Don't override hard-coded implicit fields. */
|
||||
continue;
|
||||
}
|
||||
new_inferencing_interface.inputs[output_index] = InputSocketFieldType::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If an input does not support fields, this should be reflected in all Group Input nodes. */
|
||||
for (const bNode *node : tree.group_input_nodes()) {
|
||||
for (const bNodeSocket *output_socket : node->output_sockets().drop_back(1)) {
|
||||
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
|
||||
const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] !=
|
||||
InputSocketFieldType::None;
|
||||
if (supports_field) {
|
||||
state.is_single = false;
|
||||
state.is_field_source = true;
|
||||
}
|
||||
else {
|
||||
state.requires_single = true;
|
||||
}
|
||||
}
|
||||
SocketFieldState &dummy_socket_state =
|
||||
field_state_by_socket_id[node->output_sockets().last()->index_in_tree()];
|
||||
dummy_socket_state.requires_single = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void propagate_field_status_from_left_to_right(
|
||||
const bNodeTree &tree,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const MutableSpan<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
const Span<const bNode *> toposort_result = tree.toposort_left_to_right();
|
||||
|
||||
while (true) {
|
||||
/* Node updates may require several passes due to cyclic dependencies. */
|
||||
bool need_update = false;
|
||||
|
||||
for (const bNode *node : toposort_result) {
|
||||
if (node->type == NODE_GROUP_INPUT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()];
|
||||
|
||||
/* Update field state of input sockets, also taking into account linked origin sockets. */
|
||||
for (const bNodeSocket *input_socket : node->input_sockets()) {
|
||||
SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
|
||||
if (state.is_always_single) {
|
||||
state.is_single = true;
|
||||
continue;
|
||||
}
|
||||
state.is_single = true;
|
||||
if (!input_socket->is_directly_linked()) {
|
||||
if (inferencing_interface.inputs[input_socket->index()] ==
|
||||
InputSocketFieldType::Implicit)
|
||||
{
|
||||
state.is_single = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
|
||||
if (!field_state_by_socket_id[origin_socket->index_in_tree()].is_single) {
|
||||
state.is_single = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update field state of output sockets, also taking into account input sockets. */
|
||||
for (const bNodeSocket *output_socket : node->output_sockets()) {
|
||||
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
|
||||
const OutputFieldDependency &field_dependency =
|
||||
inferencing_interface.outputs[output_socket->index()];
|
||||
|
||||
switch (field_dependency.field_type()) {
|
||||
case OutputSocketFieldType::None: {
|
||||
state.is_single = true;
|
||||
break;
|
||||
}
|
||||
case OutputSocketFieldType::FieldSource: {
|
||||
state.is_single = false;
|
||||
state.is_field_source = true;
|
||||
break;
|
||||
}
|
||||
case OutputSocketFieldType::PartiallyDependent:
|
||||
case OutputSocketFieldType::DependentField: {
|
||||
for (const bNodeSocket *input_socket :
|
||||
gather_input_socket_dependencies(field_dependency, *node))
|
||||
{
|
||||
if (!input_socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (!field_state_by_socket_id[input_socket->index_in_tree()].is_single) {
|
||||
state.is_single = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find reverse dependencies and resolve conflicts, which may require another pass. */
|
||||
if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!need_update) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void determine_group_output_states(
|
||||
const bNodeTree &tree,
|
||||
FieldInferencingInterface &new_inferencing_interface,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
const Span<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
const bNode *group_output_node = tree.group_output_node();
|
||||
if (!group_output_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const bNodeSocket *group_output_socket : group_output_node->input_sockets().drop_back(1)) {
|
||||
OutputFieldDependency field_dependency = find_group_output_dependencies(
|
||||
*group_output_socket, interface_by_node, field_state_by_socket_id);
|
||||
new_inferencing_interface.outputs[group_output_socket->index()] = std::move(field_dependency);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_socket_shapes(const bNodeTree &tree,
|
||||
const Span<SocketFieldState> field_state_by_socket_id)
|
||||
{
|
||||
const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT;
|
||||
const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND;
|
||||
|
||||
auto get_shape_for_state = [&](const SocketFieldState &state) {
|
||||
if (state.is_always_single) {
|
||||
return requires_data_shape;
|
||||
}
|
||||
if (!state.is_single) {
|
||||
return is_field_shape;
|
||||
}
|
||||
if (state.requires_single) {
|
||||
return requires_data_shape;
|
||||
}
|
||||
return data_but_can_be_field_shape;
|
||||
};
|
||||
|
||||
for (const bNodeSocket *socket : tree.all_input_sockets()) {
|
||||
const SocketFieldState &state = field_state_by_socket_id[socket->index_in_tree()];
|
||||
const_cast<bNodeSocket *>(socket)->display_shape = get_shape_for_state(state);
|
||||
}
|
||||
for (const bNodeSocket *socket : tree.all_sockets()) {
|
||||
const SocketFieldState &state = field_state_by_socket_id[socket->index_in_tree()];
|
||||
const_cast<bNodeSocket *>(socket)->display_shape = get_shape_for_state(state);
|
||||
}
|
||||
}
|
||||
|
||||
Array<SocketFieldState> solve_field_types_legacy(
|
||||
const bNodeTree &tree,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
FieldInferencingInterface &inferencing_interface)
|
||||
{
|
||||
/* Keep track of the state of all sockets. The index into this array is #SocketRef::id().
|
||||
*/
|
||||
Array<SocketFieldState> field_state_by_socket_id(tree.all_sockets().size());
|
||||
|
||||
propagate_data_requirements_from_right_to_left(
|
||||
tree, interface_by_node, field_state_by_socket_id);
|
||||
determine_group_input_states(tree, inferencing_interface, field_state_by_socket_id);
|
||||
propagate_field_status_from_left_to_right(tree, interface_by_node, field_state_by_socket_id);
|
||||
determine_group_output_states(
|
||||
tree, inferencing_interface, interface_by_node, field_state_by_socket_id);
|
||||
|
||||
return field_state_by_socket_id;
|
||||
}
|
||||
|
||||
void update_field_inferencing_legacy(
|
||||
const bNodeTree &tree,
|
||||
const Span<const FieldInferencingInterface *> interface_by_node,
|
||||
FieldInferencingInterface &inferencing_interface)
|
||||
{
|
||||
/* Keep track of the state of all sockets. The index into this array is #SocketRef::id().
|
||||
*/
|
||||
Array<SocketFieldState> field_state_by_socket_id = solve_field_types_legacy(
|
||||
tree, interface_by_node, inferencing_interface);
|
||||
update_socket_shapes(tree, field_state_by_socket_id);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::node_field_inferencing
|
|
@ -235,7 +235,7 @@ TEST(constraint_satisfaction, Sudoku)
|
|||
constraints.add_binary(var, other_var_in_col, inequality_fn);
|
||||
}
|
||||
}
|
||||
for (const int other_row : IndexRange( int(row/3) * 3, 3)) {
|
||||
for (const int other_row : IndexRange(int(row / 3) * 3, 3)) {
|
||||
for (const int other_col : IndexRange(int(col / 3) * 3, 3)) {
|
||||
const int other_var_in_block = other_row * 9 + other_col;
|
||||
if (other_var_in_block != var) {
|
||||
|
|
|
@ -737,6 +737,7 @@ typedef struct UserDef_Experimental {
|
|||
char use_grease_pencil_version3_convert_on_load;
|
||||
char use_animation_baklava;
|
||||
char use_node_field_inferencing_constraint_solver;
|
||||
|
||||
/** `makesdna` does not allow empty structs. */
|
||||
} UserDef_Experimental;
|
||||
|
||||
|
|
Loading…
Reference in New Issue