WIP: Field type inferencing using a constraint solver method #120420

Draft
Lukas Tönne wants to merge 52 commits from LukasTonne/blender:socket-type-inference into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
6 changed files with 729 additions and 599 deletions
Showing only changes of commit 10a6d84ace - Show all commits

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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