diff options
69 files changed, 2251 insertions, 1704 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a7ed16f5086..500d8e2e0ac 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -501,16 +501,13 @@ void ntreeFreeLocalTree(struct bNodeTree *ntree); struct bNode *ntreeFindType(const struct bNodeTree *ntree, int type); bool ntreeHasType(const struct bNodeTree *ntree, int type); bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup); -void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree); void ntreeUpdateAllNew(struct Main *main); -/** - * \param tree_update_flag: #eNodeTreeUpdate enum. - */ -void ntreeUpdateAllUsers(struct Main *main, struct ID *id, int tree_update_flag); +void ntreeUpdateAllUsers(struct Main *main, struct ID *id); void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***r_deplist, int *r_deplist_len); +void ntreeUpdateNodeLevels(struct bNodeTree *ntree); /** * XXX: old trees handle output flags automatically based on special output @@ -833,10 +830,6 @@ void nodeClearActive(struct bNodeTree *ntree); void nodeClearActiveID(struct bNodeTree *ntree, short idtype); struct bNode *nodeGetActiveTexture(struct bNodeTree *ntree); -void nodeUpdate(struct bNodeTree *ntree, struct bNode *node); -bool nodeUpdateID(struct bNodeTree *ntree, struct ID *id); -void nodeUpdateInternalLinks(struct bNodeTree *ntree, struct bNode *node); - int nodeSocketIsHidden(const struct bNodeSocket *sock); void ntreeTagUsedSockets(struct bNodeTree *ntree); void nodeSetSocketAvailability(struct bNodeTree *ntree, diff --git a/source/blender/blenkernel/BKE_node_tree_update.h b/source/blender/blenkernel/BKE_node_tree_update.h new file mode 100644 index 00000000000..ebaa56c89c9 --- /dev/null +++ b/source/blender/blenkernel/BKE_node_tree_update.h @@ -0,0 +1,109 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +struct bNode; +struct bNodeSocket; +struct bNodeTree; +struct bNodeLink; +struct Main; +struct ID; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Tag tree as changed without providing any more information about what has changed exactly. + * The update process has to assume that everything may have changed. + * + * Using one of the methods below to tag the tree after changes is preffered when possible. + */ +void BKE_ntree_update_tag_all(struct bNodeTree *ntree); + +/** + * More specialized tag functions that may result in a more efficient update. + */ + +void BKE_ntree_update_tag_node_property(struct bNodeTree *ntree, struct bNode *node); +void BKE_ntree_update_tag_node_new(struct bNodeTree *ntree, struct bNode *node); +void BKE_ntree_update_tag_node_removed(struct bNodeTree *ntree); +void BKE_ntree_update_tag_node_internal_link(struct bNodeTree *ntree, struct bNode *node); + +void BKE_ntree_update_tag_socket_property(struct bNodeTree *ntree, struct bNodeSocket *socket); +void BKE_ntree_update_tag_socket_new(struct bNodeTree *ntree, struct bNodeSocket *socket); +void BKE_ntree_update_tag_socket_type(struct bNodeTree *ntree, struct bNodeSocket *socket); +void BKE_ntree_update_tag_socket_availability(struct bNodeTree *ntree, struct bNodeSocket *socket); +void BKE_ntree_update_tag_socket_removed(struct bNodeTree *ntree); + +void BKE_ntree_update_tag_link_changed(struct bNodeTree *ntree); +void BKE_ntree_update_tag_link_removed(struct bNodeTree *ntree); +void BKE_ntree_update_tag_link_added(struct bNodeTree *ntree, struct bNodeLink *link); +void BKE_ntree_update_tag_link_mute(struct bNodeTree *ntree, struct bNodeLink *link); + +/** Used after file loading when run-time data on the tree has not been initialized yet. */ +void BKE_ntree_update_tag_missing_runtime_data(struct bNodeTree *ntree); +/** Used when the interface sockets/values have changed. */ +void BKE_ntree_update_tag_interface(struct bNodeTree *ntree); +/** Used when an id data block changed that might be used by nodes that need to be updated. */ +void BKE_ntree_update_tag_id_changed(struct Main *bmain, struct ID *id); + +typedef struct NodeTreeUpdateExtraParams { + /** + * Data passed into the callbacks. + */ + void *user_data; + + /** + * Called for every tree that has been changed during the update. This can be used to send + * notifiers to trigger redraws or depsgraph updates. + */ + void (*tree_changed_fn)(struct ID *, struct bNodeTree *, void *user_data); + + /** + * Called for every tree whose output value may have changed based on the provided update tags. + * This can be used to tag the depsgraph if necessary. + */ + void (*tree_output_changed_fn)(struct ID *, struct bNodeTree *, void *user_data); +} NodeTreeUpdateExtraParams; + +/** + * Updates #bmain based on changes to node trees. + */ +void BKE_ntree_update_main(struct Main *bmain, struct NodeTreeUpdateExtraParams *params); + +/** + * Same as #BKE_ntree_update_main, but will first only look at the provided tree and only looks + * at #bmain when something relevant for other data-blocks changed. This avoids scanning #bmain in + * many cases. + * + * If #bmain is null, only the provided tree is updated. This should only be used in very rare + * cases because it may result it incorrectly synced data in DNA. + * + * If #tree is null, this is the same as calling #BKE_ntree_update_main. + */ +void BKE_ntree_update_main_tree(struct Main *bmain, + struct bNodeTree *ntree, + struct NodeTreeUpdateExtraParams *params); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index f6e7f1c2473..02aef4ef79e 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -226,6 +226,7 @@ set(SRC intern/multires_versioning.c intern/nla.c intern/node.cc + intern/node_tree_update.cc intern/type_conversions.cc intern/object.cc intern/object_deform.c @@ -420,6 +421,7 @@ set(SRC BKE_multires.h BKE_nla.h BKE_node.h + BKE_node_tree_update.h BKE_object.h BKE_object_deform.h BKE_object_facemap.h diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index f43cf00a310..edd5073da79 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -84,6 +84,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_packedFile.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -3699,16 +3700,8 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) BLI_mutex_unlock(ima->runtime.cache_mutex); - /* don't use notifiers because they are not 100% sure to succeeded - * this also makes sure all scenes are accounted for. */ - { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->nodetree) { - nodeUpdateID(scene->nodetree, &ima->id); - } - } - } + BKE_ntree_update_tag_id_changed(bmain, &ima->id); + BKE_ntree_update_main(bmain, NULL); } /* return renderpass for a given pass index and active view */ diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 3cea0de32ee..9ea85714b4a 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -346,7 +346,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) { /* Update all group nodes using a node group. */ - ntreeUpdateAllUsers(bmain, new_id, 0); + ntreeUpdateAllUsers(bmain, new_id); } /** diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index ac0dbcb715d..95f41ab4b39 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -50,6 +50,7 @@ #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_texture.h" #include "BLO_read_write.h" @@ -2085,5 +2086,5 @@ void BKE_linestyle_default_shader(const bContext *C, FreestyleLineStyle *linesty tosock = BLI_findlink(&output_linestyle->inputs, 0); /* Color */ nodeAddLink(ntree, input_texure, fromsock, output_linestyle, tosock); - ntreeUpdateTree(CTX_data_main(C), ntree); + BKE_ntree_update_main_tree(CTX_data_main(C), ntree, NULL); } diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index fc2e7d0a6a3..b0c93a5614d 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -70,6 +70,7 @@ #include "BKE_main.h" #include "BKE_movieclip.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" #include "IMB_imbuf.h" @@ -1695,17 +1696,7 @@ void BKE_movieclip_reload(Main *bmain, MovieClip *clip) movieclip_calc_length(clip); - /* same as for image update -- don't use notifiers because they are not 100% sure to succeeded - * (node trees which are not currently visible wouldn't be refreshed) - */ - { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->nodetree) { - nodeUpdateID(scene->nodetree, &clip->id); - } - } - } + BKE_ntree_update_tag_id_changed(bmain, &clip->id); } void BKE_movieclip_update_scopes(MovieClip *clip, MovieClipUser *user, MovieClipScopes *scopes) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 45345b9aa94..534d131d8c0 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -74,6 +74,7 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "RNA_access.h" #include "RNA_define.h" @@ -710,6 +711,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) ntree->execdata = nullptr; ntree->field_inferencing_interface = nullptr; + BKE_ntree_update_tag_missing_runtime_data(ntree); BLO_read_data_address(reader, &ntree->adt); BKE_animdata_blend_read_data(reader, ntree->adt); @@ -852,11 +854,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) /* TODO: should be dealt by new generic cache handling of IDs... */ ntree->previews = nullptr; - if (ntree->type == NTREE_GEOMETRY) { - /* Update field referencing for the geometry nodes modifier. */ - ntree->update |= NTREE_UPDATE_FIELD_INFERENCING; - } - BLO_read_data_address(reader, &ntree->preview); BKE_previewimg_blend_read(reader, ntree->preview); @@ -1179,6 +1176,7 @@ static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo) /* Deprecated integer type. */ ntree->type = ntree->typeinfo->type; + BKE_ntree_update_tag_all(ntree); } static void node_set_typeinfo(const struct bContext *C, @@ -1229,6 +1227,7 @@ static void node_socket_set_typeinfo(bNodeTree *ntree, ntree->init &= ~NTREE_TYPE_INIT; } + BKE_ntree_update_tag_socket_type(ntree, sock); } /* Set specific typeinfo pointers in all node trees on register/unregister */ @@ -1721,7 +1720,7 @@ bNodeSocket *nodeAddSocket(bNodeTree *ntree, BLI_remlink(lb, sock); /* does nothing for new socket */ BLI_addtail(lb, sock); - node->update |= NODE_UPDATE; + BKE_ntree_update_tag_socket_new(ntree, sock); return sock; } @@ -1740,8 +1739,6 @@ bNodeSocket *nodeInsertSocket(bNodeTree *ntree, BLI_remlink(lb, sock); /* does nothing for new socket */ BLI_insertlinkbefore(lb, next_sock, sock); - node->update |= NODE_UPDATE; - return sock; } @@ -2020,7 +2017,7 @@ void nodeRemoveSocketEx(struct bNodeTree *ntree, node_socket_free(sock, do_id_user); MEM_freeN(sock); - node->update |= NODE_UPDATE; + BKE_ntree_update_tag_socket_removed(ntree); } void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node) @@ -2043,7 +2040,7 @@ void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node) } BLI_listbase_clear(&node->outputs); - node->update |= NODE_UPDATE; + BKE_ntree_update_tag_socket_removed(ntree); } bNode *nodeFindNodebyName(bNodeTree *ntree, const char *name) @@ -2195,7 +2192,7 @@ bNode *nodeAddNode(const struct bContext *C, bNodeTree *ntree, const char *idnam BLI_strncpy(node->idname, idname, sizeof(node->idname)); node_set_typeinfo(C, ntree, node, nodeTypeFind(idname)); - ntree->update |= NTREE_UPDATE_NODES; + BKE_ntree_update_tag_node_new(ntree, node); return node; } @@ -2319,7 +2316,7 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, } if (ntree) { - ntree->update |= NTREE_UPDATE_NODES; + BKE_ntree_update_tag_node_new(ntree, node_dst); } /* Reset the declaration of the new node. */ @@ -2416,7 +2413,7 @@ bNodeLink *nodeAddLink( } if (ntree) { - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_added(ntree, link); } if (link != nullptr && link->tosock->flag & SOCK_MULTI_INPUT) { @@ -2439,7 +2436,7 @@ void nodeRemLink(bNodeTree *ntree, bNodeLink *link) MEM_freeN(link); if (ntree) { - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_removed(ntree); } } @@ -2539,7 +2536,7 @@ void nodeMuteLinkToggle(bNodeTree *ntree, bNodeLink *link) } if (ntree) { - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_mute(ntree, link); } } @@ -2550,8 +2547,6 @@ void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock) nodeRemLink(ntree, link); } } - - ntree->update |= NTREE_UPDATE_LINKS; } bool nodeLinkIsHidden(const bNodeLink *link) @@ -2616,7 +2611,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node) link->flag |= NODE_LINK_MUTED; } - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_changed(ntree); } else { if (link->tosock->flag & SOCK_MULTI_INPUT) { @@ -3044,9 +3039,6 @@ void nodeUnlinkNode(bNodeTree *ntree, bNode *node) ListBase *lb; if (link->fromnode == node) { lb = &node->outputs; - if (link->tonode) { - link->tonode->update |= NODE_UPDATE; - } } else if (link->tonode == node) { lb = &node->inputs; @@ -3135,7 +3127,7 @@ static void node_free_node(bNodeTree *ntree, bNode *node) MEM_freeN(node); if (ntree) { - ntree->update |= NTREE_UPDATE_NODES; + BKE_ntree_update_tag_node_removed(ntree); } } @@ -3505,12 +3497,11 @@ bNodeSocket *ntreeAddSocketInterface(bNodeTree *ntree, bNodeSocket *iosock = make_socket_interface(ntree, in_out, idname, name); if (in_out == SOCK_IN) { BLI_addtail(&ntree->inputs, iosock); - ntree->update |= NTREE_UPDATE_GROUP_IN; } else if (in_out == SOCK_OUT) { BLI_addtail(&ntree->outputs, iosock); - ntree->update |= NTREE_UPDATE_GROUP_OUT; } + BKE_ntree_update_tag_interface(ntree); return iosock; } @@ -3523,12 +3514,11 @@ bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree, bNodeSocket *iosock = make_socket_interface(ntree, in_out, idname, name); if (in_out == SOCK_IN) { BLI_insertlinkbefore(&ntree->inputs, next_sock, iosock); - ntree->update |= NTREE_UPDATE_GROUP_IN; } else if (in_out == SOCK_OUT) { BLI_insertlinkbefore(&ntree->outputs, next_sock, iosock); - ntree->update |= NTREE_UPDATE_GROUP_OUT; } + BKE_ntree_update_tag_interface(ntree); return iosock; } @@ -3574,7 +3564,7 @@ void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock) node_socket_interface_free(ntree, sock, true); MEM_freeN(sock); - ntree->update |= NTREE_UPDATE_GROUP; + BKE_ntree_update_tag_interface(ntree); } /* generates a valid RNA identifier from the node tree name */ @@ -3918,10 +3908,13 @@ int nodeSocketIsHidden(const bNodeSocket *sock) return ((sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0); } -void nodeSetSocketAvailability(bNodeTree *UNUSED(ntree), bNodeSocket *sock, bool is_available) +void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, bool is_available) { - /* #ntree is not needed right now, but it's generally necessary when changing the tree because we - * want to tag it as changed in the future. */ + const bool was_available = (sock->flag & SOCK_UNAVAIL) == 0; + if (is_available != was_available) { + BKE_ntree_update_tag_socket_availability(ntree, sock); + } + if (is_available) { sock->flag &= ~SOCK_UNAVAIL; } @@ -4373,7 +4366,7 @@ void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***r_deplist, } /* only updates node->level for detecting cycles links */ -static void ntree_update_node_level(bNodeTree *ntree) +void ntreeUpdateNodeLevels(bNodeTree *ntree) { /* first clear tag */ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -4408,749 +4401,42 @@ void ntreeTagUsedSockets(bNodeTree *ntree) } } -static void ntree_update_link_pointers(bNodeTree *ntree) -{ - /* first clear data */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - sock->link = nullptr; - } - } - - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - link->tosock->link = link; - } - - ntreeTagUsedSockets(ntree); -} - -static void ntree_validate_links(bNodeTree *ntree) -{ - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - link->flag |= NODE_LINK_VALID; - if (link->fromnode && link->tonode && link->fromnode->level <= link->tonode->level) { - link->flag &= ~NODE_LINK_VALID; - } - else if (ntree->typeinfo->validate_link) { - if (!ntree->typeinfo->validate_link((eNodeSocketDatatype)link->fromsock->type, - (eNodeSocketDatatype)link->tosock->type)) { - link->flag &= ~NODE_LINK_VALID; - } - } - } -} - void ntreeUpdateAllNew(Main *main) { + Vector<bNodeTree *> new_ntrees; + /* Update all new node trees on file read or append, to add/remove sockets * in groups nodes if the group changed, and handle any update flags that * might have been set in file reading or versioning. */ FOREACH_NODETREE_BEGIN (main, ntree, owner_id) { if (owner_id->tag & LIB_TAG_NEW) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->typeinfo->group_update_func) { - node->typeinfo->group_update_func(ntree, node); - } - } - - ntreeUpdateTree(nullptr, ntree); + BKE_ntree_update_tag_all(ntree); } } FOREACH_NODETREE_END; + BKE_ntree_update_main(main, nullptr); } -namespace blender::bke::node_field_inferencing { - -static bool is_field_socket_type(eNodeSocketDatatype type) -{ - return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); -} - -static bool is_field_socket_type(const SocketRef &socket) -{ - return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type); -} - -static bool update_field_inferencing(bNodeTree &btree); - -static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, - const InputSocketRef &socket) -{ - if (!is_field_socket_type(socket)) { - return InputSocketFieldType::None; - } - if (node.is_reroute_node()) { - return InputSocketFieldType::IsSupported; - } - if (node.is_group_output_node()) { - /* Outputs always support fields when the data type is correct. */ - return InputSocketFieldType::IsSupported; - } - if (node.is_undefined()) { - return InputSocketFieldType::None; - } - - const NodeDeclaration *node_decl = node.declaration(); - - /* Node declarations should be implemented for nodes involved here. */ - BLI_assert(node_decl != nullptr); - - /* Get the field type from the declaration. */ - const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; - const InputSocketFieldType field_type = socket_decl.input_field_type(); - if (field_type == InputSocketFieldType::Implicit) { - return field_type; - } - if (node_decl->is_function_node()) { - /* In a function node, every socket supports fields. */ - return InputSocketFieldType::IsSupported; - } - return field_type; -} - -static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node, - const OutputSocketRef &socket) -{ - if (!is_field_socket_type(socket)) { - /* Non-field sockets always output data. */ - return OutputFieldDependency::ForDataSource(); - } - if (node.is_reroute_node()) { - /* The reroute just forwards what is passed in. */ - return OutputFieldDependency::ForDependentField(); - } - if (node.is_group_input_node()) { - /* Input nodes get special treatment in #determine_group_input_states. */ - return OutputFieldDependency::ForDependentField(); - } - if (node.is_undefined()) { - return OutputFieldDependency::ForDataSource(); - } - - const NodeDeclaration *node_decl = node.declaration(); - - /* Node declarations should be implemented for nodes involved here. */ - BLI_assert(node_decl != nullptr); - - if (node_decl->is_function_node()) { - /* In a generic function node, all outputs depend on all inputs. */ - return OutputFieldDependency::ForDependentField(); - } - - /* Use the socket declaration. */ - const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()]; - return socket_decl.output_field_dependency(); -} - -static FieldInferencingInterface get_dummy_field_inferencing_interface(const NodeRef &node) -{ - FieldInferencingInterface inferencing_interface; - inferencing_interface.inputs.append_n_times(InputSocketFieldType::None, node.inputs().size()); - inferencing_interface.outputs.append_n_times(OutputFieldDependency::ForDataSource(), - node.outputs().size()); - return inferencing_interface; -} - -/** - * Retrieves information about how the node interacts with fields. - * In the future, this information can be stored in the node declaration. This would allow this - * function to return a reference, making it more efficient. - */ -static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node) -{ - /* Node groups already reference all required information, so just return that. */ - if (node.is_group_node()) { - bNodeTree *group = (bNodeTree *)node.bnode()->id; - if (group == nullptr) { - return FieldInferencingInterface(); - } - if (!ntreeIsRegistered(group)) { - /* This can happen when there is a linked node group that was not found (see T92799). */ - return get_dummy_field_inferencing_interface(node); - } - if (group->field_inferencing_interface == nullptr) { - /* Update group recursively. */ - update_field_inferencing(*group); - } - return *group->field_inferencing_interface; - } - - FieldInferencingInterface inferencing_interface; - for (const InputSocketRef *input_socket : node.inputs()) { - inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket)); - } - - for (const OutputSocketRef *output_socket : node.outputs()) { - inferencing_interface.outputs.append( - get_interface_output_field_dependency(node, *output_socket)); - } - 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 InputSocketRef *> gather_input_socket_dependencies( - const OutputFieldDependency &field_dependency, const NodeRef &node) -{ - const OutputSocketFieldType type = field_dependency.field_type(); - Vector<const InputSocketRef *> input_sockets; - switch (type) { - case OutputSocketFieldType::FieldSource: - case OutputSocketFieldType::None: { - break; - } - case OutputSocketFieldType::DependentField: { - /* This output depends on all inputs. */ - input_sockets.extend(node.inputs()); - 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(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 InputSocketRef &group_output_socket, - 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 InputSocketRef *> handled_sockets; - Stack<const InputSocketRef *> 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 InputSocketRef *input_socket = sockets_to_check.pop(); - - for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { - const NodeRef &origin_node = origin_socket->node(); - const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; - - if (origin_state.is_field_source) { - if (origin_node.is_group_input_node()) { - /* 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 = - get_node_field_inferencing_interface(origin_node); - const OutputFieldDependency &field_dependency = - inferencing_interface.outputs[origin_socket->index()]; - - /* Propagate search further to the left. */ - for (const InputSocketRef *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->id()].is_single) { - if (handled_sockets.add(origin_input_socket)) { - sockets_to_check.push(origin_input_socket); - } - } - } - } - } - } - return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices)); -} - -static void propagate_data_requirements_from_right_to_left( - const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) -{ - const NodeTreeRef::ToposortResult toposort_result = tree.toposort( - NodeTreeRef::ToposortDirection::RightToLeft); - - for (const NodeRef *node : toposort_result.sorted_nodes) { - const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( - *node); - - for (const OutputSocketRef *output_socket : node->outputs()) { - SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; - - 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 InputSocketRef *target_socket : output_socket->directly_linked_sockets()) { - if (target_socket->is_available()) { - state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single; - } - } - - if (state.requires_single) { - bool any_input_is_field_implicitly = false; - const Vector<const InputSocketRef *> connected_inputs = gather_input_socket_dependencies( - field_dependency, *node); - for (const InputSocketRef *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 InputSocketRef *input_socket : connected_inputs) { - field_state_by_socket_id[input_socket->id()].requires_single = true; - } - } - } - } - - /* Some inputs do not require fields independent of what the outputs are connected to. */ - for (const InputSocketRef *input_socket : node->inputs()) { - SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; - if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { - state.requires_single = true; - state.is_always_single = true; - } - } - } -} - -static void determine_group_input_states( - const NodeTreeRef &tree, - FieldInferencingInterface &new_inferencing_interface, - const MutableSpan<SocketFieldState> field_state_by_socket_id) -{ - { - /* Non-field inputs never support fields. */ - int index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) { - if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { - 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 NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { - for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { - SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; - if (state.requires_single) { - new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None; - } - } - } - /* If an input does not support fields, this should be reflected in all Group Input nodes. */ - for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { - for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { - SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; - 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->outputs().last()->id()]; - dummy_socket_state.requires_single = true; - } -} - -static void propagate_field_status_from_left_to_right( - const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) -{ - const NodeTreeRef::ToposortResult toposort_result = tree.toposort( - NodeTreeRef::ToposortDirection::LeftToRight); - - for (const NodeRef *node : toposort_result.sorted_nodes) { - if (node->is_group_input_node()) { - continue; - } - - const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( - *node); - - /* Update field state of input sockets, also taking into account linked origin sockets. */ - for (const InputSocketRef *input_socket : node->inputs()) { - SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; - if (state.is_always_single) { - state.is_single = true; - continue; - } - state.is_single = true; - if (input_socket->directly_linked_sockets().is_empty()) { - if (inferencing_interface.inputs[input_socket->index()] == - InputSocketFieldType::Implicit) { - state.is_single = false; - } - } - else { - for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { - if (!field_state_by_socket_id[origin_socket->id()].is_single) { - state.is_single = false; - break; - } - } - } - } - - /* Update field state of output sockets, also taking into account input sockets. */ - for (const OutputSocketRef *output_socket : node->outputs()) { - SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; - 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 InputSocketRef *input_socket : - gather_input_socket_dependencies(field_dependency, *node)) { - if (!input_socket->is_available()) { - continue; - } - if (!field_state_by_socket_id[input_socket->id()].is_single) { - state.is_single = false; - break; - } - } - break; - } - } - } - } -} - -static void determine_group_output_states(const NodeTreeRef &tree, - FieldInferencingInterface &new_inferencing_interface, - const Span<SocketFieldState> field_state_by_socket_id) -{ - for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) { - /* Ignore inactive group output nodes. */ - if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) { - continue; - } - /* Determine dependencies of all group outputs. */ - for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) { - OutputFieldDependency field_dependency = find_group_output_dependencies( - *group_output_socket, field_state_by_socket_id); - new_inferencing_interface.outputs[group_output_socket->index()] = std::move( - field_dependency); - } - break; - } -} - -static void update_socket_shapes(const NodeTreeRef &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 InputSocketRef *socket : tree.input_sockets()) { - bNodeSocket *bsocket = socket->bsocket(); - const SocketFieldState &state = field_state_by_socket_id[socket->id()]; - bsocket->display_shape = get_shape_for_state(state); - } - for (const OutputSocketRef *socket : tree.output_sockets()) { - bNodeSocket *bsocket = socket->bsocket(); - const SocketFieldState &state = field_state_by_socket_id[socket->id()]; - bsocket->display_shape = get_shape_for_state(state); - } -} - -static bool update_field_inferencing(bNodeTree &btree) -{ - using namespace blender::nodes; - if (btree.type != NTREE_GEOMETRY) { - return false; - } - - /* Create new inferencing interface for this node group. */ - FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface(); - new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs), - InputSocketFieldType::IsSupported); - new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs), - OutputFieldDependency::ForDataSource()); - - /* Create #NodeTreeRef to accelerate various queries on the node tree (e.g. linked sockets). */ - const NodeTreeRef tree{&btree}; - - /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ - Array<SocketFieldState> field_state_by_socket_id(tree.sockets().size()); - - propagate_data_requirements_from_right_to_left(tree, 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, field_state_by_socket_id); - determine_group_output_states(tree, *new_inferencing_interface, field_state_by_socket_id); - update_socket_shapes(tree, field_state_by_socket_id); - - /* Update the previous group interface. */ - const bool group_interface_changed = btree.field_inferencing_interface == nullptr || - *btree.field_inferencing_interface != - *new_inferencing_interface; - delete btree.field_inferencing_interface; - btree.field_inferencing_interface = new_inferencing_interface; - - return group_interface_changed; -} - -} // namespace blender::bke::node_field_inferencing - -void ntreeUpdateAllUsers(Main *main, ID *id, const int tree_update_flag) +void ntreeUpdateAllUsers(Main *main, ID *id) { if (id == nullptr) { return; } + bool need_update = false; + /* Update all users of ngroup, to add/remove sockets as needed. */ FOREACH_NODETREE_BEGIN (main, ntree, owner_id) { - bool need_update = false; - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->id == id) { - if (node->typeinfo->group_update_func) { - node->typeinfo->group_update_func(ntree, node); - } - + BKE_ntree_update_tag_node_property(ntree, node); need_update = true; } } - - if (need_update) { - ntree->update |= tree_update_flag; - ntreeUpdateTree(tree_update_flag ? main : nullptr, ntree); - } } FOREACH_NODETREE_END; - - if (GS(id->name) == ID_NT) { - bNodeTree *ngroup = (bNodeTree *)id; - if (ngroup->type == NTREE_GEOMETRY && (ngroup->update & NTREE_UPDATE_GROUP)) { - LISTBASE_FOREACH (Object *, object, &main->objects) { - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Nodes) { - NodesModifierData *nmd = (NodesModifierData *)md; - if (nmd->node_group == ngroup) { - MOD_nodes_update_interface(object, nmd); - } - } - } - } - } - } -} - -void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) -{ - if (!ntree) { - return; - } - - /* Avoid re-entrant updates, can be caused by RNA update callbacks. */ - if (ntree->is_updating) { - return; - } - ntree->is_updating = true; - - if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { - /* set the bNodeSocket->link pointers */ - ntree_update_link_pointers(ntree); - } - - /* update individual nodes */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - /* node tree update tags override individual node update flags */ - if ((node->update & NODE_UPDATE) || (ntree->update & NTREE_UPDATE)) { - if (node->typeinfo->updatefunc) { - node->typeinfo->updatefunc(ntree, node); - } - - nodeUpdateInternalLinks(ntree, node); - } - } - - /* generic tree update callback */ - if (ntree->typeinfo->update) { - ntree->typeinfo->update(ntree); - } - /* XXX this should be moved into the tree type update callback for tree supporting node groups. - * Currently the node tree interface is still a generic feature of the base NodeTree type. - */ - if (ntree->update & NTREE_UPDATE_GROUP) { - ntreeInterfaceTypeUpdate(ntree); - } - - int tree_user_update_flag = 0; - - if (ntree->update & NTREE_UPDATE) { - /* If the field interface of this node tree has changed, all node trees using - * this group will need to recalculate their interface as well. */ - if (blender::bke::node_field_inferencing::update_field_inferencing(*ntree)) { - tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING; - } - } - - if (bmain) { - ntreeUpdateAllUsers(bmain, &ntree->id, tree_user_update_flag); - } - - if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { - /* node updates can change sockets or links, repeat link pointer update afterward */ - ntree_update_link_pointers(ntree); - - /* update the node level from link dependencies */ - ntree_update_node_level(ntree); - - /* check link validity */ - ntree_validate_links(ntree); - } - - /* clear update flags */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - node->update = 0; - } - ntree->update = 0; - - ntree->is_updating = false; -} - -void nodeUpdate(bNodeTree *ntree, bNode *node) -{ - /* Avoid re-entrant updates, can be caused by RNA update callbacks. */ - if (ntree->is_updating) { - return; - } - ntree->is_updating = true; - - if (node->typeinfo->updatefunc) { - node->typeinfo->updatefunc(ntree, node); - } - - nodeUpdateInternalLinks(ntree, node); - - /* clear update flag */ - node->update = 0; - - ntree->is_updating = false; -} - -bool nodeUpdateID(bNodeTree *ntree, ID *id) -{ - bool changed = false; - - if (ELEM(nullptr, id, ntree)) { - return changed; - } - - /* Avoid re-entrant updates, can be caused by RNA update callbacks. */ - if (ntree->is_updating) { - return changed; - } - ntree->is_updating = true; - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->id == id) { - changed = true; - node->update |= NODE_UPDATE_ID; - if (node->typeinfo->updatefunc) { - node->typeinfo->updatefunc(ntree, node); - } - /* clear update flag */ - node->update = 0; - } - } - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - nodeUpdateInternalLinks(ntree, node); - } - - ntree->is_updating = false; - return changed; -} - -void nodeUpdateInternalLinks(bNodeTree *ntree, bNode *node) -{ - BLI_freelistN(&node->internal_links); - if (!node->typeinfo->no_muting) { - node_internal_links_create(ntree, node); + if (need_update) { + BKE_ntree_update_main(main, nullptr); } } diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc new file mode 100644 index 00000000000..427fac747dc --- /dev/null +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -0,0 +1,1658 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_map.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_noise.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" +#include "BLI_vector_set.hh" + +#include "DNA_anim_types.h" +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" + +#include "BKE_anim_data.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_node_tree_update.h" + +#include "MOD_nodes.h" + +#include "NOD_node_declaration.hh" +#include "NOD_node_tree_ref.hh" + +#include "DEG_depsgraph_query.h" + +using namespace blender::nodes; + +/** + * These flags are used by the `changed_flag` field in #bNodeTree, #bNode and #bNodeSocket. + * This enum is not part of the public api. It should be used through the `BKE_ntree_update_tag_*` + * api. + */ +enum eNodeTreeChangedFlag { + NTREE_CHANGED_NOTHING = 0, + NTREE_CHANGED_ANY = (1 << 1), + NTREE_CHANGED_NODE_PROPERTY = (1 << 2), + NTREE_CHANGED_NODE_OUTPUT = (1 << 3), + NTREE_CHANGED_INTERFACE = (1 << 4), + NTREE_CHANGED_LINK = (1 << 5), + NTREE_CHANGED_REMOVED_NODE = (1 << 6), + NTREE_CHANGED_REMOVED_SOCKET = (1 << 7), + NTREE_CHANGED_SOCKET_PROPERTY = (1 << 8), + NTREE_CHANGED_INTERNAL_LINK = (1 << 9), + NTREE_CHANGED_ALL = -1, +}; + +static void add_tree_tag(bNodeTree *ntree, const eNodeTreeChangedFlag flag) +{ + ntree->changed_flag |= flag; +} + +static void add_node_tag(bNodeTree *ntree, bNode *node, const eNodeTreeChangedFlag flag) +{ + add_tree_tag(ntree, flag); + node->changed_flag |= flag; +} + +static void add_socket_tag(bNodeTree *ntree, bNodeSocket *socket, const eNodeTreeChangedFlag flag) +{ + add_tree_tag(ntree, flag); + socket->changed_flag |= flag; +} + +namespace blender::bke { + +namespace node_field_inferencing { + +static bool is_field_socket_type(eNodeSocketDatatype type) +{ + return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); +} + +static bool is_field_socket_type(const SocketRef &socket) +{ + return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type); +} + +static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, + const InputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + return InputSocketFieldType::None; + } + if (node.is_reroute_node()) { + return InputSocketFieldType::IsSupported; + } + if (node.is_group_output_node()) { + /* Outputs always support fields when the data type is correct. */ + return InputSocketFieldType::IsSupported; + } + if (node.is_undefined()) { + return InputSocketFieldType::None; + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + /* Get the field type from the declaration. */ + const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; + const InputSocketFieldType field_type = socket_decl.input_field_type(); + if (field_type == InputSocketFieldType::Implicit) { + return field_type; + } + if (node_decl->is_function_node()) { + /* In a function node, every socket supports fields. */ + return InputSocketFieldType::IsSupported; + } + return field_type; +} + +static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node, + const OutputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + /* Non-field sockets always output data. */ + return OutputFieldDependency::ForDataSource(); + } + if (node.is_reroute_node()) { + /* The reroute just forwards what is passed in. */ + return OutputFieldDependency::ForDependentField(); + } + if (node.is_group_input_node()) { + /* Input nodes get special treatment in #determine_group_input_states. */ + return OutputFieldDependency::ForDependentField(); + } + if (node.is_undefined()) { + return OutputFieldDependency::ForDataSource(); + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + if (node_decl->is_function_node()) { + /* In a generic function node, all outputs depend on all inputs. */ + return OutputFieldDependency::ForDependentField(); + } + + /* Use the socket declaration. */ + const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()]; + return socket_decl.output_field_dependency(); +} + +static FieldInferencingInterface get_dummy_field_inferencing_interface(const NodeRef &node) +{ + FieldInferencingInterface inferencing_interface; + inferencing_interface.inputs.append_n_times(InputSocketFieldType::None, node.inputs().size()); + inferencing_interface.outputs.append_n_times(OutputFieldDependency::ForDataSource(), + node.outputs().size()); + return inferencing_interface; +} + +/** + * Retrieves information about how the node interacts with fields. + * In the future, this information can be stored in the node declaration. This would allow this + * function to return a reference, making it more efficient. + */ +static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node) +{ + /* Node groups already reference all required information, so just return that. */ + if (node.is_group_node()) { + bNodeTree *group = (bNodeTree *)node.bnode()->id; + if (group == nullptr) { + return FieldInferencingInterface(); + } + if (!ntreeIsRegistered(group)) { + /* This can happen when there is a linked node group that was not found (see T92799). */ + return get_dummy_field_inferencing_interface(node); + } + if (group->field_inferencing_interface == nullptr) { + /* This shouldn't happen because referenced node groups should always be updated first. */ + BLI_assert_unreachable(); + } + return *group->field_inferencing_interface; + } + + FieldInferencingInterface inferencing_interface; + for (const InputSocketRef *input_socket : node.inputs()) { + inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket)); + } + + for (const OutputSocketRef *output_socket : node.outputs()) { + inferencing_interface.outputs.append( + get_interface_output_field_dependency(node, *output_socket)); + } + 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 InputSocketRef *> gather_input_socket_dependencies( + const OutputFieldDependency &field_dependency, const NodeRef &node) +{ + const OutputSocketFieldType type = field_dependency.field_type(); + Vector<const InputSocketRef *> input_sockets; + switch (type) { + case OutputSocketFieldType::FieldSource: + case OutputSocketFieldType::None: { + break; + } + case OutputSocketFieldType::DependentField: { + /* This output depends on all inputs. */ + input_sockets.extend(node.inputs()); + 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(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 InputSocketRef &group_output_socket, + 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 InputSocketRef *> handled_sockets; + Stack<const InputSocketRef *> 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 InputSocketRef *input_socket = sockets_to_check.pop(); + + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { + const NodeRef &origin_node = origin_socket->node(); + const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; + + if (origin_state.is_field_source) { + if (origin_node.is_group_input_node()) { + /* 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 = + get_node_field_inferencing_interface(origin_node); + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[origin_socket->index()]; + + /* Propagate search further to the left. */ + for (const InputSocketRef *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->id()].is_single) { + if (handled_sockets.add(origin_input_socket)) { + sockets_to_check.push(origin_input_socket); + } + } + } + } + } + } + return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices)); +} + +static void propagate_data_requirements_from_right_to_left( + const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) +{ + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( + NodeTreeRef::ToposortDirection::RightToLeft); + + for (const NodeRef *node : toposort_result.sorted_nodes) { + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + + 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 InputSocketRef *target_socket : output_socket->directly_linked_sockets()) { + if (target_socket->is_available()) { + state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single; + } + } + + if (state.requires_single) { + bool any_input_is_field_implicitly = false; + const Vector<const InputSocketRef *> connected_inputs = gather_input_socket_dependencies( + field_dependency, *node); + for (const InputSocketRef *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 InputSocketRef *input_socket : connected_inputs) { + field_state_by_socket_id[input_socket->id()].requires_single = true; + } + } + } + } + + /* Some inputs do not require fields independent of what the outputs are connected to. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { + state.requires_single = true; + state.is_always_single = true; + } + } + } +} + +static void determine_group_input_states( + const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const MutableSpan<SocketFieldState> field_state_by_socket_id) +{ + { + /* Non-field inputs never support fields. */ + int index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) { + if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { + 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 NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + if (state.requires_single) { + new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None; + } + } + } + /* If an input does not support fields, this should be reflected in all Group Input nodes. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + 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->outputs().last()->id()]; + dummy_socket_state.requires_single = true; + } +} + +static void propagate_field_status_from_left_to_right( + const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) +{ + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( + NodeTreeRef::ToposortDirection::LeftToRight); + + for (const NodeRef *node : toposort_result.sorted_nodes) { + if (node->is_group_input_node()) { + continue; + } + + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + /* Update field state of input sockets, also taking into account linked origin sockets. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (state.is_always_single) { + state.is_single = true; + continue; + } + state.is_single = true; + if (input_socket->directly_linked_sockets().is_empty()) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + state.is_single = false; + } + } + else { + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { + if (!field_state_by_socket_id[origin_socket->id()].is_single) { + state.is_single = false; + break; + } + } + } + } + + /* Update field state of output sockets, also taking into account input sockets. */ + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + 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 InputSocketRef *input_socket : + gather_input_socket_dependencies(field_dependency, *node)) { + if (!input_socket->is_available()) { + continue; + } + if (!field_state_by_socket_id[input_socket->id()].is_single) { + state.is_single = false; + break; + } + } + break; + } + } + } + } +} + +static void determine_group_output_states(const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const Span<SocketFieldState> field_state_by_socket_id) +{ + for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) { + /* Ignore inactive group output nodes. */ + if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) { + continue; + } + /* Determine dependencies of all group outputs. */ + for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) { + OutputFieldDependency field_dependency = find_group_output_dependencies( + *group_output_socket, field_state_by_socket_id); + new_inferencing_interface.outputs[group_output_socket->index()] = std::move( + field_dependency); + } + break; + } +} + +static void update_socket_shapes(const NodeTreeRef &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 InputSocketRef *socket : tree.input_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + bsocket->display_shape = get_shape_for_state(state); + } + for (const OutputSocketRef *socket : tree.output_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + bsocket->display_shape = get_shape_for_state(state); + } +} + +static bool update_field_inferencing(const NodeTreeRef &tree) +{ + bNodeTree &btree = *tree.btree(); + + /* Create new inferencing interface for this node group. */ + FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface(); + new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs), + InputSocketFieldType::IsSupported); + new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs), + OutputFieldDependency::ForDataSource()); + + /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ + Array<SocketFieldState> field_state_by_socket_id(tree.sockets().size()); + + propagate_data_requirements_from_right_to_left(tree, 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, field_state_by_socket_id); + determine_group_output_states(tree, *new_inferencing_interface, field_state_by_socket_id); + update_socket_shapes(tree, field_state_by_socket_id); + + /* Update the previous group interface. */ + const bool group_interface_changed = btree.field_inferencing_interface == nullptr || + *btree.field_inferencing_interface != + *new_inferencing_interface; + delete btree.field_inferencing_interface; + btree.field_inferencing_interface = new_inferencing_interface; + + return group_interface_changed; +} + +} // namespace node_field_inferencing + +/** + * Common datatype priorities, works for compositor, shader and texture nodes alike + * defines priority of datatype connection based on output type (to): + * `< 0`: never connect these types. + * `>= 0`: priority of connection (higher values chosen first). + */ +static int get_internal_link_type_priority(const bNodeSocketType *from, const bNodeSocketType *to) +{ + switch (to->type) { + case SOCK_RGBA: + switch (from->type) { + case SOCK_RGBA: + return 4; + case SOCK_FLOAT: + return 3; + case SOCK_INT: + return 2; + case SOCK_BOOLEAN: + return 1; + } + return -1; + case SOCK_VECTOR: + switch (from->type) { + case SOCK_VECTOR: + return 4; + case SOCK_FLOAT: + return 3; + case SOCK_INT: + return 2; + case SOCK_BOOLEAN: + return 1; + } + return -1; + case SOCK_FLOAT: + switch (from->type) { + case SOCK_FLOAT: + return 5; + case SOCK_INT: + return 4; + case SOCK_BOOLEAN: + return 3; + case SOCK_RGBA: + return 2; + case SOCK_VECTOR: + return 1; + } + return -1; + case SOCK_INT: + switch (from->type) { + case SOCK_INT: + return 5; + case SOCK_FLOAT: + return 4; + case SOCK_BOOLEAN: + return 3; + case SOCK_RGBA: + return 2; + case SOCK_VECTOR: + return 1; + } + return -1; + case SOCK_BOOLEAN: + switch (from->type) { + case SOCK_BOOLEAN: + return 5; + case SOCK_INT: + return 4; + case SOCK_FLOAT: + return 3; + case SOCK_RGBA: + return 2; + case SOCK_VECTOR: + return 1; + } + return -1; + } + + /* The rest of the socket types only allow an internal link if both the input and output socket + * have the same type. If the sockets are custom, we check the idname instead. */ + if (to->type == from->type && (to->type != SOCK_CUSTOM || STREQ(to->idname, from->idname))) { + return 1; + } + + return -1; +} + +using TreeNodePair = std::pair<bNodeTree *, bNode *>; +using ObjectModifierPair = std::pair<Object *, ModifierData *>; +using NodeSocketPair = std::pair<bNode *, bNodeSocket *>; + +/** + * Cache common data about node trees from the #Main database that is expensive to retrieve on + * demand every time. + */ +struct NodeTreeRelations { + private: + Main *bmain_; + std::optional<Vector<bNodeTree *>> all_trees_; + std::optional<Map<bNodeTree *, ID *>> owner_ids_; + std::optional<MultiValueMap<bNodeTree *, TreeNodePair>> group_node_users_; + std::optional<MultiValueMap<bNodeTree *, ObjectModifierPair>> modifiers_users_; + + public: + NodeTreeRelations(Main *bmain) : bmain_(bmain) + { + } + + void ensure_all_trees() + { + if (all_trees_.has_value()) { + return; + } + all_trees_.emplace(); + owner_ids_.emplace(); + if (bmain_ == nullptr) { + return; + } + + FOREACH_NODETREE_BEGIN (bmain_, ntree, id) { + all_trees_->append(ntree); + if (&ntree->id != id) { + owner_ids_->add_new(ntree, id); + } + } + FOREACH_NODETREE_END; + } + + void ensure_owner_ids() + { + this->ensure_all_trees(); + } + + void ensure_group_node_users() + { + if (group_node_users_.has_value()) { + return; + } + group_node_users_.emplace(); + if (bmain_ == nullptr) { + return; + } + + this->ensure_all_trees(); + + for (bNodeTree *ntree : *all_trees_) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id == nullptr) { + continue; + } + ID *id = node->id; + if (GS(id->name) == ID_NT) { + bNodeTree *group = (bNodeTree *)id; + group_node_users_->add(group, {ntree, node}); + } + } + } + } + + void ensure_modifier_users() + { + if (modifiers_users_.has_value()) { + return; + } + modifiers_users_.emplace(); + if (bmain_ == nullptr) { + return; + } + + LISTBASE_FOREACH (Object *, object, &bmain_->objects) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group != nullptr) { + modifiers_users_->add(nmd->node_group, {object, md}); + } + } + } + } + } + + Span<ObjectModifierPair> get_modifier_users(bNodeTree *ntree) + { + BLI_assert(modifiers_users_.has_value()); + return modifiers_users_->lookup(ntree); + } + + Span<TreeNodePair> get_group_node_users(bNodeTree *ntree) + { + BLI_assert(group_node_users_.has_value()); + return group_node_users_->lookup(ntree); + } + + ID *get_owner_id(bNodeTree *ntree) + { + BLI_assert(owner_ids_.has_value()); + return owner_ids_->lookup_default(ntree, &ntree->id); + } +}; + +struct TreeUpdateResult { + bool interface_changed = false; + bool output_changed = false; +}; + +class NodeTreeMainUpdater { + private: + Main *bmain_; + NodeTreeUpdateExtraParams *params_; + Map<bNodeTree *, TreeUpdateResult> update_result_by_tree_; + NodeTreeRelations relations_; + + public: + NodeTreeMainUpdater(Main *bmain, NodeTreeUpdateExtraParams *params) + : bmain_(bmain), params_(params), relations_(bmain) + { + } + + void update() + { + Vector<bNodeTree *> changed_ntrees; + FOREACH_NODETREE_BEGIN (bmain_, ntree, id) { + if (ntree->changed_flag != NTREE_CHANGED_NOTHING) { + changed_ntrees.append(ntree); + } + } + FOREACH_NODETREE_END; + this->update_rooted(changed_ntrees); + } + + void update_rooted(Span<bNodeTree *> root_ntrees) + { + if (root_ntrees.is_empty()) { + return; + } + + bool is_single_tree_update = false; + + if (root_ntrees.size() == 1) { + bNodeTree *ntree = root_ntrees[0]; + if (ntree->changed_flag == NTREE_CHANGED_NOTHING) { + return; + } + const TreeUpdateResult result = this->update_tree(*ntree); + update_result_by_tree_.add_new(ntree, result); + if (!result.interface_changed && !result.output_changed) { + is_single_tree_update = true; + } + } + + if (!is_single_tree_update) { + Vector<bNodeTree *> ntrees_in_order = this->get_tree_update_order(root_ntrees); + for (bNodeTree *ntree : ntrees_in_order) { + if (ntree->changed_flag == NTREE_CHANGED_NOTHING) { + continue; + } + if (!update_result_by_tree_.contains(ntree)) { + const TreeUpdateResult result = this->update_tree(*ntree); + update_result_by_tree_.add_new(ntree, result); + } + const TreeUpdateResult result = update_result_by_tree_.lookup(ntree); + Span<TreeNodePair> dependent_trees = relations_.get_group_node_users(ntree); + if (result.output_changed) { + for (const TreeNodePair &pair : dependent_trees) { + add_node_tag(pair.first, pair.second, NTREE_CHANGED_NODE_OUTPUT); + } + } + if (result.interface_changed) { + for (const TreeNodePair &pair : dependent_trees) { + add_node_tag(pair.first, pair.second, NTREE_CHANGED_NODE_PROPERTY); + } + } + } + } + + for (const auto item : update_result_by_tree_.items()) { + bNodeTree *ntree = item.key; + const TreeUpdateResult &result = item.value; + + this->reset_changed_flags(*ntree); + + if (result.interface_changed) { + if (ntree->type == NTREE_GEOMETRY) { + relations_.ensure_modifier_users(); + for (const ObjectModifierPair &pair : relations_.get_modifier_users(ntree)) { + Object *object = pair.first; + ModifierData *md = pair.second; + + if (md->type == eModifierType_Nodes) { + MOD_nodes_update_interface(object, (NodesModifierData *)md); + } + } + } + } + + if (params_) { + relations_.ensure_owner_ids(); + ID *id = relations_.get_owner_id(ntree); + if (params_->tree_changed_fn) { + params_->tree_changed_fn(id, ntree, params_->user_data); + } + if (params_->tree_output_changed_fn && result.output_changed) { + params_->tree_output_changed_fn(id, ntree, params_->user_data); + } + } + } + } + + private: + enum class ToposortMark { + None, + Temporary, + Permanent, + }; + + using ToposortMarkMap = Map<bNodeTree *, ToposortMark>; + + /** + * Finds all trees that depend on the given trees (through node groups). Then those trees are + * ordered such that all trees used by one tree come before it. + */ + Vector<bNodeTree *> get_tree_update_order(Span<bNodeTree *> root_ntrees) + { + relations_.ensure_group_node_users(); + + Set<bNodeTree *> trees_to_update = get_trees_to_update(root_ntrees); + + Vector<bNodeTree *> sorted_ntrees; + + ToposortMarkMap marks; + for (bNodeTree *ntree : trees_to_update) { + marks.add_new(ntree, ToposortMark::None); + } + for (bNodeTree *ntree : trees_to_update) { + if (marks.lookup(ntree) == ToposortMark::None) { + const bool cycle_detected = !this->get_tree_update_order__visit_recursive( + ntree, marks, sorted_ntrees); + /* This should be prevented by higher level operators. */ + BLI_assert(!cycle_detected); + UNUSED_VARS_NDEBUG(cycle_detected); + } + } + + std::reverse(sorted_ntrees.begin(), sorted_ntrees.end()); + + return sorted_ntrees; + } + + bool get_tree_update_order__visit_recursive(bNodeTree *ntree, + ToposortMarkMap &marks, + Vector<bNodeTree *> &sorted_ntrees) + { + ToposortMark &mark = marks.lookup(ntree); + if (mark == ToposortMark::Permanent) { + return true; + } + if (mark == ToposortMark::Temporary) { + /* There is a dependency cycle. */ + return false; + } + + mark = ToposortMark::Temporary; + + for (const TreeNodePair &pair : relations_.get_group_node_users(ntree)) { + this->get_tree_update_order__visit_recursive(pair.first, marks, sorted_ntrees); + } + sorted_ntrees.append(ntree); + + mark = ToposortMark::Permanent; + return true; + } + + Set<bNodeTree *> get_trees_to_update(Span<bNodeTree *> root_ntrees) + { + relations_.ensure_group_node_users(); + + Set<bNodeTree *> reachable_trees; + VectorSet<bNodeTree *> trees_to_check = root_ntrees; + + while (!trees_to_check.is_empty()) { + bNodeTree *ntree = trees_to_check.pop(); + if (reachable_trees.add(ntree)) { + for (const TreeNodePair &pair : relations_.get_group_node_users(ntree)) { + trees_to_check.add(pair.first); + } + } + } + + return reachable_trees; + } + + TreeUpdateResult update_tree(bNodeTree &ntree) + { + TreeUpdateResult result; + + /* Use a #NodeTreeRef to speedup certain queries. It is rebuilt whenever the node tree topology + * changes, which typically happens zero or one times during the entire update of the node + * tree. */ + std::unique_ptr<NodeTreeRef> tree_ref; + this->ensure_tree_ref(ntree, tree_ref); + + this->update_socket_link_and_use(*tree_ref); + this->update_individual_nodes(ntree, tree_ref); + this->update_internal_links(ntree, tree_ref); + this->update_generic_callback(ntree, tree_ref); + this->remove_unused_previews_when_necessary(ntree); + + this->ensure_tree_ref(ntree, tree_ref); + if (ntree.type == NTREE_GEOMETRY) { + if (node_field_inferencing::update_field_inferencing(*tree_ref)) { + result.interface_changed = true; + } + } + + result.output_changed = this->check_if_output_changed(*tree_ref); + + this->update_socket_link_and_use(*tree_ref); + this->update_node_levels(ntree); + this->update_link_validation(ntree); + + if (ntree.type == NTREE_TEXTURE) { + ntreeTexCheckCyclics(&ntree); + } + + if (ntree.changed_flag & NTREE_CHANGED_INTERFACE || ntree.changed_flag & NTREE_CHANGED_ANY) { + result.interface_changed = true; + } + + if (result.interface_changed) { + ntreeInterfaceTypeUpdate(&ntree); + } + + return result; + } + + void ensure_tree_ref(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref) + { + if (!tree_ref) { + tree_ref = std::make_unique<NodeTreeRef>(&ntree); + } + } + + void update_socket_link_and_use(const NodeTreeRef &tree) + { + for (const InputSocketRef *socket : tree.input_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + if (socket->directly_linked_links().is_empty()) { + bsocket->link = nullptr; + } + else { + bsocket->link = socket->directly_linked_links()[0]->blink(); + } + } + + this->update_socket_used_tags(tree); + } + + void update_socket_used_tags(const NodeTreeRef &tree) + { + for (const SocketRef *socket : tree.sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + bsocket->flag &= ~SOCK_IN_USE; + for (const LinkRef *link : socket->directly_linked_links()) { + if (!link->is_muted()) { + bsocket->flag |= SOCK_IN_USE; + break; + } + } + } + } + + void update_individual_nodes(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref) + { + /* Iterate over nodes instead of #NodeTreeRef, because the #tree_ref might be outdated after + * some update functions. */ + LISTBASE_FOREACH (bNode *, bnode, &ntree.nodes) { + this->ensure_tree_ref(ntree, tree_ref); + const NodeRef &node = *tree_ref->find_node(*bnode); + if (this->should_update_individual_node(node)) { + const uint32_t old_changed_flag = ntree.changed_flag; + ntree.changed_flag = NTREE_CHANGED_NOTHING; + + /* This may set #ntree.changed_flag which is detected below. */ + this->update_individual_node(node); + + if (ntree.changed_flag != NTREE_CHANGED_NOTHING) { + /* The tree ref is outdated and needs to be rebuilt. Generally, only very few update + * functions change the node. Typically zero or one nodes change after an update. */ + tree_ref.reset(); + } + ntree.changed_flag |= old_changed_flag; + } + } + } + + bool should_update_individual_node(const NodeRef &node) + { + bNodeTree &ntree = *node.btree(); + bNode &bnode = *node.bnode(); + if (ntree.changed_flag & NTREE_CHANGED_ANY) { + return true; + } + if (bnode.changed_flag & NTREE_CHANGED_NODE_PROPERTY) { + return true; + } + if (ntree.changed_flag & NTREE_CHANGED_LINK) { + /* Node groups currently always rebuilt their sockets when they are updated. + * So avoid calling the update method when no new link was added to it. */ + if (node.is_group_input_node()) { + if (node.outputs().last()->is_directly_linked()) { + return true; + } + } + else if (node.is_group_output_node()) { + if (node.inputs().last()->is_directly_linked()) { + return true; + } + } + else { + /* Currently we have no way to tell if a node needs to be updated when a link changed. */ + return true; + } + } + if (ntree.changed_flag & NTREE_CHANGED_INTERFACE) { + if (node.is_group_input_node() || node.is_group_output_node()) { + return true; + } + } + return false; + } + + void update_individual_node(const NodeRef &node) + { + bNodeTree &ntree = *node.btree(); + bNode &bnode = *node.bnode(); + bNodeType &ntype = *bnode.typeinfo; + if (ntype.group_update_func) { + ntype.group_update_func(&ntree, &bnode); + } + if (ntype.updatefunc) { + ntype.updatefunc(&ntree, &bnode); + } + } + + void update_internal_links(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref) + { + bool any_internal_links_updated = false; + this->ensure_tree_ref(ntree, tree_ref); + for (const NodeRef *node : tree_ref->nodes()) { + if (!this->should_update_individual_node(*node)) { + continue; + } + /* Find all expected internal links. */ + Vector<std::pair<bNodeSocket *, bNodeSocket *>> expected_internal_links; + for (const OutputSocketRef *output_socket : node->outputs()) { + if (!output_socket->is_available()) { + continue; + } + if (!output_socket->is_directly_linked()) { + continue; + } + if (output_socket->bsocket()->flag & SOCK_NO_INTERNAL_LINK) { + continue; + } + const InputSocketRef *input_socket = this->find_internally_linked_input(output_socket); + if (input_socket != nullptr) { + expected_internal_links.append({input_socket->bsocket(), output_socket->bsocket()}); + } + } + /* rebuilt internal links if they have changed. */ + if (node->internal_links().size() != expected_internal_links.size()) { + this->update_internal_links_in_node(ntree, *node->bnode(), expected_internal_links); + any_internal_links_updated = true; + } + else { + for (auto &item : expected_internal_links) { + const bNodeSocket *from_socket = item.first; + const bNodeSocket *to_socket = item.second; + bool found = false; + for (const InternalLinkRef *internal_link : node->internal_links()) { + if (from_socket == internal_link->from().bsocket() && + to_socket == internal_link->to().bsocket()) { + found = true; + } + } + if (!found) { + this->update_internal_links_in_node(ntree, *node->bnode(), expected_internal_links); + any_internal_links_updated = true; + break; + } + } + } + } + + if (any_internal_links_updated) { + tree_ref.reset(); + } + } + + const InputSocketRef *find_internally_linked_input(const OutputSocketRef *output_socket) + { + const InputSocketRef *selected_socket = nullptr; + int selected_priority = -1; + bool selected_is_linked = false; + for (const InputSocketRef *input_socket : output_socket->node().inputs()) { + if (!input_socket->is_available()) { + continue; + } + if (input_socket->bsocket()->flag & SOCK_NO_INTERNAL_LINK) { + continue; + } + const int priority = get_internal_link_type_priority(input_socket->bsocket()->typeinfo, + output_socket->bsocket()->typeinfo); + if (priority < 0) { + continue; + } + const bool is_linked = input_socket->is_directly_linked(); + const bool is_preferred = priority > selected_priority || (is_linked && !selected_is_linked); + if (!is_preferred) { + continue; + } + selected_socket = input_socket; + selected_priority = priority; + selected_is_linked = is_linked; + } + return selected_socket; + } + + void update_internal_links_in_node(bNodeTree &ntree, + bNode &node, + Span<std::pair<bNodeSocket *, bNodeSocket *>> links) + { + BLI_freelistN(&node.internal_links); + for (const auto &item : links) { + bNodeSocket *from_socket = item.first; + bNodeSocket *to_socket = item.second; + bNodeLink *link = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), __func__); + link->fromnode = &node; + link->fromsock = from_socket; + link->tonode = &node; + link->tosock = to_socket; + link->flag |= NODE_LINK_VALID; + BLI_addtail(&node.internal_links, link); + } + BKE_ntree_update_tag_node_internal_link(&ntree, &node); + } + + void update_generic_callback(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref) + { + if (ntree.typeinfo->update == nullptr) { + return; + } + + /* Reset the changed_flag to allow detecting when the update callback changed the node tree. */ + const uint32_t old_changed_flag = ntree.changed_flag; + ntree.changed_flag = NTREE_CHANGED_NOTHING; + + ntree.typeinfo->update(&ntree); + + if (ntree.changed_flag != NTREE_CHANGED_NOTHING) { + /* The tree ref is outdated and needs to be rebuilt. */ + tree_ref.reset(); + } + ntree.changed_flag |= old_changed_flag; + } + + void remove_unused_previews_when_necessary(bNodeTree &ntree) + { + /* Don't trigger preview removal when only those flags are set. */ + const uint32_t allowed_flags = NTREE_CHANGED_LINK | NTREE_CHANGED_SOCKET_PROPERTY | + NTREE_CHANGED_NODE_PROPERTY | NTREE_CHANGED_NODE_OUTPUT | + NTREE_CHANGED_INTERFACE; + if ((ntree.changed_flag & allowed_flags) == ntree.changed_flag) { + return; + } + BKE_node_preview_remove_unused(&ntree); + } + + void update_node_levels(bNodeTree &ntree) + { + ntreeUpdateNodeLevels(&ntree); + } + + void update_link_validation(bNodeTree &ntree) + { + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + link->flag |= NODE_LINK_VALID; + if (link->fromnode && link->tonode && link->fromnode->level <= link->tonode->level) { + link->flag &= ~NODE_LINK_VALID; + } + else if (ntree.typeinfo->validate_link) { + const eNodeSocketDatatype from_type = static_cast<eNodeSocketDatatype>( + link->fromsock->type); + const eNodeSocketDatatype to_type = static_cast<eNodeSocketDatatype>(link->tosock->type); + if (!ntree.typeinfo->validate_link(from_type, to_type)) { + link->flag &= ~NODE_LINK_VALID; + } + } + } + } + + bool check_if_output_changed(const NodeTreeRef &tree) + { + bNodeTree &btree = *tree.btree(); + + /* Compute a hash that represents the node topology connected to the output. This always has to + * be updated even if it is not used to detect changes right now. Otherwise + * #btree.output_topology_hash will go out of date. */ + const Vector<const SocketRef *> tree_output_sockets = this->find_output_sockets(tree); + const uint32_t old_topology_hash = btree.output_topology_hash; + const uint32_t new_topology_hash = this->get_combined_socket_topology_hash( + tree, tree_output_sockets); + btree.output_topology_hash = new_topology_hash; + + if (const AnimData *adt = BKE_animdata_from_id(&btree.id)) { + /* Drivers may copy values in the node tree around arbitrarily and may cause the output to + * change even if it wouldn't without drivers. Only some special drivers like `frame/5` can + * be used without causing updates all the time currently. In the future we could try to + * handle other drivers better as well. + * Note that this optimization only works in practice when the depsgraph didn't also get a + * copy-on-write tag for the node tree (which happens when changing node properties). It does + * work in a few situations like adding reroutes and duplicating nodes though. */ + LISTBASE_FOREACH (const FCurve *, fcurve, &adt->drivers) { + const ChannelDriver *driver = fcurve->driver; + const StringRef expression = driver->expression; + if (expression.startswith("frame")) { + const StringRef remaining_expression = expression.drop_known_prefix("frame"); + if (remaining_expression.find_first_not_of(" */+-0123456789.") == StringRef::not_found) { + continue; + } + } + /* Unrecognized driver, assume that the output always changes. */ + return true; + } + } + + if (btree.changed_flag & NTREE_CHANGED_ANY) { + return true; + } + + if (old_topology_hash != new_topology_hash) { + return true; + } + + /* The topology hash can only be used when only topology-changing operations have been done. */ + if (btree.changed_flag == + (btree.changed_flag & (NTREE_CHANGED_LINK | NTREE_CHANGED_REMOVED_NODE))) { + if (old_topology_hash == new_topology_hash) { + return false; + } + } + + if (!this->check_if_socket_outputs_changed_based_on_flags(tree, tree_output_sockets)) { + return false; + } + + return true; + } + + Vector<const SocketRef *> find_output_sockets(const NodeTreeRef &tree) + { + Vector<const SocketRef *> sockets; + for (const NodeRef *node : tree.nodes()) { + const bNode *bnode = node->bnode(); + if (bnode->typeinfo->nclass != NODE_CLASS_OUTPUT && bnode->type != NODE_GROUP_OUTPUT) { + continue; + } + for (const InputSocketRef *socket : node->inputs()) { + if (socket->idname() != "NodeSocketVirtual") { + sockets.append(socket); + } + } + } + return sockets; + } + + /** + * Computes a hash that changes when the node tree topology connected to an output node changes. + * Adding reroutes does not have an effect on the hash. + */ + uint32_t get_combined_socket_topology_hash(const NodeTreeRef &tree, + Span<const SocketRef *> sockets) + { + Array<uint32_t> hashes = this->get_socket_topology_hashes(tree, sockets); + uint32_t combined_hash = 0; + for (uint32_t hash : hashes) { + combined_hash = noise::hash(combined_hash, hash); + } + return combined_hash; + } + + Array<uint32_t> get_socket_topology_hashes(const NodeTreeRef &tree, + Span<const SocketRef *> sockets) + { + Array<std::optional<uint32_t>> hash_by_socket_id(tree.sockets().size()); + Stack<const SocketRef *> sockets_to_check = sockets; + + while (!sockets_to_check.is_empty()) { + const SocketRef &in_out_socket = *sockets_to_check.peek(); + const NodeRef &node = in_out_socket.node(); + + if (hash_by_socket_id[in_out_socket.id()].has_value()) { + sockets_to_check.pop(); + /* Socket is handled already. */ + continue; + } + + if (in_out_socket.is_input()) { + /* For input sockets, first compute the hashes of all linked sockets. */ + const InputSocketRef &socket = in_out_socket.as_input(); + bool all_origins_computed = true; + for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) { + if (!hash_by_socket_id[origin_socket->id()].has_value()) { + sockets_to_check.push(origin_socket); + all_origins_computed = false; + } + } + if (!all_origins_computed) { + continue; + } + /* When the hashes for the linked sockets are ready, combine them into a hash for the input + * socket. */ + const uint64_t socket_ptr = (uintptr_t)socket.bsocket(); + uint32_t socket_hash = noise::hash(socket_ptr, socket_ptr >> 32); + for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) { + const uint32_t origin_socket_hash = *hash_by_socket_id[origin_socket->id()]; + socket_hash = noise::hash(socket_hash, origin_socket_hash); + } + hash_by_socket_id[socket.id()] = socket_hash; + sockets_to_check.pop(); + } + else { + /* For output sockets, first compute the hashes of all available input sockets. */ + const OutputSocketRef &socket = in_out_socket.as_output(); + bool all_available_inputs_computed = true; + for (const InputSocketRef *input_socket : node.inputs()) { + if (input_socket->is_available()) { + if (!hash_by_socket_id[input_socket->id()].has_value()) { + sockets_to_check.push(input_socket); + all_available_inputs_computed = false; + } + } + } + if (!all_available_inputs_computed) { + continue; + } + /* When all input socket hashes have been computed, combine them into a hash for the output + * socket. */ + const uint64_t socket_ptr = (uintptr_t)socket.bsocket(); + uint32_t socket_hash = noise::hash(socket_ptr, socket_ptr >> 32); + for (const InputSocketRef *input_socket : node.inputs()) { + if (input_socket->is_available()) { + const uint32_t input_socket_hash = *hash_by_socket_id[input_socket->id()]; + socket_hash = noise::hash(socket_hash, input_socket_hash); + } + } + hash_by_socket_id[socket.id()] = socket_hash; + sockets_to_check.pop(); + } + } + + /* Create output array. */ + Array<uint32_t> hashes(sockets.size()); + for (const int i : sockets.index_range()) { + hashes[i] = *hash_by_socket_id[sockets[i]->id()]; + } + return hashes; + } + + /** + * Returns true when any of the provided sockets changed its values. A change is detected by + * checking the #changed_flag on connected sockets and nodes. + */ + bool check_if_socket_outputs_changed_based_on_flags(const NodeTreeRef &tree, + Span<const SocketRef *> sockets) + { + /* Avoid visiting the same socket twice when multiple links point to the same socket. */ + Array<bool> pushed_by_socket_id(tree.sockets().size(), false); + Stack<const SocketRef *> sockets_to_check = sockets; + + for (const SocketRef *socket : sockets) { + pushed_by_socket_id[socket->id()] = true; + } + + while (!sockets_to_check.is_empty()) { + const SocketRef &in_out_socket = *sockets_to_check.pop(); + const bNode &bnode = *in_out_socket.node().bnode(); + const bNodeSocket &bsocket = *in_out_socket.bsocket(); + if (bsocket.changed_flag != NTREE_CHANGED_NOTHING) { + return true; + } + if (bnode.changed_flag != NTREE_CHANGED_NOTHING) { + const bool only_unused_internal_link_changed = (bnode.flag & NODE_MUTED) == 0 && + bnode.changed_flag == + NTREE_CHANGED_INTERNAL_LINK; + if (!only_unused_internal_link_changed) { + return true; + } + } + if (in_out_socket.is_input()) { + const InputSocketRef &socket = in_out_socket.as_input(); + for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) { + bool &pushed = pushed_by_socket_id[origin_socket->id()]; + if (!pushed) { + sockets_to_check.push(origin_socket); + pushed = true; + } + } + } + else { + const OutputSocketRef &socket = in_out_socket.as_output(); + for (const InputSocketRef *input_socket : socket.node().inputs()) { + if (input_socket->is_available()) { + bool &pushed = pushed_by_socket_id[input_socket->id()]; + if (!pushed) { + sockets_to_check.push(input_socket); + pushed = true; + } + } + } + } + } + return false; + } + + void reset_changed_flags(bNodeTree &ntree) + { + ntree.changed_flag = NTREE_CHANGED_NOTHING; + LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { + node->changed_flag = NTREE_CHANGED_NOTHING; + node->update = 0; + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + socket->changed_flag = NTREE_CHANGED_NOTHING; + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + socket->changed_flag = NTREE_CHANGED_NOTHING; + } + } + } +}; + +} // namespace blender::bke + +void BKE_ntree_update_tag_all(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_ANY); +} + +void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node) +{ + add_node_tag(ntree, node, NTREE_CHANGED_NODE_PROPERTY); +} + +void BKE_ntree_update_tag_node_new(bNodeTree *ntree, bNode *node) +{ + add_node_tag(ntree, node, NTREE_CHANGED_NODE_PROPERTY); +} + +void BKE_ntree_update_tag_socket_property(bNodeTree *ntree, bNodeSocket *socket) +{ + add_socket_tag(ntree, socket, NTREE_CHANGED_SOCKET_PROPERTY); +} + +void BKE_ntree_update_tag_socket_new(bNodeTree *ntree, bNodeSocket *socket) +{ + add_socket_tag(ntree, socket, NTREE_CHANGED_SOCKET_PROPERTY); +} + +void BKE_ntree_update_tag_socket_removed(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_REMOVED_SOCKET); +} + +void BKE_ntree_update_tag_socket_type(bNodeTree *ntree, bNodeSocket *socket) +{ + add_socket_tag(ntree, socket, NTREE_CHANGED_SOCKET_PROPERTY); +} + +void BKE_ntree_update_tag_socket_availability(bNodeTree *ntree, bNodeSocket *socket) +{ + add_socket_tag(ntree, socket, NTREE_CHANGED_SOCKET_PROPERTY); +} + +void BKE_ntree_update_tag_node_removed(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_REMOVED_NODE); +} + +void BKE_ntree_update_tag_node_internal_link(bNodeTree *ntree, bNode *node) +{ + add_node_tag(ntree, node, NTREE_CHANGED_INTERNAL_LINK); +} + +void BKE_ntree_update_tag_link_changed(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_LINK); +} + +void BKE_ntree_update_tag_link_removed(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_LINK); +} + +void BKE_ntree_update_tag_link_added(bNodeTree *ntree, bNodeLink *UNUSED(link)) +{ + add_tree_tag(ntree, NTREE_CHANGED_LINK); +} + +void BKE_ntree_update_tag_link_mute(bNodeTree *ntree, bNodeLink *UNUSED(link)) +{ + add_tree_tag(ntree, NTREE_CHANGED_LINK); +} + +void BKE_ntree_update_tag_missing_runtime_data(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_ALL); +} + +void BKE_ntree_update_tag_interface(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_INTERFACE); +} + +void BKE_ntree_update_tag_id_changed(Main *bmain, ID *id) +{ + FOREACH_NODETREE_BEGIN (bmain, ntree, ntree_id) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id == id) { + node->update |= NODE_UPDATE_ID; + add_node_tag(ntree, node, NTREE_CHANGED_NODE_PROPERTY); + } + } + } + FOREACH_NODETREE_END; +} + +/** + * Protect from recursive calls into the updating function. Some node update functions might + * trigger this from Python or in other cases. + * + * This could be added to #Main, but given that there is generally only one #Main, that's not + * really worth it now. + */ +static bool is_updating = false; + +void BKE_ntree_update_main(Main *bmain, NodeTreeUpdateExtraParams *params) +{ + if (is_updating) { + return; + } + + is_updating = true; + blender::bke::NodeTreeMainUpdater updater{bmain, params}; + updater.update(); + is_updating = false; +} + +void BKE_ntree_update_main_tree(Main *bmain, bNodeTree *ntree, NodeTreeUpdateExtraParams *params) +{ + if (ntree == nullptr) { + BKE_ntree_update_main(bmain, params); + return; + } + + if (is_updating) { + return; + } + + is_updating = true; + blender::bke::NodeTreeMainUpdater updater{bmain, params}; + updater.update_rooted({ntree}); + is_updating = false; +} diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 54e673b51eb..8bcb99e41e4 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -70,6 +70,7 @@ #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_multires.h" +#include "BKE_node_tree_update.h" #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_screen.h" @@ -590,7 +591,7 @@ static bNodeSocket *do_versions_node_group_add_socket_2_56_2(bNodeTree *ngroup, BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs, gsock); - ngroup->update |= (in_out == SOCK_IN ? NTREE_UPDATE_GROUP_IN : NTREE_UPDATE_GROUP_OUT); + BKE_ntree_update_tag_interface(ngroup); return gsock; } @@ -2019,7 +2020,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) link->fromsock = gsock; link->tonode = node; link->tosock = sock; - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_added(ntree, link); sock->link = link; } @@ -2042,7 +2043,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) link->fromsock = sock; link->tonode = NULL; link->tosock = gsock; - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_added(ntree, link); gsock->link = link; } @@ -2282,7 +2283,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) do_versions_socket_default_value_259(sock); } - ntree->update |= NTREE_UPDATE; + BKE_ntree_update_tag_all(ntree); } FOREACH_NODETREE_END; } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 4333bdb851c..d9052c6b1f7 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -88,6 +88,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_paint.h" #include "BKE_pointcache.h" #include "BKE_report.h" @@ -896,7 +897,7 @@ static void do_versions_material_convert_legacy_blend_mode(bNodeTree *ntree, cha } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 6b22d2f97e9..5eb9b0a4c6a 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -593,7 +593,7 @@ static bNodeTree *add_realize_node_tree(Main *bmain) nodeSetSelected(node, false); } - ntreeUpdateTree(bmain, node_tree); + version_socket_update_is_used(node_tree); return node_tree; } @@ -2441,7 +2441,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) data->data_type = SOCK_FLOAT; data->operation = node->custom1; strcpy(node->idname, "FunctionNodeCompare"); - node->update = NODE_UPDATE; node->storage = data; } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 3deaaa040d6..752157bffde 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -223,3 +223,23 @@ void version_node_socket_index_animdata(Main *bmain, FOREACH_NODETREE_END; } } + +/** + * The versioning code generally expects `SOCK_IN_USE` to be set correctly. This function updates + * the flag on all sockets after changes to the node tree. + */ +void version_socket_update_is_used(bNodeTree *ntree) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + socket->flag &= ~SOCK_IN_USE; + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + socket->flag &= ~SOCK_IN_USE; + } + } + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + link->fromsock->flag |= SOCK_IN_USE; + link->tosock->flag |= SOCK_IN_USE; + } +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index 0613484b754..dd1dd1f22f2 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -100,6 +100,8 @@ struct bNodeSocket *version_node_add_socket_if_not_exist(struct bNodeTree *ntree const char *identifier, const char *name); +void version_socket_update_is_used(bNodeTree *ntree); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index afe2e1067ae..f1a2e678b2e 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -48,6 +48,7 @@ #include "BLO_readfile.h" #include "readfile.h" +#include "versioning_common.h" static bool socket_is_used(bNodeSocket *sock) { @@ -170,7 +171,7 @@ static void displacement_node_insert(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -243,7 +244,7 @@ static void square_roughness_node_insert(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -318,7 +319,7 @@ static void ambient_occlusion_node_relink(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -466,7 +467,7 @@ static void update_math_node_single_operand_operators(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -528,7 +529,7 @@ static void update_vector_math_node_add_and_subtract_operators(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -566,7 +567,7 @@ static void update_vector_math_node_dot_product_operator(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -631,7 +632,7 @@ static void update_vector_math_node_cross_product_operator(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -683,7 +684,7 @@ static void update_vector_math_node_normalize_operator(bNodeTree *ntree) } } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -785,7 +786,7 @@ static void update_vector_math_node_average_operator(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -974,7 +975,7 @@ static void update_mapping_node_inputs_and_properties(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -1149,7 +1150,7 @@ static void update_voronoi_node_crackle(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -1191,7 +1192,7 @@ static void update_voronoi_node_coloring(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -1234,7 +1235,7 @@ static void update_voronoi_node_square_distance(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -1279,7 +1280,7 @@ static void update_noise_and_wave_distortion(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index a5c44ea711b..234951eac9a 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -61,6 +61,7 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_paint.h" #include "BKE_screen.h" #include "BKE_workspace.h" @@ -583,11 +584,11 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) bNodeSocketValueFloat *roughness_data = roughness_socket->default_value; roughness_data->value = 0.4f; node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; - nodeUpdate(ma->nodetree, node); + BKE_ntree_update_tag_node_property(ma->nodetree, node); } else if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; - nodeUpdate(ma->nodetree, node); + BKE_ntree_update_tag_node_property(ma->nodetree, node); } } } diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 94cba833096..8a69dbf3312 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -153,9 +153,9 @@ void DEG_add_collection_geometry_customdata_mask(struct DepsNodeHandle *node_han void DEG_add_simulation_relation(struct DepsNodeHandle *node_handle, struct Simulation *simulation, const char *description); -void DEG_add_node_tree_relation(struct DepsNodeHandle *node_handle, - struct bNodeTree *node_tree, - const char *description); +void DEG_add_node_tree_output_relation(struct DepsNodeHandle *node_handle, + struct bNodeTree *node_tree, + const char *description); void DEG_add_bone_relation(struct DepsNodeHandle *handle, struct Object *object, const char *bone_name, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 51582508b6f..16eacc735d4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1710,8 +1710,8 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) build_idproperties(ntree->id.properties); /* Animation, */ build_animdata(&ntree->id); - /* Shading update. */ - add_operation_node(&ntree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); + /* Output update. */ + add_operation_node(&ntree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index b195b2d9e11..09263718677 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1770,7 +1770,7 @@ void DepsgraphRelationBuilder::build_world(World *world) if (world->nodetree != nullptr) { build_nodetree(world->nodetree); OperationKey ntree_key( - &world->nodetree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); + &world->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); add_relation(ntree_key, world_key, "World's NTree"); build_nested_nodetree(&world->id, world->nodetree); } @@ -2381,17 +2381,17 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) ComponentKey lamp_parameters_key(&lamp->id, NodeType::PARAMETERS); + /* For allowing drivers on lamp properties. */ + ComponentKey shading_key(&lamp->id, NodeType::SHADING); + add_relation(lamp_parameters_key, shading_key, "Light Shading Parameters"); + /* light's nodetree */ if (lamp->nodetree != nullptr) { build_nodetree(lamp->nodetree); - ComponentKey nodetree_key(&lamp->nodetree->id, NodeType::SHADING); - add_relation(nodetree_key, lamp_parameters_key, "NTree->Light Parameters"); + ComponentKey nodetree_key(&lamp->nodetree->id, NodeType::NTREE_OUTPUT); + add_relation(nodetree_key, shading_key, "NTree->Light Parameters"); build_nested_nodetree(&lamp->id, lamp->nodetree); } - - /* For allowing drivers on lamp properties. */ - ComponentKey shading_key(&lamp->id, NodeType::SHADING); - add_relation(lamp_parameters_key, shading_key, "Light Shading Parameters"); } void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket) @@ -2441,7 +2441,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) build_idproperties(ntree->id.properties); build_animdata(&ntree->id); build_parameters(&ntree->id); - ComponentKey shading_key(&ntree->id, NodeType::SHADING); + OperationKey ntree_output_key(&ntree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); @@ -2460,25 +2460,25 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) if (id_type == ID_MA) { build_material((Material *)bnode->id); ComponentKey material_key(id, NodeType::SHADING); - add_relation(material_key, shading_key, "Material -> Node"); + add_relation(material_key, ntree_output_key, "Material -> Node"); } else if (id_type == ID_TE) { build_texture((Tex *)bnode->id); ComponentKey texture_key(id, NodeType::GENERIC_DATABLOCK); - add_relation(texture_key, shading_key, "Texture -> Node"); + add_relation(texture_key, ntree_output_key, "Texture -> Node"); } else if (id_type == ID_IM) { build_image((Image *)bnode->id); ComponentKey image_key(id, NodeType::GENERIC_DATABLOCK); - add_relation(image_key, shading_key, "Image -> Node"); + add_relation(image_key, ntree_output_key, "Image -> Node"); } else if (id_type == ID_OB) { build_object((Object *)id); ComponentKey object_transform_key(id, NodeType::TRANSFORM); - add_relation(object_transform_key, shading_key, "Object Transform -> Node"); + add_relation(object_transform_key, ntree_output_key, "Object Transform -> Node"); if (object_have_geometry_component(reinterpret_cast<Object *>(id))) { ComponentKey object_geometry_key(id, NodeType::GEOMETRY); - add_relation(object_geometry_key, shading_key, "Object Geometry -> Node"); + add_relation(object_geometry_key, ntree_output_key, "Object Geometry -> Node"); } } else if (id_type == ID_SCE) { @@ -2498,23 +2498,28 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) else if (id_type == ID_MSK) { build_mask((Mask *)id); OperationKey mask_key(id, NodeType::PARAMETERS, OperationCode::MASK_EVAL); - add_relation(mask_key, shading_key, "Mask -> Node"); + add_relation(mask_key, ntree_output_key, "Mask -> Node"); } else if (id_type == ID_MC) { build_movieclip((MovieClip *)id); OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL); - add_relation(clip_key, shading_key, "Clip -> Node"); + add_relation(clip_key, ntree_output_key, "Clip -> Node"); } else if (id_type == ID_VF) { build_vfont((VFont *)id); ComponentKey vfont_key(id, NodeType::GENERIC_DATABLOCK); - add_relation(vfont_key, shading_key, "VFont -> Node"); + add_relation(vfont_key, ntree_output_key, "VFont -> Node"); } else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { bNodeTree *group_ntree = (bNodeTree *)id; build_nodetree(group_ntree); - ComponentKey group_shading_key(&group_ntree->id, NodeType::SHADING); - add_relation(group_shading_key, shading_key, "Group Node"); + ComponentKey group_output_key(&group_ntree->id, NodeType::NTREE_OUTPUT); + /* The output of the current tree does not necessarily change when the output of the group + * changed. The parent node group is currently explicitly tagged for update in + * #ED_node_tree_propagate_change. In the future we could move this relation to the + * depsgraph, but then the depsgraph has to do some more static analysis of the node tree to + * see which groups the output actually depends on. */ + add_relation(group_output_key, ntree_output_key, "Group Node", RELATION_FLAG_NO_FLUSH); } else { BLI_assert_msg(0, "Unknown ID type used for node"); @@ -2528,14 +2533,10 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) build_idproperties(socket->prop); } - OperationKey shading_update_key(&ntree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); - if (check_id_has_anim_component(&ntree->id)) { ComponentKey animation_key(&ntree->id, NodeType::ANIMATION); - add_relation(animation_key, shading_update_key, "NTree Shading Parameters"); + add_relation(animation_key, ntree_output_key, "NTree Shading Parameters"); } - ComponentKey parameters_key(&ntree->id, NodeType::PARAMETERS); - add_relation(parameters_key, shading_update_key, "NTree Shading Parameters"); } /* Recursively build graph for material */ @@ -2558,7 +2559,7 @@ void DepsgraphRelationBuilder::build_material(Material *material) if (material->nodetree != nullptr) { build_nodetree(material->nodetree); OperationKey ntree_key( - &material->nodetree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); + &material->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); add_relation(ntree_key, material_key, "Material's NTree"); build_nested_nodetree(&material->id, material->nodetree); } @@ -2587,8 +2588,13 @@ void DepsgraphRelationBuilder::build_texture(Tex *texture) build_parameters(&texture->id); /* texture's nodetree */ - build_nodetree(texture->nodetree); - build_nested_nodetree(&texture->id, texture->nodetree); + if (texture->nodetree) { + build_nodetree(texture->nodetree); + OperationKey ntree_key( + &texture->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); + add_relation(ntree_key, texture_key, "Texture's NTree"); + build_nested_nodetree(&texture->id, texture->nodetree); + } /* Special cases for different IDs which texture uses. */ if (texture->type == TEX_IMAGE) { @@ -2875,12 +2881,16 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations() * * This is similar to what happens in ntree_hack_remap_pointers(). */ -void DepsgraphRelationBuilder::build_nested_datablock(ID *owner, ID *id) +void DepsgraphRelationBuilder::build_nested_datablock(ID *owner, ID *id, bool flush_cow_changes) { + int relation_flag = 0; + if (!flush_cow_changes) { + relation_flag |= RELATION_FLAG_NO_FLUSH; + } OperationKey owner_copy_on_write_key( owner, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); OperationKey id_copy_on_write_key(id, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); - add_relation(id_copy_on_write_key, owner_copy_on_write_key, "Eval Order"); + add_relation(id_copy_on_write_key, owner_copy_on_write_key, "Eval Order", relation_flag); } void DepsgraphRelationBuilder::build_nested_nodetree(ID *owner, bNodeTree *ntree) @@ -2888,7 +2898,10 @@ void DepsgraphRelationBuilder::build_nested_nodetree(ID *owner, bNodeTree *ntree if (ntree == nullptr) { return; } - build_nested_datablock(owner, &ntree->id); + /* Don't flush cow changes, because the node tree may change in ways that do not affect the + * owner data block (e.g. when a node is deleted that is not connected to any output). + * Data blocks owning node trees should add a relation to the `NTREE_OUTPUT` node instead. */ + build_nested_datablock(owner, &ntree->id, false); } void DepsgraphRelationBuilder::build_nested_shapekey(ID *owner, Key *key) @@ -2896,7 +2909,7 @@ void DepsgraphRelationBuilder::build_nested_shapekey(ID *owner, Key *key) if (key == nullptr) { return; } - build_nested_datablock(owner, &key->id); + build_nested_datablock(owner, &key->id, true); } void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 09003de3ce4..787e3799029 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -299,7 +299,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer); virtual void build_vfont(VFont *vfont); - virtual void build_nested_datablock(ID *owner, ID *id); + virtual void build_nested_datablock(ID *owner, ID *id, bool flush_cow_changes); virtual void build_nested_nodetree(ID *owner, bNodeTree *ntree); virtual void build_nested_shapekey(ID *owner, Key *key); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 40e59875832..43d24125dc2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -351,7 +351,7 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, return node_identifier; } else if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) { - node_identifier.type = NodeType::SHADING; + node_identifier.type = NodeType::NTREE_OUTPUT; return node_identifier; } else if (RNA_struct_is_a(ptr->type, &RNA_ShaderNode)) { diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc index 7be661d9668..36120ae76d1 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc @@ -425,6 +425,7 @@ static void deg_debug_graphviz_node(DotExportContext &ctx, case NodeType::ARMATURE: case NodeType::GENERIC_DATABLOCK: case NodeType::VISIBILITY: + case NodeType::NTREE_OUTPUT: case NodeType::SIMULATION: { ComponentNode *comp_node = (ComponentNode *)node; if (comp_node->operations.is_empty()) { diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index db00c595383..acb8fa8fe88 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -148,15 +148,15 @@ void DEG_add_simulation_relation(DepsNodeHandle *node_handle, deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description); } -void DEG_add_node_tree_relation(DepsNodeHandle *node_handle, - bNodeTree *node_tree, - const char *description) +void DEG_add_node_tree_output_relation(DepsNodeHandle *node_handle, + bNodeTree *node_tree, + const char *description) { - /* Using shading key, because that's the one that exists right now. Should use something else in - * the future. */ - deg::ComponentKey shading_key(&node_tree->id, deg::NodeType::SHADING); + deg::OperationKey ntree_output_key( + &node_tree->id, deg::NodeType::NTREE_OUTPUT, deg::OperationCode::NTREE_OUTPUT); deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle); - deg_node_handle->builder->add_node_handle_relation(shading_key, deg_node_handle, description); + deg_node_handle->builder->add_node_handle_relation( + ntree_output_key, deg_node_handle, description); } void DEG_add_object_cache_relation(DepsNodeHandle *node_handle, diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index 7af3d03d478..c84adbcde00 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -334,10 +334,13 @@ static void DEG_iterator_ids_step(BLI_Iterator *iter, deg::IDNode *id_node, bool return; } if (only_updated && !(id_cow->recalc & ID_RECALC_ALL)) { - bNodeTree *ntree = ntreeFromID(id_cow); - /* Node-tree is considered part of the data-block. */ - if (!(ntree && (ntree->id.recalc & ID_RECALC_ALL))) { + bNodeTree *ntree = ntreeFromID(id_cow); + if (ntree == nullptr) { + iter->skip = true; + return; + } + if ((ntree->id.recalc & ID_RECALC_NTREE_OUTPUT) == 0) { iter->skip = true; return; } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 2b0d0e6e780..4bc9e0d2d14 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -232,6 +232,10 @@ void depsgraph_tag_to_component_opcode(const ID *id, break; case ID_RECALC_TAG_FOR_UNDO: break; /* Must be ignored by depsgraph. */ + case ID_RECALC_NTREE_OUTPUT: + *component_type = NodeType::NTREE_OUTPUT; + *operation_code = OperationCode::NTREE_OUTPUT; + break; } } @@ -749,6 +753,8 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "ALL"; case ID_RECALC_TAG_FOR_UNDO: return "TAG_FOR_UNDO"; + case ID_RECALC_NTREE_OUTPUT: + return "ID_RECALC_NTREE_OUTPUT"; } return nullptr; } diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc index 8bc03d8b736..075bfd35ec1 100644 --- a/source/blender/depsgraph/intern/node/deg_node.cc +++ b/source/blender/depsgraph/intern/node/deg_node.cc @@ -116,6 +116,8 @@ const char *nodeTypeAsString(NodeType type) return "VISIBILITY"; case NodeType::SIMULATION: return "SIMULATION"; + case NodeType::NTREE_OUTPUT: + return "NTREE_OUTPUT"; /* Total number of meaningful node types. */ case NodeType::NUM_TYPES: @@ -174,6 +176,7 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type) case NodeType::CACHE: case NodeType::PROXY: case NodeType::SIMULATION: + case NodeType::NTREE_OUTPUT: return DEG_SCENE_COMP_PARAMETERS; case NodeType::VISIBILITY: @@ -251,6 +254,7 @@ eDepsObjectComponentType nodeTypeToObjectComponent(NodeType type) case NodeType::DUPLI: case NodeType::SYNCHRONIZATION: case NodeType::SIMULATION: + case NodeType::NTREE_OUTPUT: case NodeType::UNDEFINED: case NodeType::NUM_TYPES: return DEG_OB_COMP_PARAMETERS; diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h index 7e093ab8765..ef58a35afb2 100644 --- a/source/blender/depsgraph/intern/node/deg_node.h +++ b/source/blender/depsgraph/intern/node/deg_node.h @@ -147,6 +147,8 @@ enum class NodeType { SYNCHRONIZATION, /* Simulation component. */ SIMULATION, + /* Node tree output component. */ + NTREE_OUTPUT, /* Total number of meaningful node types. */ NUM_TYPES, diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index b716877902c..4c430904e44 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -351,6 +351,7 @@ DEG_COMPONENT_NODE_DEFINE(Armature, ARMATURE, 0); DEG_COMPONENT_NODE_DEFINE(GenericDatablock, GENERIC_DATABLOCK, 0); DEG_COMPONENT_NODE_DEFINE(Visibility, VISIBILITY, 0); DEG_COMPONENT_NODE_DEFINE(Simulation, SIMULATION, 0); +DEG_COMPONENT_NODE_DEFINE(NTreeOutput, NTREE_OUTPUT, ID_RECALC_NTREE_OUTPUT); /** \} */ @@ -385,6 +386,7 @@ void deg_register_component_depsnodes() register_node_typeinfo(&DNTI_GENERIC_DATABLOCK); register_node_typeinfo(&DNTI_VISIBILITY); register_node_typeinfo(&DNTI_SIMULATION); + register_node_typeinfo(&DNTI_NTREE_OUTPUT); } /** \} */ diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index 9f108af8012..8fd28dfc497 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -222,6 +222,7 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature); DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock); DEG_COMPONENT_NODE_DECLARE_NO_COW(Visibility); DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation); +DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput); /* Bone Component */ struct BoneComponentNode : public ComponentNode { diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index eaae5d2d5dc..2d0d6854851 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -182,6 +182,9 @@ const char *operationCodeAsString(OperationCode opcode) return "LIGHT_UPDATE"; case OperationCode::WORLD_UPDATE: return "WORLD_UPDATE"; + /* Node Tree. */ + case OperationCode::NTREE_OUTPUT: + return "NTREE_OUTPUT"; /* Movie clip. */ case OperationCode::MOVIECLIP_EVAL: return "MOVIECLIP_EVAL"; diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index 31cbb9702ba..4abdc014946 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -177,6 +177,9 @@ enum class OperationCode { LIGHT_UPDATE, WORLD_UPDATE, + /* Node Tree. ----------------------------------------------------------- */ + NTREE_OUTPUT, + /* Batch caches. -------------------------------------------------------- */ GEOMETRY_SELECT_UPDATE, diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 5bac452c7c9..db1ba80dd3c 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -100,7 +100,7 @@ void ED_node_socket_draw(struct bNodeSocket *sock, float scale); void ED_node_tree_update(const struct bContext *C); void ED_node_tag_update_id(struct ID *id); -void ED_node_tag_update_nodetree(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node); + /** * Sort nodes by selection: unselected nodes first, then selected, * then the active node at the very end. Relative order is kept intact. @@ -152,6 +152,26 @@ void ED_node_set_active(struct Main *bmain, bool *r_active_texture_changed); /** + * Call after one or more node trees have been changed and tagged accordingly. + * + * This function will make sure that other parts of Blender update accordingly. For example, if the + * node group interface changed, parent node groups have to be updated as well. + * + * Additionally, this will send notifiers and tag the depsgraph based on the changes. Depsgraph + * relation updates have to be triggered by the caller. + * + * \param C: Context if available. This can be null. + * \param bmain: Main whose data-blocks should be updated based on the changes. + * \param ntree: Under some circumstances the caller knows that only one node tree has + * changed since the last update. In this case the function may be able to skip scanning #bmain + * for other things that have to be changed. It may still scan #bmain if the interface of the + * node tree has changed. + */ +void ED_node_tree_propagate_change(const struct bContext *C, + struct Main *bmain, + struct bNodeTree *ntree); + +/** * \param scene_owner: is the owner of the job, * we don't use it for anything else currently so could also be a void pointer, * but for now keep it an 'Scene' for consistency. diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 29d829dc131..1a9b72c1fab 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -50,6 +50,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_object.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -725,7 +726,8 @@ static void render_endjob(void *rjv) rj->scene->r.scemode &= ~R_NO_FRAME_UPDATE; if (rj->single_layer) { - nodeUpdateID(rj->scene->nodetree, &rj->scene->id); + BKE_ntree_update_tag_id_changed(rj->main, &rj->scene->id); + BKE_ntree_update_main(rj->main, NULL); WM_main_add_notifier(NC_NODE | NA_EDITED, rj->scene); } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 7df5848e068..8a5d75d5f77 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -6623,7 +6623,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) } } - ntreeUpdateTree(CTX_data_main(C), ntree); + ED_node_tree_propagate_change(C, bmain, ntree); /* In case we added more than one node, position them too. */ nodePositionPropagate(out_node); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index bbfd886ce56..90fd6e7d657 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -36,6 +36,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_scene.h" #include "BKE_tracking.h" @@ -1558,7 +1559,8 @@ static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), Poin { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } static void node_socket_template_properties_update(bNodeType *ntype, bNodeSocketTemplate *stemp) diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index e1ba36e81c0..a2dd32b7cc6 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -29,6 +29,8 @@ #include "WM_api.h" +#include "ED_node.h" + #include "node_intern.hh" using blender::nodes::SocketLinkOperation; @@ -77,7 +79,7 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) bNode &group_input = params.add_node("NodeGroupInput"); /* This is necessary to create the new sockets in the other input nodes. */ - ntreeUpdateTree(CTX_data_main(¶ms.C), ¶ms.node_tree); + ED_node_tree_propagate_change(¶ms.C, CTX_data_main(¶ms.C), ¶ms.node_tree); /* Hide the new input in all other group input nodes, to avoid making them taller. */ LISTBASE_FOREACH (bNode *, node, ¶ms.node_tree.nodes) { @@ -203,9 +205,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2) /* Ideally it would be possible to tag the node tree in some way so it updates only after the * translate operation is finished, but normally moving nodes around doesn't cause updates. */ - ntreeUpdateTree(&bmain, snode.edittree); - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, &bmain, snode.edittree); /* Start translation operator with the new node. */ wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_translate", true); @@ -288,4 +288,4 @@ void invoke_node_link_drag_add_menu(bContext &C, UI_popup_block_invoke_ex(&C, create_search_popup_block, storage, nullptr, false); } -} // namespace blender::ed::space_node
\ No newline at end of file +} // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index c6a5e8e68c0..0bb090f9a5f 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -36,6 +36,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_texture.h" @@ -83,15 +84,8 @@ bNode *node_add_node(const bContext &C, const char *idname, int type, float locx nodeSetSelected(node, true); - ntreeUpdateTree(&bmain, snode.edittree); ED_node_set_active(&bmain, &snode, snode.edittree, node, nullptr); - - snode_update(snode, node); - - if (snode.nodetree->type == NTREE_TEXTURE) { - ntreeTexCheckCyclics(snode.edittree); - } - + BKE_ntree_update_main_tree(&bmain, snode.edittree, nullptr); return node; } @@ -281,10 +275,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) BLI_freelistN(&input_links); /* always last */ - ntreeUpdateTree(CTX_data_main(C), &ntree); - snode_notify(*C, snode); - snode_dag_update(*C, snode); - + ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); return OPERATOR_FINISHED; } @@ -385,12 +376,7 @@ static int node_add_group_exec(bContext *C, wmOperator *op) id_us_plus(group_node->id); nodeSetActive(ntree, group_node); - ntreeUpdateTree(bmain, node_group); - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); - + ED_node_tree_propagate_change(C, bmain, nullptr); return OPERATOR_FINISHED; } @@ -479,12 +465,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op) id_us_plus(&object->id); nodeSetActive(ntree, object_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); - - ED_node_tag_update_nodetree(bmain, ntree, object_node); + ED_node_tree_propagate_change(C, bmain, ntree); DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; @@ -587,14 +568,9 @@ static int node_add_texture_exec(bContext *C, wmOperator *op) id_us_plus(&texture->id); nodeSetActive(ntree, texture_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, ntree); DEG_relations_tag_update(bmain); - ED_node_tag_update_nodetree(bmain, ntree, texture_node); - return OPERATOR_FINISHED; } @@ -701,14 +677,9 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) id_us_plus(&collection->id); nodeSetActive(ntree, collection_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, bmain, ntree); DEG_relations_tag_update(bmain); - ED_node_tag_update_nodetree(bmain, ntree, collection_node); - return OPERATOR_FINISHED; } @@ -834,8 +805,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); } - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, bmain, snode.edittree); DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; @@ -937,8 +907,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) node->id = mask; id_us_plus(mask); - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, bmain, snode.edittree); DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 1ce1032cf97..635ef41d859 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -176,35 +176,6 @@ void ED_node_tag_update_id(ID *id) } } -void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree, bNode *node) -{ - if (!ntree) { - return; - } - - bool do_tag_update = true; - if (node != nullptr) { - if (!node_connected_to_output(*bmain, *ntree, *node)) { - do_tag_update = false; - } - } - - /* Look through all datablocks to support groups. */ - if (do_tag_update) { - FOREACH_NODETREE_BEGIN (bmain, tntree, id) { - /* Check if nodetree uses the group. */ - if (ntreeHasTree(tntree, ntree)) { - ED_node_tag_update_id(id); - } - } - FOREACH_NODETREE_END; - } - - if (ntree->type == NTREE_TEXTURE) { - ntreeTexCheckCyclics(ntree); - } -} - static bool compare_nodes(const bNode *a, const bNode *b) { /* These tell if either the node or any of the parent nodes is selected. diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index fb90e2bfe50..3fe90450f20 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -38,6 +38,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_workspace.h" @@ -383,31 +384,11 @@ bool composite_node_editable(bContext *C) return false; } -void snode_dag_update(bContext &C, SpaceNode &snode) +static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) { - Main *bmain = CTX_data_main(&C); + WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); - /* for groups, update all ID's using this */ - if ((snode.edittree->id.flag & LIB_EMBEDDED_DATA) == 0) { - FOREACH_NODETREE_BEGIN (bmain, tntree, id) { - if (ntreeHasTree(tntree, snode.edittree)) { - DEG_id_tag_update(id, 0); - } - } - FOREACH_NODETREE_END; - } - - DEG_id_tag_update(snode.id, 0); - DEG_id_tag_update(&snode.nodetree->id, 0); -} - -void snode_notify(bContext &C, SpaceNode &snode) -{ - ID *id = snode.id; - - WM_event_add_notifier(&C, NC_NODE | NA_EDITED, nullptr); - - if (ED_node_is_shader(&snode)) { + if (ntree->type == NTREE_SHADER) { if (GS(id->name) == ID_MA) { WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id); } @@ -418,17 +399,37 @@ void snode_notify(bContext &C, SpaceNode &snode) WM_main_add_notifier(NC_WORLD | ND_WORLD, id); } } - else if (ED_node_is_compositor(&snode)) { - WM_event_add_notifier(&C, NC_SCENE | ND_NODES, id); + else if (ntree->type == NTREE_COMPOSIT) { + WM_main_add_notifier(NC_SCENE | ND_NODES, id); } - else if (ED_node_is_texture(&snode)) { - WM_event_add_notifier(&C, NC_TEXTURE | ND_NODES, id); + else if (ntree->type == NTREE_TEXTURE) { + WM_main_add_notifier(NC_TEXTURE | ND_NODES, id); } - else if (ED_node_is_geometry(&snode)) { + else if (ntree->type == NTREE_GEOMETRY) { WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id); } } +void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree) +{ + if (C != nullptr) { + SpaceNode *snode = CTX_wm_space_node(C); + if (snode != nullptr && root_ntree != nullptr) { + send_notifiers_after_tree_change(snode->id, root_ntree); + } + } + + NodeTreeUpdateExtraParams params = {0}; + params.tree_changed_fn = [](ID *id, bNodeTree *ntree, void *UNUSED(user_data)) { + send_notifiers_after_tree_change(id, ntree); + }; + params.tree_output_changed_fn = [](ID *UNUSED(id), bNodeTree *ntree, void *UNUSED(user_data)) { + DEG_id_tag_update(&ntree->id, ID_RECALC_NTREE_OUTPUT); + }; + + BKE_ntree_update_main_tree(bmain, root_ntree, ¶ms); +} + void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo) { if (typeinfo) { @@ -477,7 +478,7 @@ void ED_node_shader_default(const bContext *C, ID *id) } ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree); - ntreeUpdateTree(bmain, ma->nodetree); + BKE_ntree_update_main_tree(bmain, ma->nodetree, nullptr); } else if (ELEM(GS(id->name), ID_WO, ID_LA)) { /* Emission */ @@ -517,7 +518,7 @@ void ED_node_shader_default(const bContext *C, ID *id) output->locx = 300.0f; output->locy = 300.0f; nodeSetActive(ntree, output); - ntreeUpdateTree(bmain, ntree); + BKE_ntree_update_main_tree(bmain, ntree, nullptr); } else { printf("ED_node_shader_default called on wrong ID type.\n"); @@ -555,7 +556,7 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce) bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; nodeAddLink(sce->nodetree, in, fromsock, out, tosock); - ntreeUpdateTree(CTX_data_main(C), sce->nodetree); + BKE_ntree_update_main_tree(CTX_data_main(C), sce->nodetree, nullptr); } void ED_node_texture_default(const bContext *C, Tex *tex) @@ -583,7 +584,7 @@ void ED_node_texture_default(const bContext *C, Tex *tex) bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; nodeAddLink(tex->nodetree, in, fromsock, out, tosock); - ntreeUpdateTree(CTX_data_main(C), tex->nodetree); + BKE_ntree_update_main_tree(CTX_data_main(C), tex->nodetree, nullptr); } /** @@ -628,28 +629,6 @@ void snode_set_context(const bContext &C) } } -void snode_update(SpaceNode &snode, bNode *node) -{ - /* XXX this only updates nodes in the current node space tree path. - * The function supposedly should update any potential group node linking to changed tree, - * this really requires a working depsgraph ... - */ - - /* update all edited group nodes */ - bNodeTreePath *path = (bNodeTreePath *)snode.treepath.last; - if (path) { - bNodeTree *ngroup = path->nodetree; - for (path = path->prev; path; path = path->prev) { - nodeUpdateID(path->nodetree, (ID *)ngroup); - ngroup = path->nodetree; - } - } - - if (node) { - nodeUpdate(snode.edittree, node); - } -} - void ED_node_set_active( Main *bmain, SpaceNode *snode, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed) { @@ -697,14 +676,10 @@ void ED_node_set_active( } node->flag |= NODE_DO_OUTPUT; - if (was_output == 0) { - ED_node_tag_update_nodetree(bmain, ntree, node); - } - } - else if (do_update) { - ED_node_tag_update_nodetree(bmain, ntree, node); } + ED_node_tree_propagate_change(nullptr, bmain, ntree); + if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) { /* If active texture changed, free glsl materials. */ LISTBASE_FOREACH (Material *, ma, &bmain->materials) { @@ -750,7 +725,7 @@ void ED_node_set_active( if (r_active_texture_changed) { *r_active_texture_changed = true; } - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_IMAGE, nullptr); } @@ -767,7 +742,7 @@ void ED_node_set_active( node->flag |= NODE_DO_OUTPUT; if (was_output == 0) { - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } /* Adding a node doesn't link this yet. */ @@ -782,11 +757,11 @@ void ED_node_set_active( } node->flag |= NODE_DO_OUTPUT; - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } } else if (do_update) { - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } } else if (ntree->type == NTREE_TEXTURE) { @@ -1302,7 +1277,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs"); - bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -1310,9 +1284,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & SELECT) { BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); - - /* To ensure redraws or re-renders happen. */ - ED_node_tag_update_id(snode->id); } /* make sure we don't copy new nodes again! */ @@ -1378,8 +1349,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) nodeSetSelected(node, false); node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE); nodeSetSelected(newnode, true); - - do_tag_update |= (do_tag_update || node_connected_to_output(*bmain, *ntree, *newnode)); } /* make sure we don't copy new nodes again! */ @@ -1388,13 +1357,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(*C, *snode); - if (do_tag_update) { - snode_dag_update(*C, *snode); - } - + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1485,8 +1448,7 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) } } - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1653,7 +1615,7 @@ static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op)) node_flag_toggle_exec(snode, NODE_PREVIEW); - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); return OPERATOR_FINISHED; } @@ -1732,7 +1694,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op)) } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -1760,23 +1722,16 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); - bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if ((node->flag & SELECT) && !node->typeinfo->no_muting) { node->flag ^= NODE_MUTED; - snode_update(*snode, node); - do_tag_update |= (do_tag_update || - node_connected_to_output(*bmain, *snode->edittree, *node)); } } - snode_notify(*C, *snode); - if (do_tag_update) { - snode_dag_update(*C, *snode); - } + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1802,24 +1757,16 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); - bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { - do_tag_update |= (do_tag_update || - node_connected_to_output(*bmain, *snode->edittree, *node)); nodeRemoveNode(bmain, snode->edittree, node, true); } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(*C, *snode); - if (do_tag_update) { - snode_dag_update(*C, *snode); - } + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1863,10 +1810,7 @@ static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op)) } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); return OPERATOR_FINISHED; } @@ -1901,10 +1845,7 @@ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1951,7 +1892,7 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "file_path", file_path); ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format); - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); return OPERATOR_FINISHED; } @@ -2000,7 +1941,7 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *U return OPERATOR_CANCELLED; } - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); return OPERATOR_FINISHED; } @@ -2067,7 +2008,7 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) nimf->active_input++; } - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); return OPERATOR_FINISHED; } @@ -2313,10 +2254,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) } Main *bmain = CTX_data_main(C); - ntreeUpdateTree(bmain, snode->edittree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, snode->edittree); /* Pasting nodes can create arbitrary new relations, because nodes can reference IDs. */ DEG_relations_tag_update(bmain); @@ -2384,10 +2322,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op) /* make the new socket active */ sock->flag |= SELECT; - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -2434,10 +2369,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) active_sock->flag |= SELECT; } - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -2488,7 +2420,7 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) /* Need the extra update here because the loop above does not check for valid links in the node * group we're currently editing. */ - ntree->update |= NTREE_UPDATE_GROUP | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_interface(ntree); /* Deactivate sockets. */ LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { @@ -2497,10 +2429,7 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) /* Make the new socket active. */ iosock->flag |= SELECT; - ntreeUpdateTree(main, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, main, ntree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -2611,11 +2540,8 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op) } } - ntree->update |= NTREE_UPDATE_GROUP; - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -2846,7 +2772,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op) btree->flag |= NTREE_VIEWER_BORDER; } - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, bmain, btree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); } else { @@ -2886,7 +2812,7 @@ static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op)) bNodeTree *btree = snode->nodetree; btree->flag &= ~NTREE_VIEWER_BORDER; - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), btree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; @@ -2931,7 +2857,7 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) ntreeCompositCryptomatteAddSocket(ntree, node); - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); return OPERATOR_FINISHED; } @@ -2977,7 +2903,7 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(o return OPERATOR_CANCELLED; } - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index 704ffe1e478..290ed172a48 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -40,6 +40,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node_tree_update.h" #include "BKE_report.h" #include "DEG_depsgraph_build.h" @@ -258,6 +259,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) /* migrate node */ BLI_remlink(&wgroup->nodes, node); BLI_addtail(&ntree->nodes, node); + BKE_ntree_update_tag_node_new(ntree, node); /* ensure unique node name in the node tree */ nodeUniqueName(ntree, node); @@ -284,6 +286,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) { BLI_remlink(&wgroup->links, link); BLI_addtail(&ntree->links, link); + BKE_ntree_update_tag_link_added(ntree, link); } bNodeLink *glinks_last = (bNodeLink *)ntree->links.last; @@ -393,8 +396,6 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) /* delete the group instance and dereference group tree */ nodeRemoveNode(bmain, ntree, gnode, true); - ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; - return 1; } @@ -412,16 +413,13 @@ static int node_group_ungroup_exec(bContext *C, wmOperator *op) } if (gnode->id && node_group_ungroup(bmain, snode->edittree, gnode)) { - ntreeUpdateTree(bmain, snode->nodetree); + ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr); } else { BKE_report(op->reports, RPT_WARNING, "Cannot ungroup"); return OPERATOR_CANCELLED; } - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); - return OPERATOR_FINISHED; } @@ -558,9 +556,9 @@ static bool node_group_separate_selected( } } - ntree.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_all(&ntree); if (!make_copy) { - ngroup.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_all(&ngroup); } return true; @@ -614,10 +612,7 @@ static int node_group_separate_exec(bContext *C, wmOperator *op) /* switch to parent tree */ ED_node_tree_pop(snode); - ntreeUpdateTree(CTX_data_main(C), snode->nodetree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr); return OPERATOR_FINISHED; } @@ -812,6 +807,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, /* change node-collection membership */ BLI_remlink(&ntree.nodes, node); BLI_addtail(&ngroup->nodes, node); + BKE_ntree_update_tag_node_removed(&ntree); + BKE_ntree_update_tag_node_new(ngroup, node); /* ensure unique node name in the ngroup */ nodeUniqueName(ngroup, node); @@ -983,11 +980,6 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, } } } - - /* update of the group tree */ - ngroup->update |= NTREE_UPDATE | NTREE_UPDATE_LINKS; - /* update of the tree containing the group instance node */ - ntree.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; } static bNode *node_group_make_from_selected(const bContext &C, @@ -1016,9 +1008,6 @@ static bNode *node_group_make_from_selected(const bContext &C, node_group_make_insert_selected(C, ntree, gnode); - /* update of the tree containing the group instance node */ - ntree.update |= NTREE_UPDATE_NODES; - return gnode; } @@ -1047,14 +1036,10 @@ static int node_group_make_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { sort_multi_input_socket_links(snode, *node, nullptr, nullptr); } - ntreeUpdateTree(bmain, ngroup); } } - ntreeUpdateTree(bmain, &ntree); - - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, bmain, nullptr); /* We broke relations in node tree, need to rebuild them in the graphs. */ DEG_relations_tag_update(bmain); @@ -1107,12 +1092,7 @@ static int node_group_insert_exec(bContext *C, wmOperator *op) nodeSetActive(ntree, gnode); ED_node_tree_push(snode, ngroup, gnode); - ntreeUpdateTree(bmain, ngroup); - - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, nullptr); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 2e55bb0cb28..52dde965114 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -235,7 +235,6 @@ void sort_multi_input_socket_links(SpaceNode &snode, bNode &node, bNodeLink *drag_link, const blender::float2 *cursor); -bool node_connected_to_output(Main &bmain, bNodeTree &ntree, bNode &node); void NODE_OT_link(wmOperatorType *ot); void NODE_OT_link_make(wmOperatorType *ot); @@ -252,12 +251,8 @@ void NODE_OT_link_viewer(wmOperatorType *ot); void NODE_OT_insert_offset(wmOperatorType *ot); -void snode_notify(bContext &C, SpaceNode &snode); -void snode_dag_update(bContext &C, SpaceNode &snode); void snode_set_context(const bContext &C); -void snode_update(SpaceNode &snode, bNode *node); -/** Operator poll callback. */ bool composite_node_active(bContext *C); /** Operator poll callback. */ bool composite_node_editable(bContext *C); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 90b53258d5e..c441cf14683 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -34,6 +34,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_screen.h" #include "ED_node.h" /* own include */ @@ -73,121 +74,6 @@ using blender::StringRefNull; using blender::Vector; /* -------------------------------------------------------------------- */ -/** \name Relations Helpers - * \{ */ - -static bool ntree_has_drivers(bNodeTree &ntree) -{ - const AnimData *adt = BKE_animdata_from_id(&ntree.id); - if (adt == nullptr) { - return false; - } - return !BLI_listbase_is_empty(&adt->drivers); -} - -static bool ntree_check_nodes_connected_dfs(bNodeTree &ntree, bNode &from, bNode &to) -{ - if (from.flag & NODE_TEST) { - return false; - } - from.flag |= NODE_TEST; - LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (link->fromnode == &from) { - if (link->tonode == &to) { - return true; - } - - if (ntree_check_nodes_connected_dfs(ntree, *link->tonode, to)) { - return true; - } - } - } - return false; -} - -static bool ntree_check_nodes_connected(bNodeTree &ntree, bNode &from, bNode &to) -{ - if (&from == &to) { - return true; - } - ntreeNodeFlagSet(&ntree, NODE_TEST, false); - return ntree_check_nodes_connected_dfs(ntree, from, to); -} - -static bool node_group_has_output_dfs(bNode &node) -{ - bNodeTree *ntree = (bNodeTree *)node.id; - if (ntree->id.tag & LIB_TAG_DOIT) { - return false; - } - ntree->id.tag |= LIB_TAG_DOIT; - LISTBASE_FOREACH (bNode *, current_node, &ntree->nodes) { - if (current_node->type == NODE_GROUP) { - if (current_node->id && node_group_has_output_dfs(*current_node)) { - return true; - } - } - if (current_node->flag & NODE_DO_OUTPUT && current_node->type != NODE_GROUP_OUTPUT) { - return true; - } - } - return false; -} - -static bool node_group_has_output(Main &bmain, bNode &node) -{ - BLI_assert(ELEM(node.type, NODE_GROUP, NODE_CUSTOM_GROUP)); - bNodeTree *ntree = (bNodeTree *)node.id; - if (ntree == nullptr) { - return false; - } - BKE_main_id_tag_listbase(&bmain.nodetrees, LIB_TAG_DOIT, false); - return node_group_has_output_dfs(node); -} - -bool node_connected_to_output(Main &bmain, bNodeTree &ntree, bNode &node) -{ - /* Special case for drivers: if node tree has any drivers we assume it is - * always to be tagged for update when node changes. Otherwise we will be - * doomed to do some deep and nasty deep search of indirect dependencies, - * which will be too complicated without real benefit. - */ - if (ntree_has_drivers(ntree)) { - return true; - } - LISTBASE_FOREACH (bNode *, current_node, &ntree.nodes) { - /* Special case for group nodes -- if modified node connected to a group - * with active output inside we consider refresh is needed. - * - * We could make check more grained here by taking which socket the node - * is connected to and so eventually. - */ - if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { - if (current_node->id != nullptr && ntree_has_drivers((bNodeTree &)current_node->id)) { - return true; - } - if (ntree_check_nodes_connected(ntree, node, *current_node) && - node_group_has_output(bmain, *current_node)) { - return true; - } - } - if (current_node->flag & NODE_DO_OUTPUT) { - if (ntree_check_nodes_connected(ntree, node, *current_node)) { - return true; - } - } - if (current_node->type == GEO_NODE_VIEWER) { - if (ntree_check_nodes_connected(ntree, node, *current_node)) { - return true; - } - } - } - return false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Add Node * \{ */ @@ -214,7 +100,7 @@ static void clear_picking_highlight(ListBase *links) } } -static bNodeLink *create_drag_link(Main &bmain, SpaceNode &snode, bNode &node, bNodeSocket &sock) +static bNodeLink *create_drag_link(bNode &node, bNodeSocket &sock) { bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), __func__); if (sock.in_out == SOCK_OUT) { @@ -226,27 +112,17 @@ static bNodeLink *create_drag_link(Main &bmain, SpaceNode &snode, bNode &node, b oplink->tosock = &sock; } oplink->flag |= NODE_LINK_VALID; - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, *snode.edittree, node)) { - oplink->flag |= NODE_LINK_TEST; - } oplink->flag |= NODE_LINK_DRAGGED; return oplink; } -static void pick_link(const bContext &C, - wmOperator &op, - bNodeLinkDrag &nldrag, - SpaceNode &snode, - bNode *node, - bNodeLink &link_to_pick) +static void pick_link( + wmOperator &op, bNodeLinkDrag &nldrag, SpaceNode &snode, bNode *node, bNodeLink &link_to_pick) { clear_picking_highlight(&snode.edittree->links); RNA_boolean_set(op.ptr, "has_link_picked", true); - Main *bmain = CTX_data_main(&C); - bNodeLink *link = create_drag_link( - *bmain, snode, *link_to_pick.fromnode, *link_to_pick.fromsock); + bNodeLink *link = create_drag_link(*link_to_pick.fromnode, *link_to_pick.fromsock); nldrag.links.append(link); nodeRemLink(snode.edittree, &link_to_pick); @@ -258,7 +134,7 @@ static void pick_link(const bContext &C, /* Send changed event to original link->tonode. */ if (node) { - snode_update(snode, node); + BKE_ntree_update_tag_node_property(snode.edittree, node); } } @@ -324,7 +200,7 @@ static void pick_input_link_by_link_intersect(const bContext &C, ED_area_tag_redraw(CTX_wm_area(&C)); if (!node_find_indicated_socket(*snode, &node, &socket, cursor, SOCK_IN)) { - pick_link(C, op, nldrag, *snode, node, *link_to_pick); + pick_link(op, nldrag, *snode, node, *link_to_pick); } } } @@ -566,7 +442,7 @@ static void snode_autoconnect(Main &bmain, } if (numlinks > 0) { - ntreeUpdateTree(&bmain, ntree); + BKE_ntree_update_main_tree(&bmain, ntree, nullptr); } } @@ -640,7 +516,7 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree, (eNodeSocketDatatype)src_socket.type); BLI_assert(data_type != CD_AUTO_FROM_NAME); storage->data_type = data_type; - nodeUpdate(&ntree, &viewer_node); + viewer_node.typeinfo->updatefunc(&ntree, &viewer_node); return viewer_socket; } } @@ -810,7 +686,7 @@ static int link_socket_to_viewer(const bContext &C, else { link_to_change->fromnode = &bnode_to_view; link_to_change->fromsock = &bsocket_to_view; - btree.update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_changed(&btree); } remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode); @@ -819,10 +695,7 @@ static int link_socket_to_viewer(const bContext &C, ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(&C), &snode, viewer_bnode); } - ntreeUpdateTree(CTX_data_main(&C), &btree); - snode_update(snode, viewer_bnode); - DEG_id_tag_update(&btree.id, 0); - + BKE_ntree_update_main_tree(CTX_data_main(&C), &btree, nullptr); return OPERATOR_FINISHED; } @@ -863,7 +736,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } - snode_notify(*C, snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); return OPERATOR_FINISHED; } @@ -1041,17 +914,10 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links) SpaceNode &snode = *CTX_wm_space_node(&C); bNodeTree &ntree = *snode.edittree; bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op.customdata; - bool do_tag_update = false; /* avoid updates while applying links */ ntree.is_updating = true; for (bNodeLink *link : nldrag->links) { - /* See note below, but basically TEST flag means that the link - * was connected to output (or to a node which affects the - * output). - */ - do_tag_update |= (link->flag & NODE_LINK_TEST) != 0; - link->flag &= ~NODE_LINK_DRAGGED; if (apply_links && link->tosock && link->fromsock) { @@ -1067,18 +933,10 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links) /* add link to the node tree */ BLI_addtail(&ntree.links, link); - - ntree.update |= NTREE_UPDATE_LINKS; - - /* tag tonode for update */ - link->tonode->update |= NODE_UPDATE; + BKE_ntree_update_tag_link_added(&ntree, link); /* we might need to remove a link */ node_remove_extra_links(snode, *link); - - if (link->tonode) { - do_tag_update |= (do_tag_update || node_connected_to_output(*bmain, ntree, *link->tonode)); - } } else { nodeRemLink(&ntree, link); @@ -1086,11 +944,7 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links) } ntree.is_updating = false; - ntreeUpdateTree(bmain, &ntree); - snode_notify(C, snode); - if (do_tag_update) { - snode_dag_update(C, snode); - } + ED_node_tree_propagate_change(&C, bmain, &ntree); /* Ensure draglink tooltip is disabled. */ draw_draglink_tooltip_deactivate(*CTX_wm_region(&C), *nldrag); @@ -1250,8 +1104,7 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, - SpaceNode &snode, +static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode, float2 cursor, const bool detach) { @@ -1276,17 +1129,6 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, oplink->flag |= NODE_LINK_VALID; oplink->flag |= NODE_LINK_DRAGGED; - /* The link could be disconnected and in that case we - * wouldn't be able to check whether tag update is - * needed or not when releasing mouse button. So we - * cache whether the link affects output or not here - * using TEST flag. - */ - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, *snode.edittree, *link->tonode)) { - oplink->flag |= NODE_LINK_TEST; - } - nldrag->links.append(oplink); nodeRemLink(snode.edittree, link); } @@ -1296,7 +1138,7 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, /* dragged links are fixed on output side */ nldrag->in_out = SOCK_OUT; /* create a new link */ - nldrag->links.append(create_drag_link(bmain, snode, *node, *sock)); + nldrag->links.append(create_drag_link(*node, *sock)); } return nldrag; } @@ -1329,17 +1171,13 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; oplink->flag |= NODE_LINK_DRAGGED; - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, *snode.edittree, *link_to_pick->tonode)) { - oplink->flag |= NODE_LINK_TEST; - } nldrag->links.append(oplink); nodeRemLink(snode.edittree, link_to_pick); /* send changed event to original link->tonode */ if (node) { - snode_update(snode, node); + BKE_ntree_update_tag_node_property(snode.edittree, node); } } } @@ -1347,7 +1185,7 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, /* dragged links are fixed on input side */ nldrag->in_out = SOCK_IN; /* create a new link */ - nldrag->links.append(create_drag_link(bmain, snode, *node, *sock)); + nldrag->links.append(create_drag_link(*node, *sock)); } return nldrag; } @@ -1370,7 +1208,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); - std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(bmain, snode, cursor, detach); + std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(snode, cursor, detach); if (nldrag) { UI_view2d_edge_pan_operator_init(C, &nldrag->pan_data, op); @@ -1473,9 +1311,7 @@ static int node_make_link_exec(bContext *C, wmOperator *op) node_deselect_all_input_sockets(snode, false); node_deselect_all_output_sockets(snode, false); - ntreeUpdateTree(CTX_data_main(C), snode.edittree); - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); return OPERATOR_FINISHED; } @@ -1532,7 +1368,6 @@ static int cut_links_exec(bContext *C, wmOperator *op) Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); ARegion ®ion = *CTX_wm_region(C); - bool do_tag_update = false; int i = 0; float mcoords[256][2]; @@ -1567,23 +1402,14 @@ static int cut_links_exec(bContext *C, wmOperator *op) found = true; } - do_tag_update |= (do_tag_update || - node_connected_to_output(bmain, *snode.edittree, *link->tonode)); - - snode_update(snode, link->tonode); bNode *to_node = link->tonode; nodeRemLink(snode.edittree, link); sort_multi_input_socket_links(snode, *to_node, nullptr, nullptr); } } + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); if (found) { - ntreeUpdateTree(CTX_data_main(C), snode.edittree); - snode_notify(*C, snode); - if (do_tag_update) { - snode_dag_update(*C, snode); - } - return OPERATOR_FINISHED; } @@ -1629,7 +1455,6 @@ static int mute_links_exec(bContext *C, wmOperator *op) Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); ARegion ®ion = *CTX_wm_region(C); - bool do_tag_update = false; int i = 0; float mcoords[256][2]; @@ -1671,10 +1496,6 @@ static int mute_links_exec(bContext *C, wmOperator *op) } if (node_links_intersect(*link, mcoords, i)) { - do_tag_update |= (do_tag_update || - node_connected_to_output(bmain, *snode.edittree, *link->tonode)); - - snode_update(snode, link->tonode); nodeMuteLinkToggle(snode.edittree, link); } } @@ -1687,12 +1508,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) link->flag &= ~NODE_LINK_TEST; } - ntreeUpdateTree(CTX_data_main(C), snode.edittree); - snode_notify(*C, snode); - if (do_tag_update) { - snode_dag_update(*C, snode); - } - + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); return OPERATOR_FINISHED; } @@ -1743,11 +1559,7 @@ static int detach_links_exec(bContext *C, wmOperator *UNUSED(op)) } } - ntreeUpdateTree(CTX_data_main(C), &ntree); - - snode_notify(*C, snode); - snode_dag_update(*C, snode); - + ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); return OPERATOR_FINISHED; } @@ -2663,10 +2475,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) snode->runtime->iofsd = iofsd; } - ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */ - snode_update(*snode, select); - ED_node_tag_update_id((ID *)snode->edittree); - ED_node_tag_update_id(snode->id); + ED_node_tree_propagate_change(nullptr, bmain, snode->edittree); } } } diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 386178596af..defb1e82c3e 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -37,6 +37,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node_tree_update.h" #include "RNA_access.h" @@ -84,7 +85,7 @@ static void node_link_item_apply(Main *bmain, bNode *node, NodeLinkItem *item) { if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { node->id = (ID *)item->ngroup; - ntreeUpdateTree(bmain, item->ngroup); + BKE_ntree_update_main_tree(bmain, item->ngroup, nullptr); } else { /* nothing to do for now */ @@ -179,10 +180,8 @@ static void node_socket_disconnect(Main *bmain, nodeRemLink(ntree, sock_to->link); sock_to->flag |= SOCK_COLLAPSED; - nodeUpdate(ntree, node_to); - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, node_to); + BKE_ntree_update_tag_node_property(ntree, node_to); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } /* remove all nodes connected to this socket, if they aren't connected to other nodes */ @@ -195,10 +194,8 @@ static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bN node_remove_linked(bmain, ntree, sock_to->link->fromnode); sock_to->flag |= SOCK_COLLAPSED; - nodeUpdate(ntree, node_to); - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, node_to); + BKE_ntree_update_tag_node_property(ntree, node_to); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } /* add new node connected to this socket, or replace an existing one */ @@ -299,11 +296,9 @@ static void node_socket_add_replace(const bContext *C, node_remove_linked(bmain, ntree, node_prev); } - nodeUpdate(ntree, node_from); - nodeUpdate(ntree, node_to); - ntreeUpdateTree(CTX_data_main(C), ntree); - - ED_node_tag_update_nodetree(CTX_data_main(C), ntree, node_to); + BKE_ntree_update_tag_node_property(ntree, node_from); + BKE_ntree_update_tag_node_property(ntree, node_to); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } /****************************** Node Link Menu *******************************/ diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 4cc0bed1928..96c6dd89447 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -313,6 +313,19 @@ static void node_init(struct wmWindowManager *UNUSED(wm), ScrArea *area) } } +static bool any_node_uses_id(const bNodeTree *ntree, const ID *id) +{ + if (ELEM(nullptr, ntree, id)) { + return false; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id == id) { + return true; + } + } + return false; +} + static void node_area_listener(const wmSpaceTypeListenerParams *params) { ScrArea *area = params->area; @@ -436,10 +449,9 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_IMAGE: if (wmn->action == NA_EDITED) { if (ED_node_is_compositor(snode)) { - /* note that nodeUpdateID is already called by BKE_image_signal() on all - * scenes so really this is just to know if the images is used in the compo else - * painting on images could become very slow when the compositor is open. */ - if (nodeUpdateID(snode->nodetree, (ID *)wmn->reference)) { + /* Without this check drawing on an image could become very slow when the compositor is + * open. */ + if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) { ED_area_tag_refresh(area); } } @@ -449,7 +461,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_MOVIECLIP: if (wmn->action == NA_EDITED) { if (ED_node_is_compositor(snode)) { - if (nodeUpdateID(snode->nodetree, (ID *)wmn->reference)) { + if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) { ED_area_tag_refresh(area); } } diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c index 1a25cfd1efb..e26172cd764 100644 --- a/source/blender/editors/transform/transform_convert_mask.c +++ b/source/blender/editors/transform/transform_convert_mask.c @@ -472,12 +472,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t) } if (t->scene->nodetree) { - /* tracks can be used for stabilization nodes, - * flush update for such nodes */ - // if (nodeUpdateID(t->scene->nodetree, &mask->id)) - { - WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); - } + WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); } /* TODO: don't key all masks. */ diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index da11666d445..e8cdfaf1f40 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -31,6 +31,7 @@ #include "BKE_context.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_report.h" #include "ED_node.h" @@ -246,7 +247,7 @@ void special_aftertrans_update__node(bContext *C, TransInfo *t) nodeRemoveNode(bmain, ntree, node, true); } } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(C, bmain, ntree); } } diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index dc37f2796bf..f2d0fb7ac2f 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -29,8 +29,10 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_main.h" #include "BKE_movieclip.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" #include "ED_clip.h" @@ -793,8 +795,12 @@ void special_aftertrans_update__movieclip(bContext *C, TransInfo *t) /* Tracks can be used for stabilization nodes, * flush update for such nodes. */ - nodeUpdateID(t->scene->nodetree, &clip->id); - WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL); + if (t->context != NULL) { + Main *bmain = CTX_data_main(C); + BKE_ntree_update_tag_id_changed(bmain, &clip->id); + BKE_ntree_update_main(bmain, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL); + } } } diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 342afa847b4..64e1fa2f768 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -180,7 +180,7 @@ void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *i if (node && is_image_texture_node(node)) { node->id = &ima->id; - ED_node_tag_update_nodetree(bmain, ma->nodetree, node); + ED_node_tree_propagate_change(NULL, bmain, ma->nodetree); } } diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 0a82c237256..6b33e17070a 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -48,6 +48,7 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_object.h" #include "BKE_scene.h" @@ -415,7 +416,7 @@ Material *BlenderStrokeRenderer::GetStrokeShader(Main *bmain, } nodeSetActive(ntree, output_material); - ntreeUpdateTree(bmain, ntree); + BKE_ntree_update_main_tree(bmain, ntree, NULL); return ma; } diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp index 1f358accc4e..f469be20cfb 100644 --- a/source/blender/io/collada/Materials.cpp +++ b/source/blender/io/collada/Materials.cpp @@ -16,6 +16,8 @@ #include "Materials.h" +#include "BKE_node_tree_update.h" + MaterialNode::MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map) : mContext(C), material(ma), effect(nullptr), key_image_map(&key_image_map) { @@ -106,7 +108,7 @@ bNodeTree *MaterialNode::prepare_material_nodetree() void MaterialNode::update_material_nodetree() { - ntreeUpdateTree(CTX_data_main(mContext), ntree); + BKE_ntree_update_main_tree(CTX_data_main(mContext), ntree, NULL); } bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label) diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index db0c5785a68..645dea42971 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -23,6 +23,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BLI_math_vector.h" #include "BLI_string.h" @@ -339,7 +340,7 @@ void USDMaterialReader::import_usd_preview(Material *mtl, nodeSetActive(ntree, output); - ntreeUpdateTree(bmain_, ntree); + BKE_ntree_update_main_tree(bmain_, ntree, nullptr); /* Optionally, set the material blend mode. */ diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 6a478f9abb5..17b3fb302e6 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -802,6 +802,9 @@ typedef enum IDRecalcFlag { */ ID_RECALC_TAG_FOR_UNDO = (1 << 24), + /* The node tree has changed in a way that affects its output nodes. */ + ID_RECALC_NTREE_OUTPUT = (1 << 25), + /*************************************************************************** * Pseudonyms, to have more semantic meaning in the actual code without * using too much low-level and implementation specific tags. */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index f4a1efe84b8..5d51d8eb606 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -159,7 +159,7 @@ typedef struct bNodeSocket { * restores pointer from matching own_index. */ struct bNodeSocket *groupsock DNA_DEPRECATED; - /** A link pointer, set in ntreeUpdateTree. */ + /** A link pointer, set in #BKE_ntree_update_main. */ struct bNodeLink *link; /* XXX deprecated, socket input values are stored in default_value now. @@ -172,6 +172,10 @@ typedef struct bNodeSocket { * data. It has to be updated when the node declaration changes. */ const SocketDeclarationHandle *declaration; + + /** #eNodeTreeChangedFlag. */ + uint32_t changed_flag; + char _pad[4]; } bNodeSocket; /** #bNodeSocket.type & #bNodeSocketType.type */ @@ -260,8 +264,9 @@ typedef struct bNode { /** Used as a boolean for execution. */ uint8_t need_exec; - - char _pad[1]; + char _pad2[5]; + /** #eNodeTreeChangedFlag. */ + uint32_t changed_flag; /** Custom user-defined color. */ float color[3]; @@ -400,10 +405,6 @@ typedef struct bNode { #define __NODE_ACTIVE_PREVIEW (1 << 18) /* deprecated */ /* node->update */ -/* XXX NODE_UPDATE is a generic update flag. More fine-grained updates - * might be used in the future, but currently all work the same way. - */ -#define NODE_UPDATE 0xFFFF /* generic update flag (includes all others) */ #define NODE_UPDATE_ID 1 /* associated id data block has changed */ #define NODE_UPDATE_OPERATOR 2 /* node update triggered from update operator */ @@ -511,8 +512,12 @@ typedef struct bNodeTree { */ int cur_index; int flag; - /** Update flags. */ - int update; + /** + * Keeps track of what changed in the node tree until the next update. + * Should not be changed directly, instead use the functions in `BKE_node_tree_update.h`. + * #eNodeTreeChangedFlag. + */ + uint32_t changed_flag; /** Flag to prevent re-entrant update calls. */ short is_updating; /** Generic temporary flag for recursion check (DFS/BFS). */ @@ -546,7 +551,11 @@ typedef struct bNodeTree { * in case multiple different editors are used and make context ambiguous. */ bNodeInstanceKey active_viewer_key; - char _pad[4]; + /** + * A hash of the topology of the node tree leading up to the outputs. This is used to determine + * of the node tree changed in a way that requires updating geometry nodes or shaders. + */ + uint32_t output_topology_hash; /** Execution data. * @@ -594,21 +603,7 @@ typedef struct bNodeTree { /* tree is localized copy, free when deleting node groups */ /* #define NTREE_IS_LOCALIZED (1 << 5) */ -/** #NodeTree.update */ -typedef enum eNodeTreeUpdate { - NTREE_UPDATE = 0xFFFF, /* generic update flag (includes all others) */ - NTREE_UPDATE_LINKS = (1 << 0), /* links have been added or removed */ - NTREE_UPDATE_NODES = (1 << 1), /* nodes or sockets have been added or removed */ - NTREE_UPDATE_GROUP_IN = (1 << 4), /* group inputs have changed */ - NTREE_UPDATE_GROUP_OUT = (1 << 5), /* group outputs have changed */ - /* The field interface has changed. So e.g. an output that was always a field before is not - * anymore. This implies that the field type inferencing has to be done again. */ - NTREE_UPDATE_FIELD_INFERENCING = (1 << 6), - /* group has changed (generic flag including all other group flags) */ - NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), -} eNodeTreeUpdate; - -/** #NodeTree.execution_mode */ +/* tree->execution_mode */ typedef enum eNodeTreeExecutionMode { NTREE_EXECUTION_MODE_TILED = 0, NTREE_EXECUTION_MODE_FULL_FRAME = 1, diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 5c12fc3a227..091b457cc83 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -321,7 +321,7 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * CMP_NODE_VALTORGB, TEX_NODE_VALTORGB, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP)) { - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); } } break; diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 2f42e521b52..0d86572357f 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -168,7 +168,7 @@ static void rna_ImageUser_update(Main *bmain, Scene *scene, PointerRNA *ptr) if (id) { if (GS(id->name) == ID_NT) { /* Special update for nodetrees to find parent datablock. */ - ED_node_tag_update_nodetree(bmain, (bNodeTree *)id, NULL); + ED_node_tree_propagate_change(NULL, bmain, NULL); } else { /* Update material or texture for render preview. */ diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index df0271d81d5..e8f9a0b423b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -42,6 +42,7 @@ #include "BKE_geometry_set.h" #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_texture.h" #include "RNA_access.h" @@ -1219,7 +1220,7 @@ static void rna_NodeTree_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *p WM_main_add_notifier(NC_NODE | NA_EDITED, NULL); WM_main_add_notifier(NC_SCENE | ND_NODES, &ntree->id); - ED_node_tag_update_nodetree(bmain, ntree, NULL); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static bNode *rna_NodeTree_node_new(bNodeTree *ntree, @@ -1269,8 +1270,7 @@ static bNode *rna_NodeTree_node_new(bNodeTree *ntree, } Main *bmain = CTX_data_main(C); - ntreeUpdateTree(bmain, ntree); - nodeUpdate(ntree, node); + ED_node_tree_propagate_change(C, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); if (node->type == GEO_NODE_INPUT_SCENE_TIME) { @@ -1300,7 +1300,7 @@ static void rna_NodeTree_node_remove(bNodeTree *ntree, RNA_POINTER_INVALIDATE(node_ptr); - ntreeUpdateTree(bmain, ntree); /* update group node socket links */ + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1320,8 +1320,7 @@ static void rna_NodeTree_node_clear(bNodeTree *ntree, Main *bmain, ReportList *r node = next_node; } - ntreeUpdateTree(bmain, ntree); - + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1400,13 +1399,7 @@ static bNodeLink *rna_NodeTree_link_new(bNodeTree *ntree, fromsock->flag &= ~SOCK_HIDDEN; tosock->flag &= ~SOCK_HIDDEN; - if (tonode) { - nodeUpdate(ntree, tonode); - } - - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, ret->tonode); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } return ret; @@ -1431,7 +1424,7 @@ static void rna_NodeTree_link_remove(bNodeTree *ntree, nodeRemLink(ntree, link); RNA_POINTER_INVALIDATE(link_ptr); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1450,8 +1443,7 @@ static void rna_NodeTree_link_clear(bNodeTree *ntree, Main *bmain, ReportList *r link = next_link; } - ntreeUpdateTree(bmain, ntree); - + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1508,7 +1500,7 @@ static bNodeSocket *rna_NodeTree_inputs_new( bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_IN, type, name); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); return sock; @@ -1523,7 +1515,7 @@ static bNodeSocket *rna_NodeTree_outputs_new( bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_OUT, type, name); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); return sock; @@ -1544,8 +1536,7 @@ static void rna_NodeTree_socket_remove(bNodeTree *ntree, else { ntreeRemoveSocketInterface(ntree, sock); - ntreeUpdateTree(bmain, ntree); - DEG_id_tag_update(&ntree->id, 0); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } } @@ -1560,7 +1551,7 @@ static void rna_NodeTree_inputs_clear(bNodeTree *ntree, Main *bmain, ReportList ntreeRemoveSocketInterface(ntree, socket); } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1574,7 +1565,7 @@ static void rna_NodeTree_outputs_clear(bNodeTree *ntree, Main *bmain, ReportList ntreeRemoveSocketInterface(ntree, socket); } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1603,9 +1594,9 @@ static void rna_NodeTree_inputs_move(bNodeTree *ntree, Main *bmain, int from_ind } } - ntree->update |= NTREE_UPDATE_GROUP_IN; + BKE_ntree_update_tag_interface(ntree); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1634,9 +1625,9 @@ static void rna_NodeTree_outputs_move(bNodeTree *ntree, Main *bmain, int from_in } } - ntree->update |= NTREE_UPDATE_GROUP_OUT; + BKE_ntree_update_tag_interface(ntree); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1644,10 +1635,8 @@ static void rna_NodeTree_interface_update(bNodeTree *ntree, bContext *C) { Main *bmain = CTX_data_main(C); - ntree->update |= NTREE_UPDATE_GROUP; - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, NULL); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); } /* ******** NodeLink ******** */ @@ -2617,7 +2606,8 @@ static void rna_Node_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static void rna_Node_update_relations(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -2626,9 +2616,10 @@ static void rna_Node_update_relations(Main *bmain, Scene *scene, PointerRNA *ptr DEG_relations_tag_update(bmain); } -static void rna_Node_socket_value_update(ID *id, bNode *node, bContext *C) +static void rna_Node_socket_value_update(ID *id, bNode *UNUSED(node), bContext *C) { - ED_node_tag_update_nodetree(CTX_data_main(C), (bNodeTree *)id, node); + BKE_ntree_update_tag_all((bNodeTree *)id); + ED_node_tree_propagate_change(C, CTX_data_main(C), (bNodeTree *)id); } static void rna_Node_select_set(PointerRNA *ptr, bool value) @@ -2682,7 +2673,7 @@ static bNodeSocket *rna_Node_inputs_new(ID *id, BKE_report(reports, RPT_ERROR, "Unable to create socket"); } else { - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2716,7 +2707,7 @@ static bNodeSocket *rna_Node_outputs_new(ID *id, BKE_report(reports, RPT_ERROR, "Unable to create socket"); } else { - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2734,7 +2725,7 @@ static void rna_Node_socket_remove( else { nodeRemoveSocket(ntree, node, sock); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } } @@ -2749,7 +2740,7 @@ static void rna_Node_inputs_clear(ID *id, bNode *node, Main *bmain) nodeRemoveSocket(ntree, node, sock); } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2763,7 +2754,7 @@ static void rna_Node_outputs_clear(ID *id, bNode *node, Main *bmain) nodeRemoveSocket(ntree, node, sock); } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2795,7 +2786,7 @@ static void rna_Node_inputs_move(ID *id, bNode *node, Main *bmain, int from_inde } } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2827,7 +2818,7 @@ static void rna_Node_outputs_move(ID *id, bNode *node, Main *bmain, int from_ind } } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -3055,10 +3046,9 @@ static void rna_NodeSocket_update(Main *bmain, Scene *UNUSED(scene), PointerRNA { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNodeSocket *sock = (bNodeSocket *)ptr->data; - bNode *node; - if (nodeFindNode(ntree, sock, &node, NULL)) { - ED_node_tag_update_nodetree(bmain, ntree, node); - } + + BKE_ntree_update_tag_socket_property(ntree, sock); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static bool rna_NodeSocket_is_output_get(PointerRNA *ptr) @@ -3351,10 +3341,8 @@ static void rna_NodeSocketInterface_update(Main *bmain, Scene *UNUSED(scene), Po return; } - ntree->update |= NTREE_UPDATE_GROUP; - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, NULL); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); } /* ******** Standard Node Socket Base Types ******** */ @@ -3452,28 +3440,14 @@ static void rna_NodeSocketStandard_vector_range( /* using a context update function here, to avoid searching the node if possible */ static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA *ptr) { - bNode *node; - /* default update */ rna_NodeSocket_update(CTX_data_main(C), CTX_data_scene(C), ptr); - - /* try to use node from context, faster */ - node = CTX_data_pointer_get(C, "node").data; - if (!node) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNodeSocket *sock = ptr->data; - - /* fall back to searching node in the tree */ - nodeFindNode(ntree, sock, &node, NULL); - } } static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C, PointerRNA *ptr) { rna_NodeSocketStandard_value_update(C, ptr); - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; Main *bmain = CTX_data_main(C); - ntreeUpdateTree(bmain, ntree); DEG_relations_tag_update(bmain); } @@ -3567,12 +3541,11 @@ static bool rna_NodeInternal_poll_instance(bNode *node, bNodeTree *ntree) } } -static void rna_NodeInternal_update(ID *id, bNode *node) +static void rna_NodeInternal_update(ID *id, bNode *node, Main *bmain) { bNodeTree *ntree = (bNodeTree *)id; - if (node->typeinfo->updatefunc) { - node->typeinfo->updatefunc(ntree, node); - } + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static void rna_NodeInternal_draw_buttons(ID *id, @@ -3721,7 +3694,8 @@ static void rna_Node_tex_image_update(Main *bmain, Scene *UNUSED(scene), Pointer bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_IMAGE, NULL); } @@ -3730,11 +3704,8 @@ static void rna_NodeGroup_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - if (node->id) { - ntreeUpdateTree(bmain, (bNodeTree *)node->id); - } - - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); DEG_relations_tag_update(bmain); } @@ -4142,13 +4113,12 @@ static const EnumPropertyItem *rna_Node_channel_itemf(bContext *UNUSED(C), return item; } -static void rna_Image_Node_update_id(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Image_Node_update_id(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; node->update |= NODE_UPDATE_ID; - nodeUpdate(ntree, node); /* to update image node sockets */ + rna_Node_update(bmain, scene, ptr); } static void rna_NodeOutputFile_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -4395,7 +4365,7 @@ static bNodeSocket *rna_NodeOutputFile_slots_new( sock = ntreeCompositOutputFileAddSocket(ntree, node, name, im_format); - ntreeUpdateTree(CTX_data_main(C), ntree); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); return sock; @@ -4504,42 +4474,27 @@ static void rna_ShaderNodeScript_update(Main *bmain, Scene *scene, PointerRNA *p RE_engine_free(engine); } - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static void rna_ShaderNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = (bNode *)ptr->data; - - nodeUpdate(ntree, node); rna_Node_update(bmain, scene, ptr); } static void rna_Node_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = (bNode *)ptr->data; - - nodeUpdate(ntree, node); rna_Node_update(bmain, scene, ptr); } static void rna_GeometryNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = (bNode *)ptr->data; - - nodeUpdate(ntree, node); rna_Node_update(bmain, scene, ptr); } static void rna_CompositorNodeScale_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = (bNode *)ptr->data; - - nodeUpdate(ntree, node); rna_Node_update(bmain, scene, ptr); } @@ -12386,7 +12341,7 @@ static void rna_def_internal_node(BlenderRNA *brna) func = RNA_def_function(srna, "update", "rna_NodeInternal_update"); RNA_def_function_ui_description( func, "Update on node graph topology changes (adding or removing nodes and links)"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_ALLOW_WRITE); /* draw buttons */ func = RNA_def_function(srna, "draw_buttons", "rna_NodeInternal_draw_buttons"); diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 5a74cfa9964..f66fb2653b5 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -199,7 +199,7 @@ static void rna_Texture_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *pt } else if (GS(id->name) == ID_NT) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - ED_node_tag_update_nodetree(bmain, ntree, NULL); + ED_node_tree_propagate_change(NULL, bmain, ntree); } } diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index c81588aa8b5..03f4acdae79 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -24,6 +24,7 @@ #include "MEM_guardedalloc.h" #include "BKE_movieclip.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" #include "RNA_access.h" @@ -415,11 +416,12 @@ static void rna_tracking_stabRotTracks_active_index_range( *max = max_ii(0, clip->tracking.stabilization.tot_rot_track - 1); } -static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +static void rna_tracking_flushUpdate(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->owner_id; - nodeUpdateID(scene->nodetree, &clip->id); + BKE_ntree_update_tag_id_changed(bmain, &clip->id); + BKE_ntree_update_main(bmain, NULL); WM_main_add_notifier(NC_SCENE | ND_NODES, NULL); WM_main_add_notifier(NC_SCENE, NULL); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 50ded9fc9cb..cee5d0be65d 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -60,6 +60,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_node_tree_update.h" #include "BKE_object.h" #include "BKE_pointcloud.h" #include "BKE_screen.h" @@ -237,7 +238,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md); DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier"); if (nmd->node_group != nullptr) { - DEG_add_node_tree_relation(ctx->node, nmd->node_group, "Nodes Modifier"); + DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Nodes Modifier"); Set<ID *> used_ids; find_used_ids_from_settings(nmd->settings, used_ids); @@ -725,7 +726,7 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd) group_input_node, (bNodeSocket *)group_input_node->outputs.first); - ntreeUpdateTree(bmain, ntree); + BKE_ntree_update_main_tree(bmain, ntree, nullptr); } static void initialize_group_input(NodesModifierData &nmd, diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index 1326c9edab1..54967c82562 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -33,6 +33,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" #include "node_common.h" @@ -186,11 +187,6 @@ static void update(bNodeTree *ntree) ntreeSetOutput(ntree); ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode) @@ -301,14 +297,15 @@ void ntreeCompositTagRender(Scene *scene) if (sce_iter->nodetree) { LISTBASE_FOREACH (bNode *, node, &sce_iter->nodetree->nodes) { if (node->id == (ID *)scene || node->type == CMP_NODE_COMPOSITE) { - nodeUpdate(sce_iter->nodetree, node); + BKE_ntree_update_tag_node_property(sce_iter->nodetree, node); } else if (node->type == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ { - nodeUpdate(sce_iter->nodetree, node); + BKE_ntree_update_tag_node_property(sce_iter->nodetree, node); } } } } + BKE_ntree_update_main(G_MAIN, nullptr); } /* XXX after render animation system gets a refresh, this call allows composite to end clean */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 21bee2577b2..98480971fbd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -112,35 +112,26 @@ static void node_update(bNodeTree *ntree, bNode *node) bNodeSocket *p3 = p2->next; bNodeSocket *p4 = p3->next; - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - nodeSetSocketAvailability(ntree, sock, false); - } + Vector<bNodeSocket *> available_sockets; if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE) { - nodeSetSocketAvailability(ntree, width, true); - nodeSetSocketAvailability(ntree, height, true); + available_sockets.extend({width, height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM) { - nodeSetSocketAvailability(ntree, width, true); - nodeSetSocketAvailability(ntree, height, true); - nodeSetSocketAvailability(ntree, offset, true); + available_sockets.extend({width, height, offset}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID) { - nodeSetSocketAvailability(ntree, bottom, true); - nodeSetSocketAvailability(ntree, top, true); - nodeSetSocketAvailability(ntree, offset, true); - nodeSetSocketAvailability(ntree, height, true); + available_sockets.extend({bottom, top, offset, height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE) { - nodeSetSocketAvailability(ntree, width, true); - nodeSetSocketAvailability(ntree, bottom_height, true); - nodeSetSocketAvailability(ntree, top_height, true); + available_sockets.extend({width, bottom_height, top_height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS) { - nodeSetSocketAvailability(ntree, p1, true); - nodeSetSocketAvailability(ntree, p2, true); - nodeSetSocketAvailability(ntree, p3, true); - nodeSetSocketAvailability(ntree, p4, true); + available_sockets.extend({p1, p2, p3, p4}); + } + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + nodeSetSocketAvailability(ntree, sock, available_sockets.contains(sock)); } } diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index c302b1081af..a1fccc401a4 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -38,6 +38,7 @@ #include "BLT_translation.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "RNA_types.h" @@ -153,8 +154,6 @@ static void update_socket_to_match_interface(bNodeTree &node_tree, /* Update socket type if necessary */ if (socket_to_update.typeinfo != interface_socket.typeinfo) { nodeModifySocketType(&node_tree, &node, &socket_to_update, interface_socket.idname); - /* Flag the tree to make sure link validity is updated after type changes. */ - node_tree.update |= NTREE_UPDATE_LINKS; } if (interface_socket.typeinfo->interface_verify_socket) { diff --git a/source/blender/nodes/intern/node_exec.cc b/source/blender/nodes/intern/node_exec.cc index 95070bf735e..f5b64f8499c 100644 --- a/source/blender/nodes/intern/node_exec.cc +++ b/source/blender/nodes/intern/node_exec.cc @@ -28,6 +28,7 @@ #include "BKE_global.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "MEM_guardedalloc.h" @@ -170,7 +171,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context, /* Using global main here is likely totally wrong, not sure what to do about that one though... * We cannot even check ntree is in global main, * since most of the time it won't be (thanks to ntree design)!!! */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); /* get a dependency-sorted list of nodes */ ntreeGetDependencyList(ntree, &nodelist, &totnodes); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index d83c05b38a1..46cb9dcf891 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -236,6 +236,14 @@ static void refresh_socket_list(bNodeTree &ntree, link->tosock = new_socket; } } + LISTBASE_FOREACH (bNodeLink *, internal_link, &node.internal_links) { + if (internal_link->fromsock == old_socket_with_same_identifier) { + internal_link->fromsock = new_socket; + } + else if (internal_link->tosock == old_socket_with_same_identifier) { + internal_link->tosock = new_socket; + } + } } } } diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 912d5e5322c..ffe0edb9762 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -70,6 +70,8 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) break; } } + BLI_assert(internal_link.from_ != nullptr); + BLI_assert(internal_link.to_ != nullptr); node.internal_links_.append(&internal_link); } diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 7620c8fa1a8..5c2d84cf605 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -35,6 +35,7 @@ #include "BKE_colortools.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "RNA_access.h" #include "RNA_enum_types.h" @@ -340,188 +341,6 @@ void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Internal Links (mute and disconnect) - * \{ */ - -/** - * Common datatype priorities, works for compositor, shader and texture nodes alike - * defines priority of datatype connection based on output type (to): - * `< 0`: never connect these types. - * `>= 0`: priority of connection (higher values chosen first). - */ -static int node_datatype_priority(const bNodeSocketType *from, const bNodeSocketType *to) -{ - switch (to->type) { - case SOCK_RGBA: - switch (from->type) { - case SOCK_RGBA: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_INT: - return 2; - case SOCK_BOOLEAN: - return 1; - } - return -1; - case SOCK_VECTOR: - switch (from->type) { - case SOCK_VECTOR: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_INT: - return 2; - case SOCK_BOOLEAN: - return 1; - } - return -1; - case SOCK_FLOAT: - switch (from->type) { - case SOCK_FLOAT: - return 5; - case SOCK_INT: - return 4; - case SOCK_BOOLEAN: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - } - return -1; - case SOCK_INT: - switch (from->type) { - case SOCK_INT: - return 5; - case SOCK_FLOAT: - return 4; - case SOCK_BOOLEAN: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - } - return -1; - case SOCK_BOOLEAN: - switch (from->type) { - case SOCK_BOOLEAN: - return 5; - case SOCK_INT: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - } - return -1; - } - - /* The rest of the socket types only allow an internal link if both the input and output socket - * have the same type. If the sockets are custom, we check the idname instead. */ - if (to->type == from->type && (to->type != SOCK_CUSTOM || STREQ(to->idname, from->idname))) { - return 1; - } - - return -1; -} - -/* select a suitable input socket for an output */ -static bNodeSocket *select_internal_link_input(bNode *node, bNodeSocket *output) -{ - if (node->type == NODE_REROUTE) { - return node->inputs.first; - } - - bNodeSocket *selected = NULL, *input; - int i; - int sel_priority = -1; - bool sel_is_linked = false; - - for (input = node->inputs.first, i = 0; input; input = input->next, i++) { - int priority = node_datatype_priority(input->typeinfo, output->typeinfo); - bool is_linked = (input->link != NULL); - bool preferred; - - if (nodeSocketIsHidden(input) || /* ignore hidden sockets */ - input->flag & - SOCK_NO_INTERNAL_LINK || /* ignore if input is not allowed for internal connections */ - priority < 0 || /* ignore incompatible types */ - priority < sel_priority) /* ignore if we already found a higher priority input */ - { - continue; - } - - /* determine if this input is preferred over the currently selected */ - preferred = (priority > sel_priority) || /* prefer higher datatype priority */ - (is_linked && !sel_is_linked); /* prefer linked over unlinked */ - - if (preferred) { - selected = input; - sel_is_linked = is_linked; - sel_priority = priority; - } - } - - return selected; -} - -void node_internal_links_create(bNodeTree *ntree, bNode *node) -{ - bNodeLink *link; - bNodeSocket *output, *input; - - /* sanity check */ - if (!ntree) { - return; - } - - /* use link pointer as a tag for handled sockets (for outputs is unused anyway) */ - for (output = node->outputs.first; output; output = output->next) { - output->link = NULL; - } - - for (link = ntree->links.first; link; link = link->next) { - if (nodeLinkIsHidden(link)) { - continue; - } - - output = link->fromsock; - if (link->fromnode != node || output->link) { - continue; - } - if (nodeSocketIsHidden(output) || output->flag & SOCK_NO_INTERNAL_LINK) { - continue; - } - output->link = link; /* not really used, just for tagging handled sockets */ - - /* look for suitable input */ - input = select_internal_link_input(node, output); - - if (input) { - bNodeLink *ilink = MEM_callocN(sizeof(bNodeLink), "internal node link"); - ilink->fromnode = node; - ilink->fromsock = input; - ilink->tonode = node; - ilink->tosock = output; - /* internal link is always valid */ - ilink->flag |= NODE_LINK_VALID; - BLI_addtail(&node->internal_links, ilink); - } - } - - /* clean up */ - for (output = node->outputs.first; output; output = output->next) { - output->link = NULL; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Default value RNA access * \{ */ diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index b79dbe3f3f8..b19835fae19 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -44,6 +44,7 @@ #include "BKE_lib_id.h" #include "BKE_linestyle.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_scene.h" #include "RNA_access.h" @@ -153,11 +154,6 @@ static void update(bNodeTree *ntree) ntreeSetOutput(ntree); ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } static bool shader_validate_link(eNodeSocketDatatype from, eNodeSocketDatatype to) @@ -352,7 +348,7 @@ static void ntree_shader_unlink_hidden_value_sockets(bNode *group_node, bNodeSoc } if (removed_link) { - ntreeUpdateTree(G.main, group_ntree); + BKE_ntree_update_main_tree(G.main, group_ntree, NULL); } } @@ -411,7 +407,7 @@ static void ntree_shader_groups_expand_inputs(bNodeTree *localtree) } if (link_added) { - ntreeUpdateTree(G.main, localtree); + BKE_ntree_update_main_tree(G.main, localtree, NULL); } } @@ -491,7 +487,7 @@ static void flatten_group_do(bNodeTree *ntree, bNode *gnode) ntreeFreeLocalNode(ntree, node); } - ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_all(ntree); } /* Flatten group to only have a simple single tree */ @@ -516,7 +512,7 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree) } } - ntreeUpdateTree(G.main, localtree); + BKE_ntree_update_main_tree(G.main, localtree, NULL); } /* Check whether shader has a displacement. @@ -536,7 +532,7 @@ static bool ntree_shader_has_displacement(bNodeTree *ntree, return false; } /* Make sure sockets links pointers are correct. */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); bNodeSocket *displacement = ntree_shader_node_find_input(output_node, "Displacement"); if (displacement == NULL) { @@ -625,7 +621,7 @@ static void ntree_shader_bypass_tagged_bump_nodes(bNodeTree *ntree) ntree_shader_bypass_bump_link(ntree, node, link); } } - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); } static bool ntree_branch_count_and_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata) @@ -710,7 +706,7 @@ static void ntree_shader_copy_branch_displacement(bNodeTree *ntree, nodeRemLink(ntree, displacement_link); nodeAddLink(ntree, displacement_node, displacement_socket, tonode, tosock); - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); } /* Re-link displacement output to unconnected normal sockets via bump node. @@ -774,12 +770,12 @@ static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_nod geo_node->tmp_flag = -2; bump_node->tmp_flag = -2; - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); /* Connect all free-standing Normal inputs and relink geometry/coordinate nodes. */ ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket); /* We modified the tree, it needs to be updated now. */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); } static void node_tag_branch_as_derivative(bNode *node, int dx) @@ -859,7 +855,7 @@ void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tag return; } /* Make sure sockets links pointers are correct. */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); nodeChainIterBackwards(ntree, output_node, ntree_tag_bsdf_cb, tags, 0); } diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 30ad0d1d629..1125936aded 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -135,11 +135,6 @@ static void localize(bNodeTree *UNUSED(localtree), bNodeTree *UNUSED(ntree)) static void update(bNodeTree *ntree) { ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } static bool texture_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 44abbbdf074..721abe45932 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1131,7 +1131,7 @@ static void do_render_compositor_scenes(Render *re) render_scene_has_layers_to_render(scene, false)) { do_render_compositor_scene(re, scene, cfra); BLI_gset_add(scenes_rendered, scene); - nodeUpdate(restore_scene->nodetree, node); + node->typeinfo->updatefunc(restore_scene->nodetree, node); } } } |