Nodes: avoid slow and unecessary node group updates on file read

On file read we need to update group nodes in case the group they refer to
has changed its inputs and outputs. This had O(n^2) time complexity and was
updating all datablocks even if they did not change.
This commit is contained in:
2019-04-20 20:25:22 +02:00
parent 62421470ee
commit d730e512ac
20 changed files with 119 additions and 124 deletions

View File

@@ -218,7 +218,7 @@ typedef struct bNodeType {
/// Called when the node is updated in the editor.
void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node);
/// Check and update if internal ID data has changed.
void (*verifyfunc)(struct bNodeTree *ntree, struct bNode *node, struct ID *id);
void (*group_update_func)(struct bNodeTree *ntree, struct bNode *node);
/// Initialize a new node instance of this type after creation.
void (*initfunc)(struct bNodeTree *ntree, struct bNode *node);
@@ -395,10 +395,8 @@ 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);
/* XXX Currently each tree update call does call to ntreeVerifyNodes too.
* Some day this should be replaced by a decent depsgraph automatism!
*/
void ntreeVerifyNodes(struct Main *main, struct ID *id);
void ntreeUpdateAllNew(struct Main *main);
void ntreeUpdateAllUsers(struct Main *main, struct ID *id);
void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***deplist, int *totnodes);
@@ -736,10 +734,10 @@ void node_type_label(
struct bNodeType *ntype,
void (*labelfunc)(struct bNodeTree *ntree, struct bNode *, char *label, int maxlen));
void node_type_update(struct bNodeType *ntype,
void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node),
void (*verifyfunc)(struct bNodeTree *ntree,
struct bNode *node,
struct ID *id));
void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node));
void node_type_group_update(struct bNodeType *ntype,
void (*group_update_func)(struct bNodeTree *ntree,
struct bNode *node));
void node_type_exec(struct bNodeType *ntype,
NodeInitExecFunction initexecfunc,

View File

@@ -387,15 +387,8 @@ 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)
{
/* Verify all nodetree user nodes. */
ntreeVerifyNodes(bmain, new_id);
/* Update node trees as necessary. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
/* make an update call for the tree */
ntreeUpdateTree(bmain, ntree);
}
FOREACH_NODETREE_END;
/* Update all group nodes using a node group. */
ntreeUpdateAllUsers(bmain, new_id);
}
/**

View File

@@ -3206,16 +3206,44 @@ static void ntree_validate_links(bNodeTree *ntree)
}
}
void ntreeVerifyNodes(struct Main *main, struct ID *id)
void ntreeUpdateAllNew(Main *main)
{
/* 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) {
bNode *node;
for (node = ntree->nodes.first; node; node = node->next) {
if (node->typeinfo->verifyfunc) {
node->typeinfo->verifyfunc(ntree, node, id);
if (owner_id->tag & LIB_TAG_NEW) {
for (bNode *node = ntree->nodes.first; node; node = node->next) {
if (node->typeinfo->group_update_func) {
node->typeinfo->group_update_func(ntree, node);
}
}
ntreeUpdateTree(NULL, ntree);
}
}
FOREACH_NODETREE_END;
}
void ntreeUpdateAllUsers(Main *main, ID *ngroup)
{
/* Update all users of ngroup, to add/remove sockets as needed. */
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
bool need_update = false;
for (bNode *node = ntree->nodes.first; node; node = node->next) {
if (node->id == ngroup) {
if (node->typeinfo->group_update_func) {
node->typeinfo->group_update_func(ntree, node);
}
need_update = true;
}
}
if (need_update) {
ntreeUpdateTree(NULL, ntree);
}
}
FOREACH_NODETREE_END;
}
@@ -3264,7 +3292,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
/* XXX hack, should be done by depsgraph!! */
if (bmain) {
ntreeVerifyNodes(bmain, &ntree->id);
ntreeUpdateAllUsers(bmain, &ntree->id);
}
if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) {
@@ -3581,13 +3609,15 @@ void node_type_label(
}
void node_type_update(struct bNodeType *ntype,
void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node),
void (*verifyfunc)(struct bNodeTree *ntree,
struct bNode *node,
struct ID *id))
void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
{
ntype->updatefunc = updatefunc;
ntype->verifyfunc = verifyfunc;
}
void node_type_group_update(struct bNodeType *ntype,
void (*group_update_func)(struct bNodeTree *ntree, struct bNode *node))
{
ntype->group_update_func = group_update_func;
}
void node_type_exec(struct bNodeType *ntype,

View File

@@ -3454,26 +3454,6 @@ static void lib_link_nodetree(FileData *fd, Main *main)
}
}
/* Verify group nodes have sockets matching their node groups. All data has
* to be read and versioned at this point, since this accesses data across
* files. */
static void lib_verify_nodetree(Main *main)
{
/* verify all group user nodes */
for (bNodeTree *ntree = main->nodetrees.first; ntree; ntree = ntree->id.next) {
ntreeVerifyNodes(main, &ntree->id);
}
/* make update calls where necessary */
{
FOREACH_NODETREE_BEGIN (main, ntree, id) {
/* make an update call for the tree */
ntreeUpdateTree(main, ntree);
}
FOREACH_NODETREE_END;
}
}
static void direct_link_node_socket(FileData *fd, bNodeSocket *sock)
{
sock->prop = newdataadr(fd, sock->prop);
@@ -9734,13 +9714,13 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
do_versions_after_linking(mainvar);
}
blo_join_main(&mainlist);
/* After all data has been read and versioned, uses LIB_TAG_NEW. */
ntreeUpdateAllNew(bfd->main);
}
BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false);
/* After all data has been read and versioned. */
lib_verify_nodetree(bfd->main);
/* Now that all our data-blocks are loaded, we can re-generate overrides from their references. */
if (fd->memfile == NULL) {
/* Do not apply in undo case! */
@@ -11508,9 +11488,11 @@ static void library_link_end(Main *mainl,
mainvar = (*fd)->mainlist->first;
MEM_freeN((*fd)->mainlist);
/* After all data has been read and versioned, uses LIB_TAG_NEW. */
ntreeUpdateAllNew(mainvar);
BKE_main_id_tag_all(mainvar, LIB_TAG_NEW, false);
lib_verify_nodetree(mainvar);
fix_relpaths_library(BKE_main_blendfile_path(mainvar),
mainvar); /* make all relative paths, relative to the open blend file */

View File

@@ -823,8 +823,8 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree,
/* update the group node and interface node sockets,
* so the new interface socket can be linked.
*/
node_group_verify(ntree, gnode, (ID *)ngroup);
node_group_input_verify(ngroup, input_node, (ID *)ngroup);
node_group_update(ntree, gnode);
node_group_input_update(ngroup, input_node);
/* create new internal link */
input_sock = node_group_input_find_socket(input_node, iosock->identifier);
@@ -857,8 +857,8 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree,
/* update the group node and interface node sockets,
* so the new interface socket can be linked.
*/
node_group_verify(ntree, gnode, (ID *)ngroup);
node_group_output_verify(ngroup, output_node, (ID *)ngroup);
node_group_update(ntree, gnode);
node_group_output_update(ngroup, output_node);
/* create new internal link */
output_sock = node_group_output_find_socket(output_node, iosock->identifier);
@@ -898,7 +898,7 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree,
iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
node_group_input_verify(ngroup, input_node, (ID *)ngroup);
node_group_input_update(ngroup, input_node);
/* create new internal link */
input_sock = node_group_input_find_socket(input_node, iosock->identifier);
@@ -919,7 +919,7 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree,
iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
node_group_output_verify(ngroup, output_node, (ID *)ngroup);
node_group_output_update(ngroup, output_node);
/* create new internal link */
output_sock = node_group_output_find_socket(output_node, iosock->identifier);

View File

@@ -2597,7 +2597,7 @@ static StructRNA *rna_NodeCustomGroup_register(Main *bmain,
return NULL;
/* this updates the group node instance from the tree's interface */
nt->verifyfunc = node_group_verify;
nt->group_update_func = node_group_update;
nodeRegisterType(nt);
@@ -2621,7 +2621,7 @@ static StructRNA *rna_ShaderNodeCustomGroup_register(Main *bmain,
if (!nt)
return NULL;
nt->verifyfunc = node_group_verify;
nt->group_update_func = node_group_update;
nt->type = NODE_CUSTOM_GROUP;
register_node_type_sh_custom_group(nt);
@@ -2646,7 +2646,7 @@ static StructRNA *rna_CompositorNodeCustomGroup_register(Main *bmain,
if (!nt)
return NULL;
nt->verifyfunc = node_group_verify;
nt->group_update_func = node_group_update;
nt->type = NODE_CUSTOM_GROUP;
register_node_type_cmp_custom_group(nt);

View File

@@ -35,11 +35,11 @@ void register_node_type_group_output(void);
/* internal functions for editor */
struct bNodeSocket *node_group_find_input_socket(struct bNode *groupnode, const char *identifier);
struct bNodeSocket *node_group_find_output_socket(struct bNode *groupnode, const char *identifier);
void node_group_verify(struct bNodeTree *ntree, struct bNode *node, struct ID *id);
void node_group_update(struct bNodeTree *ntree, struct bNode *node);
struct bNodeSocket *node_group_input_find_socket(struct bNode *node, const char *identifier);
struct bNodeSocket *node_group_output_find_socket(struct bNode *node, const char *identifier);
void node_group_input_verify(struct bNodeTree *ntree, struct bNode *node, struct ID *id);
void node_group_output_verify(struct bNodeTree *ntree, struct bNode *node, struct ID *id);
void node_group_input_update(struct bNodeTree *ntree, struct bNode *node);
void node_group_output_update(struct bNodeTree *ntree, struct bNode *node);
#endif /* __NOD_COMMON_H__ */

View File

@@ -53,7 +53,7 @@ void register_node_type_cmp_group(void)
node_type_socket_templates(&ntype, NULL, NULL);
node_type_size(&ntype, 140, 60, 400);
node_type_label(&ntype, node_group_label);
node_type_update(&ntype, NULL, node_group_verify);
node_type_group_update(&ntype, node_group_update);
nodeRegisterType(&ntype);
}

View File

@@ -446,7 +446,7 @@ void register_node_type_cmp_image(void)
cmp_node_type_base(&ntype, CMP_NODE_IMAGE, "Image", NODE_CLASS_INPUT, NODE_PREVIEW);
node_type_init(&ntype, node_composit_init_image);
node_type_storage(&ntype, "ImageUser", node_composit_free_image, node_composit_copy_image);
node_type_update(&ntype, cmp_node_image_update, NULL);
node_type_update(&ntype, cmp_node_image_update);
node_type_label(&ntype, node_image_label);
nodeRegisterType(&ntype);
@@ -580,7 +580,7 @@ void register_node_type_cmp_rlayers(void)
ntype.initfunc_api = node_composit_init_rlayers;
ntype.poll = node_composit_poll_rlayers;
node_type_storage(&ntype, NULL, node_composit_free_rlayers, node_composit_copy_rlayers);
node_type_update(&ntype, cmp_node_rlayers_update, NULL);
node_type_update(&ntype, cmp_node_rlayers_update);
node_type_init(&ntype, node_cmp_rlayers_outputs);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);

View File

@@ -281,7 +281,7 @@ void register_node_type_cmp_output_file(void)
node_type_socket_templates(&ntype, NULL, NULL);
ntype.initfunc_api = init_output_file;
node_type_storage(&ntype, "NodeImageMultiFile", free_output_file, copy_output_file);
node_type_update(&ntype, update_output_file, NULL);
node_type_update(&ntype, update_output_file);
nodeRegisterType(&ntype);
}

View File

@@ -56,7 +56,7 @@ void register_node_type_cmp_scale(void)
cmp_node_type_base(&ntype, CMP_NODE_SCALE, "Scale", NODE_CLASS_DISTORT, 0);
node_type_socket_templates(&ntype, cmp_node_scale_in, cmp_node_scale_out);
node_type_update(&ntype, node_composite_update_scale, NULL);
node_type_update(&ntype, node_composite_update_scale);
nodeRegisterType(&ntype);
}

View File

@@ -148,7 +148,7 @@ void register_node_type_cmp_switch_view(void)
ntype.initfunc_api = init_switch_view;
node_type_update(&ntype, cmp_node_switch_view_update, NULL);
node_type_update(&ntype, cmp_node_switch_view_update);
nodeRegisterType(&ntype);
}

