/** * ***** BEGIN GPL LICENSE BLOCK ***** * * 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. * * The Original Code is Copyright (C) 2007 Blender Foundation. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/nodes/composite/node_composite_tree.c * \ingroup nodes */ #include #include "DNA_anim_types.h" #include "DNA_scene_types.h" #include "DNA_node_types.h" #include "BLI_listbase.h" #include "BLI_threads.h" #include "BKE_animsys.h" #include "BKE_colortools.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_utildefines.h" #include "node_exec.h" #include "node_util.h" #include "PIL_time.h" #include "RNA_access.h" #include "NOD_composite.h" #include "node_composite_util.h" static void foreach_nodetree(Main *main, void *calldata, bNodeTreeCallback func) { Scene *sce; for(sce= main->scene.first; sce; sce= sce->id.next) { if(sce->nodetree) { func(calldata, &sce->id, sce->nodetree); } } } static void free_node_cache(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sock; for(sock= node->outputs.first; sock; sock= sock->next) { if(sock->cache) { free_compbuf(sock->cache); sock->cache= NULL; } } } static void free_cache(bNodeTree *ntree) { bNode *node; for(node= ntree->nodes.first; node; node= node->next) free_node_cache(ntree, node); } static void update_node(bNodeTree *ntree, bNode *node) { bNodeSocket *sock; for(sock= node->outputs.first; sock; sock= sock->next) { if(sock->cache) { //free_compbuf(sock->cache); //sock->cache= NULL; } } node->need_exec= 1; /* individual node update call */ if (node->typeinfo->updatefunc) node->typeinfo->updatefunc(ntree, node); } /* local tree then owns all compbufs */ static void localize(bNodeTree *UNUSED(localtree), bNodeTree *ntree) { bNode *node; bNodeSocket *sock; for(node= ntree->nodes.first; node; node= node->next) { /* ensure new user input gets handled ok */ node->need_exec= 0; /* move over the compbufs */ /* right after ntreeCopyTree() oldsock pointers are valid */ if(ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if(node->id) { if(node->flag & NODE_DO_OUTPUT) node->new_node->id= (ID *)copy_image((Image *)node->id); else node->new_node->id= NULL; } } for(sock= node->outputs.first; sock; sock= sock->next) { sock->new_sock->cache= sock->cache; compbuf_set_node(sock->new_sock->cache, node->new_node); sock->cache= NULL; sock->new_sock->new_sock= sock; } } } static void local_sync(bNodeTree *localtree, bNodeTree *ntree) { bNode *lnode; /* move over the compbufs and previews */ for(lnode= localtree->nodes.first; lnode; lnode= lnode->next) { if( (lnode->exec & NODE_READY) && !(lnode->exec & NODE_SKIPPED) ) { if(ntreeNodeExists(ntree, lnode->new_node)) { if(lnode->preview && lnode->preview->rect) { nodeFreePreview(lnode->new_node); lnode->new_node->preview= lnode->preview; lnode->preview= NULL; } } } } } static void local_merge(bNodeTree *localtree, bNodeTree *ntree) { bNode *lnode; bNodeSocket *lsock; /* move over the compbufs and previews */ for(lnode= localtree->nodes.first; lnode; lnode= lnode->next) { if(ntreeNodeExists(ntree, lnode->new_node)) { if(ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if(lnode->id && (lnode->flag & NODE_DO_OUTPUT)) { /* image_merge does sanity check for pointers */ BKE_image_merge((Image *)lnode->new_node->id, (Image *)lnode->id); } } for(lsock= lnode->outputs.first; lsock; lsock= lsock->next) { if(ntreeOutputExists(lnode->new_node, lsock->new_sock)) { lsock->new_sock->cache= lsock->cache; compbuf_set_node(lsock->new_sock->cache, lnode->new_node); lsock->cache= NULL; lsock->new_sock= NULL; } } } } } bNodeTreeType ntreeType_Composite = { /* type */ NTREE_COMPOSIT, /* idname */ "NTCompositing Nodetree", /* node_types */ { NULL, NULL }, /* free_cache */ free_cache, /* free_node_cache */ free_node_cache, /* foreach_nodetree */ foreach_nodetree, /* localize */ localize, /* local_sync */ local_sync, /* local_merge */ local_merge, /* update */ NULL, /* update_node */ update_node }; struct bNodeTreeExec *ntreeCompositBeginExecTree(bNodeTree *ntree) { bNodeTreeExec *exec; bNode *node; bNodeSocket *sock; /* XXX hack: prevent exec data from being generated twice. * this should be handled by the renderer! */ if (ntree->execdata) return ntree->execdata; /* ensures only a single output node is enabled */ ntreeSetOutput(ntree); exec = ntree_exec_begin(ntree); for(node= exec->nodetree->nodes.first; node; node= node->next) { /* initialize needed for groups */ node->exec= 0; for(sock= node->outputs.first; sock; sock= sock->next) { bNodeStack *ns= node_get_socket_stack(exec->stack, sock); if(ns && sock->cache) { ns->data= sock->cache; sock->cache= NULL; } } /* cannot initialize them while using in threads */ if(ELEM4(node->type, CMP_NODE_TIME, CMP_NODE_CURVE_VEC, CMP_NODE_CURVE_RGB, CMP_NODE_HUECORRECT)) { curvemapping_initialize(node->storage); if(node->type==CMP_NODE_CURVE_RGB) curvemapping_premultiply(node->storage, 0); } } /* XXX this should not be necessary, but is still used for cmp/sha/tex nodes, * which only store the ntree pointer. Should be fixed at some point! */ ntree->execdata = exec; return exec; } void ntreeCompositEndExecTree(bNodeTreeExec *exec) { if(exec) { bNodeTree *ntree= exec->nodetree; bNode *node; bNodeStack *ns; for(node= exec->nodetree->nodes.first; node; node= node->next) { bNodeSocket *sock; for(sock= node->outputs.first; sock; sock= sock->next) { ns = node_get_socket_stack(exec->stack, sock); if(ns && ns->data) { sock->cache= ns->data; ns->data= NULL; } } if(node->type==CMP_NODE_CURVE_RGB) curvemapping_premultiply(node->storage, 1); node->need_exec= 0; } ntree_exec_end(exec); /* XXX clear nodetree backpointer to exec data, same problem as noted in ntreeBeginExecTree */ ntree->execdata = NULL; } } /* ***************************** threaded version for execute composite nodes ************* */ /* these are nodes without input, only giving values */ /* or nodes with only value inputs */ static int node_only_value(bNode *node) { bNodeSocket *sock; if(ELEM3(node->type, CMP_NODE_TIME, CMP_NODE_VALUE, CMP_NODE_RGB)) return 1; /* doing this for all node types goes wrong. memory free errors */ if(node->inputs.first && node->type==CMP_NODE_MAP_VALUE) { int retval= 1; for(sock= node->inputs.first; sock; sock= sock->next) { if(sock->link) retval &= node_only_value(sock->link->fromnode); } return retval; } return 0; } /* not changing info, for thread callback */ typedef struct ThreadData { bNodeStack *stack; RenderData *rd; } ThreadData; static void *exec_composite_node(void *nodeexec_v) { bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ bNodeExec *nodeexec= nodeexec_v; bNode *node= nodeexec->node; ThreadData *thd= (ThreadData *)node->threaddata; node_get_stack(node, thd->stack, nsin, nsout); if((node->flag & NODE_MUTED) && (!node_only_value(node))) { /* viewers we execute, for feedback to user */ if(ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) node->typeinfo->execfunc(thd->rd, node, nsin, nsout); else node_compo_pass_on(node, nsin, nsout); } else if(node->typeinfo->execfunc) node->typeinfo->execfunc(thd->rd, node, nsin, nsout); else if (node->typeinfo->newexecfunc) node->typeinfo->newexecfunc(thd->rd, 0, node, nodeexec->data, nsin, nsout); node->exec |= NODE_READY; return 0; } /* return total of executable nodes, for timecursor */ static int setExecutableNodes(bNodeTree *ntree, ThreadData *thd) { bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ bNode *node; bNodeSocket *sock; int totnode= 0, group_edit= 0; /* note; do not add a dependency sort here, the stack was created already */ /* if we are in group edit, viewer nodes get skipped when group has viewer */ for(node= ntree->nodes.first; node; node= node->next) if(node->type==NODE_GROUP && (node->flag & NODE_GROUP_EDIT)) if(ntreeHasType((bNodeTree *)node->id, CMP_NODE_VIEWER)) group_edit= 1; for(node= ntree->nodes.first; node; node= node->next) { int a; node_get_stack(node, thd->stack, nsin, nsout); /* test the outputs */ /* skip value-only nodes (should be in type!) */ if(!node_only_value(node)) { for(a=0, sock= node->outputs.first; sock; sock= sock->next, a++) { if(nsout[a]->data==NULL && nsout[a]->hasoutput) { node->need_exec= 1; break; } } } /* test the inputs */ for(a=0, sock= node->inputs.first; sock; sock= sock->next, a++) { /* skip viewer nodes in bg render or group edit */ if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER) && (G.background || group_edit)) node->need_exec= 0; /* is sock in use? */ else if(sock->link) { bNodeLink *link= sock->link; /* this is the test for a cyclic case */ if(link->fromnode==NULL || link->tonode==NULL); else if(link->fromnode->level >= link->tonode->level && link->tonode->level!=0xFFF) { if(link->fromnode->need_exec) { node->need_exec= 1; break; } } else { node->need_exec= 0; printf("Node %s skipped, cyclic dependency\n", node->name); } } } if(node->need_exec) { /* free output buffers */ for(a=0, sock= node->outputs.first; sock; sock= sock->next, a++) { if(nsout[a]->data) { free_compbuf(nsout[a]->data); nsout[a]->data= NULL; } } totnode++; /* printf("node needs exec %s\n", node->name); */ /* tag for getExecutableNode() */ node->exec= 0; } else { /* tag for getExecutableNode() */ node->exec= NODE_READY|NODE_FINISHED|NODE_SKIPPED; } } /* last step: set the stack values for only-value nodes */ /* just does all now, compared to a full buffer exec this is nothing */ if(totnode) { for(node= ntree->nodes.first; node; node= node->next) { if(node->need_exec==0 && node_only_value(node)) { if(node->typeinfo->execfunc) { node_get_stack(node, thd->stack, nsin, nsout); node->typeinfo->execfunc(thd->rd, node, nsin, nsout); } } } } return totnode; } /* while executing tree, free buffers from nodes that are not needed anymore */ static void freeExecutableNode(bNodeTree *ntree, bNodeTreeExec *exec) { /* node outputs can be freed when: - not a render result or image node - when node outputs go to nodes all being set NODE_FINISHED */ bNode *node; bNodeSocket *sock; /* set exec flag for finished nodes that might need freed */ for(node= ntree->nodes.first; node; node= node->next) { if(node->type!=CMP_NODE_R_LAYERS) if(node->exec & NODE_FINISHED) node->exec |= NODE_FREEBUFS; } /* clear this flag for input links that are not done yet */ for(node= ntree->nodes.first; node; node= node->next) { if((node->exec & NODE_FINISHED)==0) { for(sock= node->inputs.first; sock; sock= sock->next) if(sock->link) sock->link->fromnode->exec &= ~NODE_FREEBUFS; } } /* now we can free buffers */ for(node= ntree->nodes.first; node; node= node->next) { if(node->exec & NODE_FREEBUFS) { for(sock= node->outputs.first; sock; sock= sock->next) { bNodeStack *ns= node_get_socket_stack(exec->stack, sock); if(ns && ns->data) { free_compbuf(ns->data); ns->data= NULL; // printf("freed buf node %s \n", node->name); } } } } } static bNodeExec *getExecutableNode(bNodeTreeExec *exec) { bNodeExec *nodeexec; bNodeSocket *sock; int n; for(n=0, nodeexec=exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) { if(nodeexec->node->exec==0) { /* input sockets should be ready */ for(sock= nodeexec->node->inputs.first; sock; sock= sock->next) { if(sock->link && sock->link->fromnode) if((sock->link->fromnode->exec & NODE_READY)==0) break; } if(sock==NULL) return nodeexec; } } return NULL; } /* check if texture nodes need exec or end */ static void ntree_composite_texnode(bNodeTree *ntree, int init) { bNode *node; for(node= ntree->nodes.first; node; node= node->next) { if(node->type==CMP_NODE_TEXTURE && node->id) { Tex *tex= (Tex *)node->id; if(tex->nodetree && tex->use_nodes) { /* has internal flag to detect it only does it once */ if(init) { if (!tex->nodetree->execdata) tex->nodetree->execdata = ntreeTexBeginExecTree(tex->nodetree); } else ntreeTexEndExecTree(tex->nodetree->execdata); tex->nodetree->execdata = NULL; } } } } /* optimized tree execute test for compositing */ void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview) { bNodeExec *nodeexec; bNode *node; ListBase threads; ThreadData thdata; int totnode, curnode, rendering= 1, n; bNodeTreeExec *exec= NULL; if(ntree==NULL) return; if(do_preview) ntreeInitPreview(ntree, 0, 0); if (!ntree->execdata) exec = ntreeCompositBeginExecTree(ntree); ntree_composite_texnode(ntree, 1); /* prevent unlucky accidents */ if(G.background) rd->scemode &= ~R_COMP_CROP; /* setup callerdata for thread callback */ thdata.rd= rd; thdata.stack= exec->stack; /* fixed seed, for example noise texture */ BLI_srandom(rd->cfra); /* sets need_exec tags in nodes */ curnode = totnode= setExecutableNodes(ntree, &thdata); BLI_init_threads(&threads, exec_composite_node, rd->threads); while(rendering) { if(BLI_available_threads(&threads)) { nodeexec= getExecutableNode(exec); if(nodeexec) { node = nodeexec->node; if(ntree->progress && totnode) ntree->progress(ntree->prh, (1.0 - curnode/(float)totnode)); if(ntree->stats_draw) { char str[64]; sprintf(str, "Compositing %d %s", curnode, node->name); ntree->stats_draw(ntree->sdh, str); } curnode--; node->threaddata = &thdata; node->exec= NODE_PROCESSING; BLI_insert_thread(&threads, nodeexec); } else PIL_sleep_ms(50); } else PIL_sleep_ms(50); rendering= 0; /* test for ESC */ if(ntree->test_break && ntree->test_break(ntree->tbh)) { for(node= ntree->nodes.first; node; node= node->next) node->exec |= NODE_READY; } /* check for ready ones, and if we need to continue */ for(n=0, nodeexec=exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) { node = nodeexec->node; if(node->exec & NODE_READY) { if((node->exec & NODE_FINISHED)==0) { BLI_remove_thread(&threads, nodeexec); /* this waits for running thread to finish btw */ node->exec |= NODE_FINISHED; /* freeing unused buffers */ if(rd->scemode & R_COMP_FREE) freeExecutableNode(ntree, exec); } } else rendering= 1; } } BLI_end_threads(&threads); ntreeCompositEndExecTree(exec); } /* *********************************************** */ /* clumsy checking... should do dynamic outputs once */ static void force_hidden_passes(bNode *node, int passflag) { bNodeSocket *sock; for(sock= node->outputs.first; sock; sock= sock->next) sock->flag &= ~SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_Z); if(!(passflag & SCE_PASS_Z)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_NORMAL); if(!(passflag & SCE_PASS_NORMAL)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_VEC); if(!(passflag & SCE_PASS_VECTOR)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_UV); if(!(passflag & SCE_PASS_UV)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_RGBA); if(!(passflag & SCE_PASS_RGBA)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_DIFF); if(!(passflag & SCE_PASS_DIFFUSE)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_SPEC); if(!(passflag & SCE_PASS_SPEC)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_SHADOW); if(!(passflag & SCE_PASS_SHADOW)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_AO); if(!(passflag & SCE_PASS_AO)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_REFLECT); if(!(passflag & SCE_PASS_REFLECT)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_REFRACT); if(!(passflag & SCE_PASS_REFRACT)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_INDIRECT); if(!(passflag & SCE_PASS_INDIRECT)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_INDEXOB); if(!(passflag & SCE_PASS_INDEXOB)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_INDEXMA); if(!(passflag & SCE_PASS_INDEXMA)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_MIST); if(!(passflag & SCE_PASS_MIST)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_EMIT); if(!(passflag & SCE_PASS_EMIT)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_ENV); if(!(passflag & SCE_PASS_ENVIRONMENT)) sock->flag |= SOCK_UNAVAIL; } /* based on rules, force sockets hidden always */ void ntreeCompositForceHidden(bNodeTree *ntree, Scene *curscene) { bNode *node; if(ntree==NULL) return; for(node= ntree->nodes.first; node; node= node->next) { if( node->type==CMP_NODE_R_LAYERS) { Scene *sce= node->id?(Scene *)node->id:curscene; SceneRenderLayer *srl= BLI_findlink(&sce->r.layers, node->custom1); if(srl) force_hidden_passes(node, srl->passflag); } else if( node->type==CMP_NODE_IMAGE) { Image *ima= (Image *)node->id; if(ima) { if(ima->rr) { ImageUser *iuser= node->storage; RenderLayer *rl= BLI_findlink(&ima->rr->layers, iuser->layer); if(rl) force_hidden_passes(node, rl->passflag); else force_hidden_passes(node, 0); } else if(ima->type!=IMA_TYPE_MULTILAYER) { /* if ->rr not yet read we keep inputs */ force_hidden_passes(node, RRES_OUT_Z); } else force_hidden_passes(node, 0); } else force_hidden_passes(node, 0); } } } /* called from render pipeline, to tag render input and output */ /* need to do all scenes, to prevent errors when you re-render 1 scene */ void ntreeCompositTagRender(Scene *curscene) { Scene *sce; for(sce= G.main->scene.first; sce; sce= sce->id.next) { if(sce->nodetree) { bNode *node; for(node= sce->nodetree->nodes.first; node; node= node->next) { if(node->id==(ID *)curscene || node->type==CMP_NODE_COMPOSITE) NodeTagChanged(sce->nodetree, node); else if(node->type==CMP_NODE_TEXTURE) /* uses scene sizex/sizey */ NodeTagChanged(sce->nodetree, node); } } } } static int node_animation_properties(bNodeTree *ntree, bNode *node) { bNodeSocket *sock; const ListBase *lb; Link *link; PointerRNA ptr; PropertyRNA *prop; /* check to see if any of the node's properties have fcurves */ RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr); lb = RNA_struct_type_properties(ptr.type); for (link=lb->first; link; link=link->next) { int driven, len=1, index; prop = (PropertyRNA *)link; if (RNA_property_array_check(prop)) len = RNA_property_array_length(&ptr, prop); for (index=0; indexinputs.first; sock; sock=sock->next) { int driven, len=1, index; RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); prop = RNA_struct_find_property(&ptr, "default_value"); if (prop) { if (RNA_property_array_check(prop)) len = RNA_property_array_length(&ptr, prop); for (index=0; indexnodes.first; node; node= node->next) { tagged = node_animation_properties(ntree, node); /* otherwise always tag these node types */ if(node->type==CMP_NODE_IMAGE) { Image *ima= (Image *)node->id; if(ima && ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { NodeTagChanged(ntree, node); tagged= 1; } } else if(node->type==CMP_NODE_TIME) { NodeTagChanged(ntree, node); tagged= 1; } /* here was tag render layer, but this is called after a render, so re-composites fail */ else if(node->type==NODE_GROUP) { if( ntreeCompositTagAnimated((bNodeTree *)node->id) ) { NodeTagChanged(ntree, node); } } } return tagged; } /* called from image window preview */ void ntreeCompositTagGenerators(bNodeTree *ntree) { bNode *node; if(ntree==NULL) return; for(node= ntree->nodes.first; node; node= node->next) { if( ELEM(node->type, CMP_NODE_R_LAYERS, CMP_NODE_IMAGE)) NodeTagChanged(ntree, node); } } /* XXX after render animation system gets a refresh, this call allows composite to end clean */ void ntreeClearTags(bNodeTree *ntree) { bNode *node; if(ntree==NULL) return; for(node= ntree->nodes.first; node; node= node->next) { node->need_exec= 0; if(node->type==NODE_GROUP) ntreeClearTags((bNodeTree *)node->id); } }