Three-in-one commit:
authorTon Roosendaal <ton@blender.org>
Sun, 29 Jan 2006 11:36:33 +0000 (11:36 +0000)
committerTon Roosendaal <ton@blender.org>
Sun, 29 Jan 2006 11:36:33 +0000 (11:36 +0000)
- Compositor now is threaded
Enable it with the Scene buttons "Threads". This will handle over nodes to
individual threads to be calculated. However, if nodes depend on others
they have to wait. The current system only threads per entire node, not for
calculating results in parts.

I've reshuffled the node execution code to evaluate 'changed' events, and
prepare the entire tree to become simply parsed for open jobs with a call
to   node = getExecutableNode()
By default, even without 'thread' option active, all node execution is
done within a separate thread.

Also fixed issues in yesterdays commit for 'event based' calculations, it
didn't do animated images, or execute (on rendering) the correct nodes
when you don't have Render-Result nodes included.

- Added generic Thread support in blenlib/ module
The renderer and the node system now both use same code for controlling the
threads. This has been moved to a new C file in blenlib/intern/threads.c.
Check this c file for an extensive doc and example how to use it.

The current implementation for Compositing allows unlimited amount of
threads. For rendering it is still tied to two threads, although it is
pretty easy to extend to 4 already. People with giant amounts of cpus can
poke me once for tests. :)

- Bugfix in creating group nodes
Group node definitions demand a clear separation of 'internal sockets' and
'external sockets'. The first are sockets being linked internally, the latter
are sockets exposed as sockets for the group itself.
When sockets were linked both internal and external, Blender crashed. It is
solved now by removing the external link(s).

source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/blenkernel/intern/node_composite.c
source/blender/blenlib/BLI_threads.h [new file with mode: 0644]
source/blender/blenlib/SConscript
source/blender/blenlib/intern/Makefile
source/blender/blenlib/intern/threads.c [new file with mode: 0644]
source/blender/makesdna/DNA_node_types.h
source/blender/render/intern/source/pipeline.c

index f289de038d6c01e54bd1f1fabb895010066f8675..d6ebdbef08313aac4ccc9bcc12b019a739ba1d9e 100644 (file)
@@ -224,6 +224,7 @@ extern bNodeType *node_all_composit[];
 struct CompBuf;
 int ntreeCompositNeedsRender(struct bNodeTree *ntree);
 void ntreeCompositTagRender(struct bNodeTree *ntree);
+void ntreeCompositTagAnimated(struct bNodeTree *ntree);
 
 void free_compbuf(struct CompBuf *cbuf); /* internal...*/
 
index 3e99f83a8f67c4387031b5efedaef50b6aa967e3..672e3b2a0bd18abbfe7bdd20dda8c84a5b6fd750 100644 (file)
@@ -47,6 +47,9 @@
 
 #include "BLI_arithb.h"
 #include "BLI_blenlib.h"
+#include "BLI_threads.h"
+
+#include "PIL_time.h"
 
 #include "MEM_guardedalloc.h"
 #include "IMB_imbuf.h"
@@ -283,7 +286,7 @@ static void group_verify_own_indices(bNodeTree *ngroup)
                        if(sock->own_index==0 && sock->intern==0)
                                sock->own_index= ++(ngroup->cur_index);
        }
-//     printf("internal index %d\n", ngroup->cur_index);
+       //printf("internal index %d\n", ngroup->cur_index);
 }
 
 
@@ -408,6 +411,7 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree)
 {
        bNodeLink *link, *linkn;
        bNode *node, *gnode, *nextn;
+       bNodeSocket *sock;
        bNodeTree *ngroup;
        float min[2], max[2];
        int totnode=0;
@@ -478,16 +482,26 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree)
        gnode->locy= 0.5f*(min[1]+max[1]);
        
        /* relink external sockets */
