308 : bmain_(bmain), params_(
params), relations_(bmain)
317 changed_ntrees.
append(ntree);
321 this->update_rooted(changed_ntrees);
330 bool is_single_tree_update =
false;
332 if (root_ntrees.
size() == 1) {
338 update_result_by_tree_.
add_new(ntree, result);
339 if (!result.interface_changed && !result.output_changed) {
340 is_single_tree_update =
true;
344 if (!is_single_tree_update) {
346 for (
bNodeTree *ntree : ntrees_in_order) {
350 if (!update_result_by_tree_.
contains(ntree)) {
352 update_result_by_tree_.
add_new(ntree, result);
356 if (result.output_changed) {
361 if (result.interface_changed) {
369 for (
const auto item : update_result_by_tree_.
items()) {
373 this->reset_changed_flags(*ntree);
375 if (result.interface_changed) {
379 Object *
object = pair.first;
389 if (result.output_changed) {
390 ntree->
runtime->geometry_nodes_lazy_function_graph_info.reset();
407 enum class ToposortMark {
427 ToposortMarkMap marks;
428 for (
bNodeTree *ntree : trees_to_update) {
429 marks.add_new(ntree, ToposortMark::None);
431 for (
bNodeTree *ntree : trees_to_update) {
432 if (marks.lookup(ntree) == ToposortMark::None) {
433 const bool cycle_detected = !this->get_tree_update_order__visit_recursive(
434 ntree, marks, sorted_ntrees);
441 std::reverse(sorted_ntrees.
begin(), sorted_ntrees.
end());
443 return sorted_ntrees;
446 bool get_tree_update_order__visit_recursive(
bNodeTree *ntree,
447 ToposortMarkMap &marks,
448 Vector<bNodeTree *> &sorted_ntrees)
450 ToposortMark &mark = marks.lookup(ntree);
451 if (mark == ToposortMark::Permanent) {
454 if (mark == ToposortMark::Temporary) {
459 mark = ToposortMark::Temporary;
461 for (
const TreeNodePair &pair : relations_.get_group_node_users(ntree)) {
462 this->get_tree_update_order__visit_recursive(pair.first, marks, sorted_ntrees);
464 sorted_ntrees.append(ntree);
466 mark = ToposortMark::Permanent;
474 Set<bNodeTree *> reachable_trees;
477 while (!trees_to_check.
is_empty()) {
479 if (reachable_trees.add(ntree)) {
480 for (
const TreeNodePair &pair : relations_.get_group_node_users(ntree)) {
481 trees_to_check.
add(pair.first);
486 return reachable_trees;
489 TreeUpdateResult update_tree(
bNodeTree &ntree)
493 ntree.
runtime->link_errors_by_target_node.clear();
495 this->update_socket_link_and_use(ntree);
496 this->update_individual_nodes(ntree);
497 this->update_internal_links(ntree);
498 this->update_generic_callback(ntree);
499 this->remove_unused_previews_when_necessary(ntree);
500 this->make_node_previews_dirty(ntree);
502 this->propagate_runtime_flags(ntree);
504 if (this->propagate_enum_definitions(ntree)) {
505 result.interface_changed =
true;
507 if (node_field_inferencing::update_field_inferencing(ntree)) {
508 result.interface_changed =
true;
510 this->update_from_field_inference(ntree);
511 if (anonymous_attribute_inferencing::update_anonymous_attribute_relations(ntree)) {
512 result.interface_changed =
true;
514 if (nodes::gizmos::update_tree_gizmo_propagation(ntree)) {
515 result.interface_changed =
true;
517 this->update_socket_shapes(ntree);
520 result.output_changed = this->check_if_output_changed(ntree);
522 this->update_socket_link_and_use(ntree);
523 this->update_link_validation(ntree);
525 if (this->update_nested_node_refs(ntree)) {
526 result.interface_changed =
true;
534 result.interface_changed =
true;
539 Set<int32_t> node_identifiers;
541 for (
const int i : nodes.index_range()) {
542 const bNode &node = *nodes[i];
544 node_identifiers.add_new(node.identifier);
554 tree.ensure_topology_cache();
556 if (socket->directly_linked_links().is_empty()) {
557 socket->link =
nullptr;
560 socket->link = socket->directly_linked_links()[0];
564 this->update_socket_used_tags(
tree);
569 tree.ensure_topology_cache();
571 const bool socket_is_linked = !socket->directly_linked_links().is_empty();
576 void update_individual_nodes(
bNodeTree &ntree)
578 for (
bNode *node : ntree.all_nodes()) {
579 bke::node_declaration_ensure(&ntree, node);
580 if (this->should_update_individual_node(ntree, *node)) {
589 nodes::update_node_declaration_and_sockets(ntree, *node);
599 bool should_update_individual_node(
const bNodeTree &ntree,
const bNode &node)
619 if (
const bNode *output_node = zone_type.get_corresponding_output(ntree, node)) {
628 struct InternalLink {
631 int multi_input_sort_id = 0;
640 if (!link->fromnode->is_dangling_reroute()) {
647 void update_internal_links(
bNodeTree &ntree)
650 ntree.ensure_topology_cache();
651 for (
bNode *node : ntree.all_nodes()) {
652 if (!this->should_update_individual_node(ntree, *node)) {
656 Vector<InternalLink> expected_internal_links;
657 for (
const bNodeSocket *output_socket : node->output_sockets()) {
658 if (!output_socket->is_available()) {
661 if (!output_socket->is_directly_linked()) {
667 const bNodeSocket *input_socket = this->find_internally_linked_input(output_socket);
668 if (input_socket ==
nullptr) {
673 const bNodeLink *connected_link = first_non_dangling_link(ntree, connected_links);
676 std::max<int>(0, connected_links.
size() - 1);
677 expected_internal_links.append(InternalLink{
const_cast<bNodeSocket *
>(input_socket),
683 if (node->runtime->internal_links.size() != expected_internal_links.size()) {
684 this->update_internal_links_in_node(ntree, *node, expected_internal_links);
688 const bool all_expected_internal_links_exist = std::all_of(
689 node->runtime->internal_links.begin(),
690 node->runtime->internal_links.end(),
692 const InternalLink internal_link{link.fromsock, link.tosock, link.multi_input_sort_id};
693 return expected_internal_links.as_span().contains(internal_link);
696 if (all_expected_internal_links_exist) {
700 this->update_internal_links_in_node(ntree, *node, expected_internal_links);
707 int selected_priority = -1;
708 bool selected_is_linked =
false;
709 const bNode &node = output_socket->owner_node();
710 if (node.type == GEO_NODE_BAKE) {
712 return &node.input_by_identifier(output_socket->
identifier);
714 for (
const bNodeSocket *input_socket : node.input_sockets()) {
715 if (!input_socket->is_available()) {
726 const bool is_linked = input_socket->is_directly_linked();
727 const bool is_preferred = priority > selected_priority || (is_linked && !selected_is_linked);
731 selected_socket = input_socket;
732 selected_priority = priority;
733 selected_is_linked = is_linked;
735 return selected_socket;
738 void update_internal_links_in_node(
bNodeTree &ntree,
742 node.
runtime->internal_links.clear();
743 node.runtime->internal_links.reserve(internal_links.
size());
744 for (
const InternalLink &internal_link : internal_links) {
747 link.fromsock = internal_link.from;
749 link.tosock = internal_link.to;
750 link.multi_input_sort_id = internal_link.multi_input_sort_id;
752 node.runtime->internal_links.append(link);
757 void update_generic_callback(
bNodeTree &ntree)
759 if (ntree.
typeinfo->update ==
nullptr) {
765 void remove_unused_previews_when_necessary(
bNodeTree &ntree)
770 if ((ntree.
runtime->changed_flag & allowed_flags) == ntree.
runtime->changed_flag) {
776 void make_node_previews_dirty(
bNodeTree &ntree)
778 ntree.
runtime->previews_refresh_state++;
779 for (
bNode *node : ntree.all_nodes()) {
784 this->make_node_previews_dirty(*nested_tree);
789 void propagate_runtime_flags(
const bNodeTree &ntree)
791 ntree.ensure_topology_cache();
793 ntree.
runtime->runtime_flag = 0;
795 for (
const bNode *group_node : ntree.group_nodes()) {
797 if (group !=
nullptr) {
798 ntree.
runtime->runtime_flag |= group->runtime->runtime_flag;
804 for (
const StringRefNull idname : {
"ShaderNodeTexImage",
"ShaderNodeTexEnvironment"}) {
805 for (
const bNode *node : ntree.nodes_by_type(idname)) {
806 Image *image =
reinterpret_cast<Image *
>(node->id);
814 for (
const StringRefNull idname : {
"ShaderNodeOutputMaterial",
815 "ShaderNodeOutputLight",
816 "ShaderNodeOutputWorld",
817 "ShaderNodeOutputAOV"})
820 if (!nodes.is_empty()) {
828 if (!ntree.nodes_by_type(
"GeometryNodeSimulationOutput").is_empty()) {
834 void update_from_field_inference(
bNodeTree &ntree)
839 for (
bNode *node : ntree.nodes_by_type(
"GeometryNodeBake")) {
841 for (
const int i :
IndexRange(storage.items_num)) {
844 if (socket.
runtime->field_state == FieldSocketState::IsField) {
851 void update_socket_shapes(
bNodeTree &ntree)
853 ntree.ensure_topology_cache();
855 socket->display_shape = this->get_socket_shape(*socket);
861 if (socket.
runtime->field_state) {
862 switch (*socket.
runtime->field_state) {
863 case bke::FieldSocketState::RequiresSingle:
865 case bke::FieldSocketState::CanBeField:
867 case bke::FieldSocketState::IsField:
874 bool propagate_enum_definitions(
bNodeTree &ntree)
876 ntree.ensure_interface_cache();
880 for (
bNode *node : ntree.toposort_right_to_left()) {
881 const bool node_updated = this->should_update_individual_node(ntree, *node);
883 if (node->typeinfo->type == GEO_NODE_MENU_SWITCH) {
887 const RuntimeNodeEnumItems *enum_items = this->create_runtime_enum_items(
894 enum_items->remove_user_and_delete_if_last();
900 for (
bNodeSocket *socket : node->input_sockets()) {
902 clear_enum_reference(*socket);
905 for (
bNodeSocket *socket : node->output_sockets()) {
907 clear_enum_reference(*socket);
913 for (
bNodeSocket *output : node->output_sockets()) {
914 if (!output->is_available() || output->type !=
SOCK_MENU) {
917 for (
const bNodeSocket *input : output->directly_linked_sockets()) {
918 if (!input->is_available() || input->type !=
SOCK_MENU) {
926 if (node->is_group()) {
928 if (node->id ==
nullptr) {
932 group_tree->ensure_interface_cache();
934 for (
const int socket_i : group_tree->interface_inputs().index_range()) {
935 bNodeSocket &input = *node->input_sockets()[socket_i];
938 if (input.is_available() && input.type ==
SOCK_MENU) {
940 this->update_socket_enum_definition(
946 else if (node->type == GEO_NODE_MENU_SWITCH) {
948 const bNodeSocket *output = node->output_sockets().first();
949 for (
bNodeSocket *input : node->input_sockets().drop_front(1)) {
950 if (input->is_available() && input->type ==
SOCK_MENU) {
951 this->update_socket_enum_definition(
957 else if (node->type == GEO_NODE_FOREACH_GEOMETRY_ELEMENT_INPUT) {
959 BLI_assert(node->input_sockets().size() == node->output_sockets().size());
962 for (
const int socket_i : sockets) {
963 bNodeSocket *input = node->input_sockets()[socket_i];
964 bNodeSocket *output = node->output_sockets()[socket_i];
965 if (input->is_available() && input->type ==
SOCK_MENU && output->is_available() &&
968 this->update_socket_enum_definition(
980 if (input->is_available() && input->type ==
SOCK_MENU) {
981 for (
const bNodeSocket *output : node->output_sockets()) {
982 if (output->is_available() && output->type ==
SOCK_MENU) {
983 this->update_socket_enum_definition(
994 const Span<bNode *> group_input_nodes = ntree.group_input_nodes();
995 for (
const int interface_input_i : ntree.interface_inputs().index_range()) {
997 *ntree.interface_inputs()[interface_input_i];
1001 const RuntimeNodeEnumItems *found_enum_items =
nullptr;
1002 bool found_conflict =
false;
1003 for (
bNode *input_node : group_input_nodes) {
1004 const bNodeSocket &socket = input_node->output_socket(interface_input_i);
1006 if (socket_value.has_conflict()) {
1007 found_conflict =
true;
1010 if (found_enum_items ==
nullptr) {
1013 else if (socket_value.enum_items !=
nullptr) {
1014 if (found_enum_items != socket_value.enum_items) {
1015 found_conflict =
true;
1020 if (found_conflict) {
1022 for (
bNode *input_node : group_input_nodes) {
1023 bNodeSocket &socket = input_node->output_socket(interface_input_i);
1025 if (socket_value.enum_items) {
1026 socket_value.
enum_items->remove_user_and_delete_if_last();
1027 socket_value.enum_items =
nullptr;
1029 socket_value.runtime_flag |= NodeSocketValueMenuRuntimeFlag::NODE_MENU_ITEMS_CONFLICT;
1032 else if (found_enum_items !=
nullptr) {
1034 for (
bNode *input_node : group_input_nodes) {
1035 bNodeSocket &socket = input_node->output_socket(interface_input_i);
1037 if (socket_value.enum_items ==
nullptr) {
1038 found_enum_items->add_user();
1046 Vector<bNodeSocketValueMenu> interface_enum_items(ntree.interface_inputs().size(), {0});
1047 for (
const bNode *group_input_node : ntree.group_input_nodes()) {
1048 for (
const int socket_i : ntree.interface_inputs().index_range()) {
1049 const bNodeSocket &output = *group_input_node->output_sockets()[socket_i];
1051 if (output.is_available() && output.type ==
SOCK_MENU) {
1052 this->update_socket_enum_definition(interface_enum_items[socket_i],
1059 bool changed =
false;
1060 for (
const int socket_i : ntree.interface_inputs().index_range()) {
1068 dst.
enum_items->remove_user_and_delete_if_last();
1084 const RuntimeNodeEnumItems *create_runtime_enum_items(
const NodeEnumDefinition &enum_def)
1086 RuntimeNodeEnumItems *enum_items =
new RuntimeNodeEnumItems();
1087 enum_items->items.reinitialize(enum_def.
items_num);
1088 for (
const int i : enum_def.items().index_range()) {
1090 RuntimeNodeEnumItem &dst = enum_items->items[i];
1093 dst.name = src.
name ? src.
name :
"";
1103 this->reset_enum_ptr(default_value);
1104 default_value.
runtime_flag &= ~NODE_MENU_ITEMS_CONFLICT;
1109 if (dst.has_conflict()) {
1115 if (src.has_conflict()) {
1117 this->reset_enum_ptr(dst);
1126 this->reset_enum_ptr(dst);
1134 dst.
enum_items->remove_user_and_delete_if_last();
1142 dst.
enum_items->remove_user_and_delete_if_last();
1146 enum_items->add_user();
1151 void update_link_validation(
bNodeTree &ntree)
1154 const auto is_invalid_enum_ref = [](
const bNodeSocket &socket) ->
bool {
1163 if (!link->fromsock->is_available() || !link->tosock->is_available()) {
1164 link->flag &= ~NODE_LINK_VALID;
1167 if (is_invalid_enum_ref(*link->fromsock) || is_invalid_enum_ref(*link->tosock)) {
1168 link->flag &= ~NODE_LINK_VALID;
1169 ntree.
runtime->link_errors_by_target_node.add(
1170 link->tonode->identifier,
1171 NodeLinkError{TIP_(
"Use node groups to reuse the same menu multiple times")});
1175 if (link->fromsock->runtime->field_state == FieldSocketState::IsField &&
1176 link->tosock->runtime->field_state != FieldSocketState::IsField)
1178 link->flag &= ~NODE_LINK_VALID;
1179 ntree.
runtime->link_errors_by_target_node.add(
1180 link->tonode->identifier,
1181 NodeLinkError{TIP_(
"The node input does not support fields")});
1185 const bNode &from_node = *link->fromnode;
1186 const bNode &to_node = *link->tonode;
1187 if (from_node.
runtime->toposort_left_to_right_index >
1188 to_node.
runtime->toposort_left_to_right_index)
1190 link->
flag &= ~NODE_LINK_VALID;
1191 ntree.
runtime->link_errors_by_target_node.add(
1192 link->tonode->identifier,
1193 NodeLinkError{TIP_(
"The links form a cycle which is not supported")});
1196 if (ntree.
typeinfo->validate_link) {
1200 link->flag &= ~NODE_LINK_VALID;
1201 ntree.
runtime->link_errors_by_target_node.add(
1202 link->tonode->identifier,
1203 NodeLinkError{fmt::format(
"{}: {} " BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE
1205 TIP_(
"Conversion is not supported"),
1206 TIP_(link->fromsock->typeinfo->label),
1207 TIP_(link->tosock->typeinfo->label))});
1216 tree.ensure_topology_cache();
1221 const Vector<const bNodeSocket *> tree_output_sockets = this->find_output_sockets(
tree);
1222 const uint32_t old_topology_hash =
tree.runtime->output_topology_hash;
1223 const uint32_t new_topology_hash = this->get_combined_socket_topology_hash(
1224 tree, tree_output_sockets);
1225 tree.runtime->output_topology_hash = new_topology_hash;
1240 if (remaining_expression.
find_first_not_of(
" */+-0123456789.") == StringRef::not_found) {
1253 if (old_topology_hash != new_topology_hash) {
1259 if (
tree.runtime->changed_flag ==
1262 if (old_topology_hash == new_topology_hash) {
1267 if (!this->check_if_socket_outputs_changed_based_on_flags(
tree, tree_output_sockets)) {
1274 Vector<const bNodeSocket *> find_output_sockets(
const bNodeTree &
tree)
1276 Vector<const bNodeSocket *> sockets;
1277 for (
const bNode *node :
tree.all_nodes()) {
1278 if (!this->is_output_node(*node)) {
1281 for (
const bNodeSocket *socket : node->input_sockets()) {
1282 if (!
STREQ(socket->
idname,
"NodeSocketVirtual")) {
1283 sockets.append(socket);
1290 bool is_output_node(
const bNode &node)
const
1298 if (node.type == GEO_NODE_WARNING) {
1301 if (nodes::gizmos::is_builtin_gizmo_node(node)) {
1307 if (node_group !=
nullptr &&
1323 if (
tree.has_available_link_cycle()) {
1331 combined_hash = noise::hash(combined_hash,
hash);
1333 return combined_hash;
1343 auto get_socket_ptr_hash = [&](
const bNodeSocket &socket) {
1345 return noise::hash(socket_ptr, socket_ptr >> 32);
1348 while (!sockets_to_check.
is_empty()) {
1350 const bNode &node = socket.owner_node();
1352 if (hash_by_socket_id[socket.index_in_tree()].has_value()) {
1353 sockets_to_check.
pop();
1359 if (socket.is_input()) {
1361 bool all_origins_computed =
true;
1362 bool get_value_from_origin =
false;
1363 for (
const bNodeLink *link : socket.directly_linked_links()) {
1364 if (link->is_muted()) {
1367 if (!link->is_available()) {
1370 const bNodeSocket &origin_socket = *link->fromsock;
1371 const std::optional<uint32_t> origin_hash =
1372 hash_by_socket_id[origin_socket.index_in_tree()];
1373 if (origin_hash.has_value()) {
1374 if (get_value_from_origin || socket.
type != origin_socket.
type) {
1375 socket_hash = noise::hash(socket_hash, *origin_hash);
1379 socket_hash = *origin_hash;
1381 get_value_from_origin =
true;
1384 sockets_to_check.
push(&origin_socket);
1385 all_origins_computed =
false;
1388 if (!all_origins_computed) {
1392 if (!get_value_from_origin) {
1393 socket_hash = get_socket_ptr_hash(socket);
1397 bool all_available_inputs_computed =
true;
1398 for (
const bNodeSocket *input_socket : node.input_sockets()) {
1399 if (input_socket->is_available()) {
1400 if (!hash_by_socket_id[input_socket->index_in_tree()].has_value()) {
1401 sockets_to_check.
push(input_socket);
1402 all_available_inputs_computed =
false;
1406 if (!all_available_inputs_computed) {
1410 socket_hash = *hash_by_socket_id[node.input_socket(0).index_in_tree()];
1412 else if (node.is_muted()) {
1413 const bNodeSocket *internal_input = socket.internal_link_input();
1414 if (internal_input ==
nullptr) {
1415 socket_hash = get_socket_ptr_hash(socket);
1418 if (internal_input->
type == socket.
type) {
1419 socket_hash = *hash_by_socket_id[internal_input->index_in_tree()];
1422 socket_hash = get_socket_ptr_hash(socket);
1427 socket_hash = get_socket_ptr_hash(socket);
1428 for (
const bNodeSocket *input_socket : node.input_sockets()) {
1429 if (input_socket->is_available()) {
1430 const uint32_t input_socket_hash = *hash_by_socket_id[input_socket->index_in_tree()];
1431 socket_hash = noise::hash(socket_hash, input_socket_hash);
1439 const bNodeSocket &alpha_socket = node.output_socket(1);
1441 if (alpha_socket.is_directly_linked()) {
1442 socket_hash = noise::hash(socket_hash);
1447 hash_by_socket_id[socket.index_in_tree()] = socket_hash;
1450 sockets_to_check.
pop();
1455 for (
const int i : sockets.index_range()) {
1456 hashes[i] = *hash_by_socket_id[sockets[i]->index_in_tree()];
1465 bool check_if_socket_outputs_changed_based_on_flags(
const bNodeTree &
tree,
1473 pushed_by_socket_id[socket->index_in_tree()] =
true;
1476 while (!sockets_to_check.
is_empty()) {
1478 const bNode &node = socket.owner_node();
1483 const bool only_unused_internal_link_changed = !node.is_muted() &&
1486 if (!only_unused_internal_link_changed) {
1490 if (socket.is_input()) {
1491 for (
const bNodeSocket *origin_socket : socket.directly_linked_sockets()) {
1492 bool &pushed = pushed_by_socket_id[origin_socket->index_in_tree()];
1494 sockets_to_check.
push(origin_socket);
1500 for (
const bNodeSocket *input_socket : node.input_sockets()) {
1501 if (input_socket->is_available()) {
1502 bool &pushed = pushed_by_socket_id[input_socket->index_in_tree()];
1504 sockets_to_check.
push(input_socket);
1511 switch (node.type) {
1520 if (!zone->input_node) {
1523 for (
const bNodeSocket *input_socket : zone->input_node->input_sockets()) {
1524 if (input_socket->is_available()) {
1525 bool &pushed = pushed_by_socket_id[input_socket->index_in_tree()];
1527 sockets_to_check.
push(input_socket);
1539 const bNodeSocket &normal_output = node.output_socket(0);
1541 bool &pushed = pushed_by_socket_id[normal_output.index_in_tree()];
1543 sockets_to_check.
push(&normal_output);
1556 bool update_nested_node_refs(
bNodeTree &ntree)
1558 ntree.ensure_topology_cache();
1561 Map<bNestedNodePath, int32_t> old_id_by_path;
1562 Set<int32_t> old_ids;
1563 for (
const bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
1564 old_id_by_path.add(ref.path, ref.id);
1565 old_ids.add(ref.id);
1568 Vector<bNestedNodePath> nested_node_paths;
1572 const bNode *node = ntree.node_by_id(path.node_id);
1573 if (node && node->is_group() && node->id) {
1575 nested_node_paths.append(path);
1582 for (
StringRefNull idname : {
"GeometryNodeSimulationOutput",
"GeometryNodeBake"}) {
1583 for (
const bNode *node : ntree.nodes_by_type(idname)) {
1584 nested_node_paths.append({node->identifier, -1});
1589 for (
const bNode *node : ntree.group_nodes()) {
1591 if (group ==
nullptr) {
1594 for (
const int i : group->nested_node_refs_span().index_range()) {
1596 nested_node_paths.append({node->identifier, child_ref.
id});
1603 Map<int32_t, bNestedNodePath> new_path_by_id;
1605 const int32_t old_id = old_id_by_path.lookup_default(path, -1);
1608 new_path_by_id.add(old_id, path);
1614 if (!old_ids.contains(new_id) && !new_path_by_id.contains(new_id)) {
1619 new_path_by_id.add(new_id, path);
1623 if (!this->nested_node_refs_changed(ntree, new_path_by_id)) {
1628 if (new_path_by_id.is_empty()) {
1637 for (
const auto item : new_path_by_id.items()) {
1640 ref.
path = item.value;
1650 bool nested_node_refs_changed(
const bNodeTree &ntree,
1651 const Map<int32_t, bNestedNodePath> &new_path_by_id)
1656 for (
const bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
1657 if (!new_path_by_id.contains(ref.id)) {
1664 void reset_changed_flags(
bNodeTree &ntree)
1667 for (
bNode *node : ntree.all_nodes()) {
1669 node->runtime->update = 0;