View File

@@ -179,11 +179,10 @@ static void group_verify_socket_list(
}
/* make sure all group node in ntree, which use ngroup, are sync'd */
void node_group_verify(struct bNodeTree *ntree, struct bNode *node, struct ID *id)
void node_group_update(struct bNodeTree *ntree, struct bNode *node)
{
/* check inputs and outputs, and remove or insert them */
if (id == node->id) {
if (id == NULL) {
if (node->id == NULL) {
nodeRemoveAllSockets(ntree, node);
}
else {
@@ -191,7 +190,6 @@ void node_group_verify(struct bNodeTree *ntree, struct bNode *node, struct ID *i
group_verify_socket_list(ntree, node, &ngroup->inputs, &node->inputs, SOCK_IN);
group_verify_socket_list(ntree, node, &ngroup->outputs, &node->outputs, SOCK_OUT);
}
}
}
/**** FRAME ****/
@@ -408,7 +406,7 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree)
static void node_group_input_init(bNodeTree *ntree, bNode *node)
{
node_group_input_verify(ntree, node, (ID *)ntree);
node_group_input_update(ntree, node);
}
bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier)
@@ -422,19 +420,7 @@ bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier)
return NULL;
}
void node_group_input_verify(bNodeTree *ntree, bNode *node, ID *id)
{
/* check inputs and outputs, and remove or insert them */
if (id == (ID *)ntree) {
/* value_in_out inverted for interface nodes to get correct socket value_property */
group_verify_socket_list(ntree, node, &ntree->inputs, &node->outputs, SOCK_OUT);
/* add virtual extension socket */
nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketVirtual", "__extend__", "");
}
}
static void node_group_input_update(bNodeTree *ntree, bNode *node)
void node_group_input_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *extsock = node->outputs.last;
bNodeLink *link, *linknext, *exposelink;
@@ -480,7 +466,7 @@ static void node_group_input_update(bNodeTree *ntree, bNode *node)
gsock = ntreeAddSocketInterfaceFromSocket(ntree, exposelink->tonode, exposelink->tosock);
node_group_input_verify(ntree, node, (ID *)ntree);
node_group_input_update(ntree, node);
newsock = node_group_input_find_socket(node, gsock->identifier);
/* redirect links from the extension socket */
@@ -490,6 +476,15 @@ static void node_group_input_update(bNodeTree *ntree, bNode *node)
}
BLI_freelistN(&tmplinks);
/* check inputs and outputs, and remove or insert them */
{
/* value_in_out inverted for interface nodes to get correct socket value_property */
group_verify_socket_list(ntree, node, &ntree->inputs, &node->outputs, SOCK_OUT);
/* add virtual extension socket */
nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketVirtual", "__extend__", "");
}
}
void register_node_type_group_input(void)
@@ -500,7 +495,7 @@ void register_node_type_group_input(void)
node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE, 0);
node_type_size(ntype, 140, 80, 400);
node_type_init(ntype, node_group_input_init);
node_type_update(ntype, node_group_input_update, node_group_input_verify);
node_type_update(ntype, node_group_input_update);
ntype->needs_free = 1;
nodeRegisterType(ntype);
@@ -508,7 +503,7 @@ void register_node_type_group_input(void)
static void node_group_output_init(bNodeTree *ntree, bNode *node)
{
node_group_output_verify(ntree, node, (ID *)ntree);
node_group_output_update(ntree, node);
}
bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier)
@@ -522,19 +517,7 @@ bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier)
return NULL;
}
void node_group_output_verify(bNodeTree *ntree, bNode *node, ID *id)
{
/* check inputs and outputs, and remove or insert them */
if (id == (ID *)ntree) {
/* value_in_out inverted for interface nodes to get correct socket value_property */
group_verify_socket_list(ntree, node, &ntree->outputs, &node->inputs, SOCK_IN);
/* add virtual extension socket */
nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketVirtual", "__extend__", "");
}
}
static void node_group_output_update(bNodeTree *ntree, bNode *node)
void node_group_output_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *extsock = node->inputs.last;
bNodeLink *link, *linknext, *exposelink;
@@ -581,7 +564,7 @@ static void node_group_output_update(bNodeTree *ntree, bNode *node)
/* XXX what if connecting virtual to virtual socket?? */
gsock = ntreeAddSocketInterfaceFromSocket(ntree, exposelink->fromnode, exposelink->fromsock);
node_group_output_verify(ntree, node, (ID *)ntree);
node_group_output_update(ntree, node);
newsock = node_group_output_find_socket(node, gsock->identifier);
/* redirect links to the extension socket */
@@ -591,6 +574,15 @@ static void node_group_output_update(bNodeTree *ntree, bNode *node)
}
BLI_freelistN(&tmplinks);
/* check inputs and outputs, and remove or insert them */
{
/* value_in_out inverted for interface nodes to get correct socket value_property */
group_verify_socket_list(ntree, node, &ntree->outputs, &node->inputs, SOCK_IN);
/* add virtual extension socket */
nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketVirtual", "__extend__", "");
}
}
void register_node_type_group_output(void)
@@ -601,7 +593,7 @@ void register_node_type_group_output(void)
node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE, 0);
node_type_size(ntype, 140, 80, 400);
node_type_init(ntype, node_group_output_init);
node_type_update(ntype, node_group_output_update, node_group_output_verify);
node_type_update(ntype, node_group_output_update);
ntype->needs_free = 1;
nodeRegisterType(ntype);