-       for(link= ntree->links.first; link; link= link->next) {
+       for(link= ntree->links.first; link; link= linkn) {
+               linkn= link->next;
+               
                if(link->tonode->flag & NODE_SELECT) {
                        link->tonode= gnode;
-                       link->tosock= groupnode_find_tosock(gnode, link->tosock->own_index);
-                       if(link->tosock==NULL) printf("Bad!\n");
+                       sock= groupnode_find_tosock(gnode, link->tosock->own_index);
+                       if(sock==NULL) {
+                               nodeRemLink(ntree, link);       
+                               printf("Removed link, cannot mix internal and external sockets in group\n");
+                       }
+                       else link->tosock= sock;
                }
                else if(link->fromnode->flag & NODE_SELECT) {
                        link->fromnode= gnode;
-                       link->fromsock= groupnode_find_fromsock(gnode, link->fromsock->own_index);
-                       if(link->fromsock==NULL) printf("Bad!\n");
+                       sock= groupnode_find_fromsock(gnode, link->fromsock->own_index);
+                       if(sock==NULL) {
+                               nodeRemLink(ntree, link);       
+                               printf("Removed link, cannot mix internal and external sockets in group\n");
+                       }
+                       else link->fromsock= sock;
                }
        }
        
@@ -1553,32 +1567,53 @@ void ntreeExecTree(bNodeTree *ntree, void *callerdata, int thread)
        }
 }
 
-/* optimized tree execute test for compositing */
-void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview)
+
+/* ***************************** threaded version for execute composite nodes ************* */
+
+#define NODE_PROCESSING        1
+#define NODE_READY             2
+#define NODE_FINISHED  4
+
+/* not changing info, for thread callback */
+typedef struct ThreadData {
+       bNodeStack *stack;
+       RenderData *rd;
+} ThreadData;
+
+static int exec_composite_node(void *node_v)
 {
-       bNode *node;
-       bNodeSocket *sock;
        bNodeStack *nsin[MAX_SOCKET];   /* arbitrary... watch this */
        bNodeStack *nsout[MAX_SOCKET];  /* arbitrary... watch this */
-       bNodeStack *stack;
-       int totnode;
-       
-       if(ntree==NULL) return;
+       bNode *node= node_v;
+       ThreadData *thd= (ThreadData *)node->new;
        
-       totnode= BLI_countlist(&ntree->nodes);
+       node_get_stack(node, thd->stack, nsin, nsout);
        
-       if(do_preview)
-               ntreeInitPreview(ntree, 0, 0);
-       
-       ntreeBeginExecTree(ntree);
+       if(node->typeinfo->execfunc) {
+               node->typeinfo->execfunc(thd->rd, node, nsin, nsout);
+       }
+       else if(node->type==NODE_GROUP && node->id) {
+               node_group_execute(thd->stack, thd->rd, node, nsin, nsout); 
+       }
        
-       stack= ntree->stack;
+       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;
        
        for(node= ntree->nodes.first; node; node= node->next) {
                if(node->typeinfo->execfunc) {
                        int a;
                        
-                       node_get_stack(node, stack, nsin, nsout);
+                       node_get_stack(node, thd->stack, nsin, nsout);
                        
                        /* test the inputs */
                        for(a=0, sock= node->inputs.first; sock; sock= sock->next, a++) {
@@ -1607,24 +1642,110 @@ void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview)
                                                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;
+               }
+       }
+       return totnode;
+}
+
+static bNode *getExecutableNode(bNodeTree *ntree)
+{
+       bNode *node;
+       bNodeSocket *sock;
+       
+       for(node= ntree->nodes.first; node; node= node->next) {
+               if(node->exec==0) {
+                       
+                       /* input sockets should be ready */
+                       for(sock= node->inputs.first; sock; sock= sock->next) {
+                               if(sock->link)
+                                       if((sock->link->fromnode->exec & NODE_READY)==0)
+                                               break;
+                       }
+                       if(sock==NULL) {
+                               printf("exec %s\n", node->name);
+                               return node;
+                       }
+               }
+       }
+       return NULL;
+}
+
+
+/* optimized tree execute test for compositing */
+void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview)
+{
+       bNode *node;
+       ListBase threads;
+       ThreadData thdata;
+       int totnode, maxthreads, rendering= 1;
+       
+       if(ntree==NULL) return;
+       
+       if(rd->mode & R_THREADS)
+               maxthreads= 2;
+       else
+               maxthreads= 1;
+       
+       if(do_preview)
+               ntreeInitPreview(ntree, 0, 0);
+       
+       ntreeBeginExecTree(ntree);
+       
+       /* setup callerdata for thread callback */
+       thdata.rd= rd;
+       thdata.stack= ntree->stack;
+       
+       /* sets need_exec tags in nodes */
+       totnode= setExecutableNodes(ntree, &thdata);
+       
+       BLI_init_threads(&threads, exec_composite_node, maxthreads);
+       
+       while(rendering) {
+               
+               if(BLI_available_threads(&threads)) {
+                       node= getExecutableNode(ntree);
+                       if(node) {
+                               
                                if(ntree->timecursor)
-                                       ntree->timecursor(totnode);
+                                       ntree->timecursor(totnode--);
                                
-                               printf("exec node %s\n", node->name);
-                               node->typeinfo->execfunc(rd, node, nsin, nsout);
+                               node->new = (bNode *)&thdata;
+                               node->exec= NODE_PROCESSING;
+                               BLI_insert_thread(&threads, node);
                        }
                }
-               else if(node->type==NODE_GROUP && node->id) {
-                       node_get_stack(node, stack, nsin, nsout);
-                       node_group_execute(stack, rd, node, nsin, nsout); 
+               else
+                       PIL_sleep_ms(50);
+               
+               /* check for ready ones, and if we need to continue */
+               rendering= 0;
+               for(node= ntree->nodes.first; node; node= node->next) {
+                       if(node->exec & NODE_READY) {
+                               if((node->exec & NODE_FINISHED)==0) {
+                                       BLI_remove_thread(&threads, node);
+                                       node->exec |= NODE_FINISHED;
+                               }
+                       }
+                       else rendering= 1;
                }
-               totnode--;
        }
        
        
+       BLI_end_threads(&threads);
+       
        ntreeEndExecTree(ntree);
        
        free_unused_animimages();
        
 }
 
+
index bcb3e9e600d2a5eb6d447cadc496ab5fe22a8739..90e16ab6edeb641fa1383b2a8bbcdcf3a50130dc 100644 (file)
@@ -1840,6 +1840,7 @@ int ntreeCompositNeedsRender(bNodeTree *ntree)
        return 0;
 }
 
+/* called from render pipeline, to tag render input and output */
 void ntreeCompositTagRender(bNodeTree *ntree)
 {
        bNode *node;
@@ -1847,8 +1848,23 @@ void ntreeCompositTagRender(bNodeTree *ntree)
        if(ntree==NULL) return;
        
        for(node= ntree->nodes.first; node; node= node->next) {
-               if(node->type==CMP_NODE_R_RESULT)
+               if( ELEM(node->type, CMP_NODE_R_RESULT, CMP_NODE_COMPOSITE))
                        NodeTagChanged(ntree, node);
        }
 }
 
+/* tags nodes that have animation capabilities */
+void ntreeCompositTagAnimated(bNodeTree *ntree)
+{
+       bNode *node;
+       
+       if(ntree==NULL) return;
+       
+       for(node= ntree->nodes.first; node; node= node->next) {
+               if(node->type==CMP_NODE_IMAGE) {
+                       /* no actual test yet... */
+                       if(node->storage)
+                               NodeTagChanged(ntree, node);
+               }
+       }
+}
diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h
new file mode 100644 (file)
index 0000000..5ab8c2a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *
+ * $Id:
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2006 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef BLI_THREADS_H
+#define BLI_THREADS_H 
+
+
+void   BLI_init_threads        (ListBase *threadbase, int (*do_thread)(void *), int tot);
+int            BLI_available_threads(ListBase *threadbase);
+int            BLI_available_thread_index(ListBase *threadbase);
+void           BLI_insert_thread       (ListBase *threadbase, void *callerdata);
+void   BLI_remove_thread       (ListBase *threadbase, void *callerdata);
+void   BLI_end_threads         (ListBase *threadbase);
+
+
+#endif
+
index 8b8740a2e6bd450f1365f2f33aa2fe0aa8345a83..e6b50a4e2ff864b770d2fe0c0cc48e9594686f7b 100644 (file)
@@ -26,6 +26,7 @@ source_files = ['intern/BLI_dynstr.c',
                 'intern/vectorops.c',
                 'intern/freetypefont.c',
                'intern/jitter.c',
+               'intern/threads.c',
                 'intern/winstuff.c']
 
 