View File

@@ -235,8 +235,8 @@ static bNode *ntree_shader_relink_output_from_group(bNodeTree *ntree,
}
/* Need to update tree so all node instances nodes gets proper sockets. */
node_group_verify(ntree, group_node, &group_ntree->id);
node_group_output_verify(group_ntree, group_output_node, &group_ntree->id);
node_group_update(ntree, group_node);
node_group_output_update(group_ntree, group_output_node);
ntreeUpdateTree(G.main, group_ntree);
/* Remove other shader output nodes so that only the new one can be selected as active. */
@@ -576,9 +576,9 @@ static void ntree_shader_link_builtin_group_normal(bNodeTree *ntree,
group_ntree, SOCK_IN, "NodeSocketVector", "Normal");
/* Need to update tree so all node instances nodes gets proper sockets. */
bNode *group_input_node = ntreeFindType(group_ntree, NODE_GROUP_INPUT);
node_group_verify(ntree, group_node, &group_ntree->id);
node_group_update(ntree, group_node);
if (group_input_node) {
node_group_input_verify(group_ntree, group_input_node, &group_ntree->id);
node_group_input_update(group_ntree, group_input_node);
}
ntreeUpdateTree(G.main, group_ntree);
/* Assumes sockets are always added at the end. */

View File

@@ -129,7 +129,7 @@ void register_node_type_sh_bsdf_hair_principled(void)
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_init(&ntype, node_shader_init_hair_principled);
node_type_storage(&ntype, "", NULL, NULL);
node_type_update(&ntype, node_shader_update_hair_principled, NULL);
node_type_update(&ntype, node_shader_update_hair_principled);
nodeRegisterType(&ntype);
}