@@ -42,4 +43,5 @@ if user_options_dict['USE_INTERNATIONAL'] == 1:
 blenlib_env.Append (CPPPATH = extra_includes)
 blenlib_env.Prepend (CPPPATH = user_options_dict['FREETYPE_INCLUDE'])
 blenlib_env.Append (CPPPATH = user_options_dict['Z_INCLUDE'])
+blenlib_env.Append (CPPPATH = user_options_dict['SDL_INCLUDE'])
 blenlib_env.Library (target='#'+user_options_dict['BUILD_DIR']+'/lib/blender_blenlib', source=source_files)
index b6e827518bd53caa098afe76e3a53ab722830312..c0db58cdb24f2f87225c70fe7a2ce82d31f2324a 100644 (file)
@@ -56,6 +56,8 @@ CPPFLAGS += -I$(SRCHOME)/blender/blenkernel
 CPPFLAGS += -I../../include/
 # path to zlib
 CPPFLAGS += -I$(NAN_ZLIB)/include
+# path to sdl for threads
+CPPFLAGS += $(NAN_SDLCFLAGS)
 
 ifeq ($(WITH_FREETYPE2), true)
        CPPFLAGS += -DWITH_FREETYPE2
diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c
new file mode 100644 (file)
index 0000000..b404ae5
--- /dev/null
@@ -0,0 +1,183 @@
+/**
+ *
+ * $Id:
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2006 Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_threads.h"
+
+#include "SDL_thread.h"
+
+/* ********** basic thread control API ************ 
+
+Many thread cases have an X amount of jobs, and only an Y amount of
+threads are useful (typically amount of cpus)
+
+This code can be used to start a maximum amount of 'thread slots', which
+then can be filled in a loop with an idle timer. 
+
+A sample loop can look like this (pseudo c);
+
+       ListBase lb;
+       int maxthreads= 2;
+       int cont= 1;
+
+       BLI_init_threads(&lb, do_something_func, maxthreads);
+
+       while(cont) {
+               if(BLI_available_threads(&lb) && !(escape loop event)) {
+                       // get new job (data pointer)
+                       // tag job 'processed 
+                       BLI_insert_thread(&lb, job);
+               }
+               else PIL_sleep_ms(50);
+               
+               // find if a job is ready, this the do_something_func() should write in job somewhere
+               cont= 0;
+               for(go over all jobs)
+                       if(job is ready) {
+                               if(job was not removed) {
+                                       BLI_remove_thread(&lb, job);
+                               }
+                       }
+                       else cont= 1;
+               }
+               // conditions to exit loop 
+               if(if escape loop event) {
+                       if(BLI_available_threadslots(&lb)==maxthreads)
+                               break;
+               }
+       }
+
+       BLI_end_threads(&lb);
+
+ ************************************************ */
+
+/* just a max for security reasons */
+#define RE_MAX_THREAD  8
+
+typedef struct ThreadSlot {
+       struct ThreadSlot *next, *prev;
+       int (*do_thread)(void *);
+       void *callerdata;
+       SDL_Thread *sdlthread;
+       int avail;
+} ThreadSlot;
+
+static ThreadSlot threadslots[RE_MAX_THREAD];
+
+void BLI_init_threads(ListBase *threadbase, int (*do_thread)(void *), int tot)
+{
+       int a;
+       
+       if(threadbase==NULL)
+               return;
+       threadbase->first= threadbase->last= NULL;
+       
+       if(tot>RE_MAX_THREAD) tot= RE_MAX_THREAD;
+       else if(tot<1) tot= 1;
+       
+       for(a=0; a<tot; a++) {
+               ThreadSlot *tslot= MEM_callocN(sizeof(ThreadSlot), "threadslot");
+               BLI_addtail(threadbase, tslot);
+               tslot->do_thread= do_thread;
+       }
+}
+
+/* amount of available threads */
+int BLI_available_threads(ListBase *threadbase)
+{
+       ThreadSlot *tslot;
+       int counter=0;
+       
+       for(tslot= threadbase->first; tslot; tslot= tslot->next) {
+               if(tslot->sdlthread==NULL)
+                       counter++;
+       }
+       return counter;
+}
+
+/* returns thread number, for sample patterns or threadsafe tables */
+int BLI_available_thread_index(ListBase *threadbase)
+{
+       ThreadSlot *tslot;
+       int counter=0;
+       
+       for(tslot= threadbase->first; tslot; tslot= tslot->next, counter++) {
+               if(tslot->sdlthread==NULL)
+                       return counter;
+       }
+       return 0;
+}
+
+
+void BLI_insert_thread(ListBase *threadbase, void *callerdata)
+{
+       ThreadSlot *tslot;
+       
+       for(tslot= threadbase->first; tslot; tslot= tslot->next) {
+               if(tslot->sdlthread==NULL) {
+                       tslot->callerdata= callerdata;
+                       tslot->sdlthread= SDL_CreateThread(tslot->do_thread, tslot->callerdata);
+                       return;
+               }
+       }
+       printf("ERROR: could not insert thread slot\n");
+}
+
+void BLI_remove_thread(ListBase *threadbase, void *callerdata)
+{
+       ThreadSlot *tslot;
+       
+       for(tslot= threadbase->first; tslot; tslot= tslot->next) {
+               if(tslot->callerdata==callerdata) {
+                       tslot->callerdata= NULL;
+                       SDL_WaitThread(tslot->sdlthread, NULL);
+                       tslot->sdlthread= NULL;
+               }
+       }
+}
+
+void BLI_end_threads(ListBase *threadbase)
+{
+       ThreadSlot *tslot;
+       
+       for(tslot= threadbase->first; tslot; tslot= tslot->next) {
+               if(tslot->sdlthread) {
+                       SDL_WaitThread(tslot->sdlthread, NULL);
+               }
+       }
+       BLI_freelistN(threadbase);
+}
+
+/* eof */
index 853857f6915df3bbb9b67e38e62560c7e6eab82a..05d3a276607eb0a374166c25573fce2771746cec 100644 (file)
@@ -109,7 +109,7 @@ typedef struct bNode {
        short done, level;              /* both for dependency and sorting */
        short lasty, menunr;    /* lasty: check preview render status, menunr: browse ID blocks */
        short stack_index;              /* for groupnode, offset in global caller stack */
-       short nr;                               /* number of this node in list, used for exec events */
+       short nr;                               /* number of this node in list, used for UI exec events */
        
        ListBase inputs, outputs;
        struct ID *id;                  /* optional link to libdata */
@@ -120,7 +120,7 @@ typedef struct bNode {
        float width, miniwidth;                 
        short custom1, custom2; /* to be abused for buttons */
        
-       short need_exec, pad1;  /* need_exec is set to optimize execution */
+       short need_exec, exec;  /* need_exec is set as UI execution event, exec is flag during exec */
        
        rctf totr;                              /* entire boundbox */
        rctf butr;                              /* optional buttons area */
index d109989126406c8fdefb00c628e11790a4bb7e45..5780b780b0f0d4b3bb3dfa30b7f99b8db28fbda8 100644 (file)
@@ -45,6 +45,7 @@
 
 #include "BLI_arithb.h"
 #include "BLI_blenlib.h"
+#include "BLI_threads.h"
 
 #include "PIL_time.h"
 #include "IMB_imbuf.h"
@@ -557,75 +558,7 @@ void RE_AddObject(Render *re, Object *ob)
        
 }
 
-/* ********** basic thread control API ************ */
-
-#define RE_MAX_THREAD 4
-
-typedef struct ThreadSlot {
-       RenderPart *part;
-       SDL_Thread *sdlthread;
-       int avail;
-} ThreadSlot;
-
-static ThreadSlot threadslots[RE_MAX_THREAD];
-
-static void init_threadslots(int tot)
-{
-       int a;
-       
-       if(tot>RE_MAX_THREAD) tot= RE_MAX_THREAD;
-       else if(tot<1) tot= 1;
-       
-       for(a=0; a< RE_MAX_THREAD; a++) {
-               threadslots[a].part= NULL;
-               threadslots[a].sdlthread= NULL;
-               if(a<tot)
-                       threadslots[a].avail= 1;
-               else
-                       threadslots[a].avail= 0;
-       }
-}
-
-static int available_threadslots(void)
-{
-       int a, counter=0;
-       for(a=0; a< RE_MAX_THREAD; a++)
-               if(threadslots[a].avail)
-                       counter++;
-       return counter;
-}
-
-/* prototype for functional below */
-static int do_part_thread(void *pa_v);
-
-static void insert_threadslot(RenderPart *pa)
-{
-       int a;
-       for(a=0; a< RE_MAX_THREAD; a++) {
-               if(threadslots[a].avail) {
-                       pa->thread= a;
-                       threadslots[a].avail= 0;
-                       threadslots[a].part= pa;
-                       threadslots[a].sdlthread= SDL_CreateThread(do_part_thread, pa);
-                       break;
-               }
-       }
-}
-
-static void remove_threadslot(RenderPart *pa)
-{
-       int a;
-       for(a=0; a< RE_MAX_THREAD; a++) {
-               if(threadslots[a].part==pa) {
-                       threadslots[a].avail= 1;
-                       threadslots[a].part= NULL;
-                       SDL_WaitThread(threadslots[a].sdlthread, NULL);
-                       threadslots[a].sdlthread= NULL;
-               }
-       }
-}
-
-/* ********** basic thread control API ************ */
+/* *************************************** */
 
 static int do_part_thread(void *pa_v)
 {
@@ -726,6 +659,7 @@ static RenderPart *find_nicest_part(Render *re)
 
 static void threaded_tile_processor(Render *re)
 {
+       ListBase threads;
        RenderPart *pa;
        int maxthreads=2, rendering=1, counter= 1;
        
@@ -735,7 +669,7 @@ static void threaded_tile_processor(Render *re)
                return;
        
        initparts(re);
-       init_threadslots(maxthreads);
+       BLI_init_threads(&threads, do_part_thread, maxthreads);
        
        /* assuming no new data gets added to dbase... */
        R= *re;
@@ -745,11 +679,12 @@ static void threaded_tile_processor(Render *re)
        while(rendering) {
                
                /* I noted that test_break() in a thread doesn't make ghost send ESC */
-               if(available_threadslots() && !re->test_break()) {
+               if(BLI_available_threads(&threads) && !re->test_break()) {
                        pa= find_nicest_part(re);
                        if(pa) {
-                               pa->nr= counter++;      /* only for stats */
-                               insert_threadslot(pa);
+                               pa->nr= counter++;      /* for nicest part, and for stats */
+                               pa->thread= BLI_available_thread_index(&threads);       /* sample index */
+                               BLI_insert_thread(&threads, pa);
                        }
                }
                else
@@ -760,7 +695,7 @@ static void threaded_tile_processor(Render *re)
                for(pa= re->parts.first; pa; pa= pa->next) {
                        if(pa->ready) {
                                if(pa->result) {
-                                       remove_threadslot(pa);  /* do it here, not in thread */
+                                       BLI_remove_thread(&threads, pa);
                                        re->display_draw(pa->result, NULL);
                                        free_render_result(pa->result);
                                        pa->result= NULL;
@@ -771,13 +706,14 @@ static void threaded_tile_processor(Render *re)
                }
                
                /* on break, wait for all slots to get freed */
-               if(re->test_break() && available_threadslots()==maxthreads)
+               if(re->test_break() && BLI_available_threads(&threads)==maxthreads)
                        rendering= 0;
                
        }
        
        if(malloc_lock) SDL_DestroyMutex(malloc_lock); malloc_lock= NULL;
        
+       BLI_end_threads(&threads);
        freeparts(re);
 }
 
@@ -849,6 +785,7 @@ static void do_render_final(Render *re, Scene *scene)
                }
                
                ntreeCompositTagRender(scene->nodetree);
+               ntreeCompositTagAnimated(scene->nodetree);
                
                if(re->r.scemode & R_DOCOMP)
                        ntreeCompositExecTree(scene->nodetree, &re->r, 0);