View File

@@ -217,7 +217,7 @@ void register_node_type_sh_bsdf_principled(void)
node_type_init(&ntype, node_shader_init_principled);
node_type_storage(&ntype, "", NULL, NULL);
node_type_gpu(&ntype, node_shader_gpu_bsdf_principled);
node_type_update(&ntype, node_shader_update_principled, NULL);
node_type_update(&ntype, node_shader_update_principled);
nodeRegisterType(&ntype);
}

View File

@@ -245,7 +245,7 @@ void register_node_type_sh_group(void)
node_type_socket_templates(&ntype, NULL, NULL);
node_type_size(&ntype, 140, 60, 400);
node_type_label(&ntype, node_group_label);
node_type_update(&ntype, NULL, node_group_verify);
node_type_group_update(&ntype, node_group_update);
node_type_exec(&ntype, group_initexec, group_freeexec, group_execute);
node_type_gpu(&ntype, gpu_group_execute);

View File

@@ -107,7 +107,7 @@ void register_node_type_sh_subsurface_scattering(void)
node_type_init(&ntype, node_shader_init_subsurface_scattering);
node_type_storage(&ntype, "", NULL, NULL);
node_type_gpu(&ntype, node_shader_gpu_subsurface_scattering);
node_type_update(&ntype, node_shader_update_subsurface_scattering, NULL);
node_type_update(&ntype, node_shader_update_subsurface_scattering);
nodeRegisterType(&ntype);
}

View File

@@ -122,7 +122,7 @@ void register_node_type_sh_tex_voronoi(void)
node_type_storage(
&ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_voronoi);
node_type_update(&ntype, node_shader_update_tex_voronoi, NULL);
node_type_update(&ntype, node_shader_update_tex_voronoi);
nodeRegisterType(&ntype);
}

View File

@@ -174,7 +174,7 @@ void register_node_type_tex_group(void)
node_type_socket_templates(&ntype, NULL, NULL);
node_type_size(&ntype, 140, 60, 400);
node_type_label(&ntype, node_group_label);
node_type_update(&ntype, NULL, node_group_verify);
node_type_group_update(&ntype, node_group_update);
node_type_exec(&ntype, group_initexec, group_freeexec, group_execute);
nodeRegisterType(&ntype);