Task scheduler ported form CYcles to C

Replaces ThreadedWorker and is gonna to be used
for threaded object update in the future and
some more upcoming changes.

But in general, it's to be used for any task
based subsystem in Blender.

Originally written by Brecht, with some fixes
and tweaks by self.
This commit is contained in:
Sergey Sharybin 2013-10-12 14:08:59 +00:00
parent 9d7567d6ac
commit f0dcff9aa9
9 changed files with 694 additions and 243 deletions

View File

@ -0,0 +1,108 @@
/*
* ***** 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.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BLI_TASK_H__
#define __BLI_TASK_H__
/** \file BLI_task.h
* \ingroup bli
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "BLI_threads.h"
#include "BLI_utildefines.h"
/* Task Scheduler
*
* Central scheduler that holds running threads ready to execute tasks. A single
* queue holds the task from all pools.
*
* Init/exit must be called before/after any task pools are created/freed, and
* must be called from the main threads. All other scheduler and pool functions
* are thread-safe. */
typedef struct TaskScheduler TaskScheduler;
enum {
TASK_SCHEDULER_AUTO_THREADS = 0,
TASK_SCHEDULER_SINGLE_THREAD = 1
};
TaskScheduler *BLI_task_scheduler_create(int num_threads);
void BLI_task_scheduler_free(TaskScheduler *scheduler);
int BLI_task_scheduler_num_threads(TaskScheduler *scheduler);
/* Task Pool
*
* Pool of tasks that will be executed by the central TaskScheduler. For each
* pool, we can wait for all tasks to be done, or cancel them before they are
* done.
*
* Running tasks may spawn new tasks.
*
* Pools may be nested, i.e. a thread running a task can create another task
* pool with smaller tasks. When other threads are busy they will continue
* working on their own tasks, if not they will join in, no new threads will
* be launched.
*/
typedef enum TaskPriority {
TASK_PRIORITY_LOW,
TASK_PRIORITY_HIGH
} TaskPriority;
typedef struct TaskPool TaskPool;
typedef void (*TaskRunFunction)(TaskPool *pool, void *taskdata, int threadid);
TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata);
void BLI_task_pool_free(TaskPool *pool);
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run,
void *taskdata, bool free_taskdata, TaskPriority priority);
/* work and wait until all tasks are done */
void BLI_task_pool_work_and_wait(TaskPool *pool);
/* cancel all tasks, keep worker threads running */
void BLI_task_pool_cancel(TaskPool *pool);
/* stop all worker threads */
void BLI_task_pool_stop(TaskPool *pool);
/* for worker threads, test if cancelled */
bool BLI_task_pool_cancelled(TaskPool *pool);
/* optional userdata pointer to pass along to run function */
void *BLI_task_pool_userdata(TaskPool *pool);
/* optional mutex to use from run function */
ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool);
/* number of tasks done, for stats, don't use this to make decisions */
size_t BLI_task_pool_tasks_done(TaskPool *pool);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -45,6 +45,7 @@ extern "C" {
#define BLENDER_MAX_THREADS 64
struct ListBase;
struct TaskScheduler;
/* Threading API */
@ -52,6 +53,8 @@ struct ListBase;
void BLI_threadapi_init(void);
void BLI_threadapi_exit(void);
struct TaskScheduler *BLI_task_scheduler_get(void);
void BLI_init_threads(struct ListBase *threadbase, void *(*do_thread)(void *), int tot);
int BLI_available_threads(struct ListBase *threadbase);
int BLI_available_thread_index(struct ListBase *threadbase);
@ -101,6 +104,7 @@ ThreadMutex *BLI_mutex_alloc(void);
void BLI_mutex_free(ThreadMutex *mutex);
void BLI_mutex_lock(ThreadMutex *mutex);
bool BLI_mutex_trylock(ThreadMutex *mutex);
void BLI_mutex_unlock(ThreadMutex *mutex);
/* Spin Lock */
@ -144,27 +148,15 @@ void BLI_ticket_mutex_free(TicketMutex *ticket);
void BLI_ticket_mutex_lock(TicketMutex *ticket);
void BLI_ticket_mutex_unlock(TicketMutex *ticket);
/* ThreadedWorker
*
* A simple tool for dispatching work to a limited number of threads
* in a transparent fashion from the caller's perspective. */
/* Condition */
typedef pthread_cond_t ThreadCondition;
struct ThreadedWorker;
/* Create a new worker supporting tot parallel threads.
* When new work in inserted and all threads are busy, sleep(sleep_time) before checking again
*/
struct ThreadedWorker *BLI_create_worker(void *(*do_thread)(void *), int tot, int sleep_time);
/* join all working threads */
void BLI_end_worker(struct ThreadedWorker *worker);
/* also ends all working threads */
void BLI_destroy_worker(struct ThreadedWorker *worker);
/* Spawns a new work thread if possible, sleeps until one is available otherwise
* NOTE: inserting work is NOT thread safe, so make sure it is only done from one thread */
void BLI_insert_work(struct ThreadedWorker *worker, void *param);
void BLI_condition_init(ThreadCondition *cond);
void BLI_condition_wait(ThreadCondition *cond, ThreadMutex *mutex);
void BLI_condition_notify_one(ThreadCondition *cond);
void BLI_condition_notify_all(ThreadCondition *cond);
void BLI_condition_end(ThreadCondition *cond);
/* ThreadWorkQueue
*

View File

@ -94,6 +94,7 @@ set(SRC
intern/string.c
intern/string_cursor_utf8.c
intern/string_utf8.c
intern/task.c
intern/threads.c
intern/time.c
intern/uvproject.c
@ -160,6 +161,7 @@ set(SRC
BLI_string_cursor_utf8.h
BLI_string_utf8.h
BLI_sys_types.h
BLI_task.h
BLI_threads.h
BLI_utildefines.h
BLI_uvproject.h

View File

@ -0,0 +1,424 @@
/*
* ***** 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.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_task.h"
#include "BLI_threads.h"
/* Types */
typedef struct Task {
struct Task *next, *prev;
TaskRunFunction run;
void *taskdata;
bool free_taskdata;
TaskPool *pool;
} Task;
struct TaskPool {
TaskScheduler *scheduler;
volatile size_t num;
volatile size_t done;
ThreadMutex num_mutex;
ThreadCondition num_cond;
void *userdata;
ThreadMutex user_mutex;
volatile bool do_cancel;
};
struct TaskScheduler {
pthread_t *threads;
struct TaskThread *task_threads;
int num_threads;
ListBase queue;
ThreadMutex queue_mutex;
ThreadCondition queue_cond;
volatile bool do_exit;
};
typedef struct TaskThread {
TaskScheduler *scheduler;
int id;
} TaskThread;
/* Task Scheduler */
static void task_pool_num_decrease(TaskPool *pool, size_t done)
{
BLI_mutex_lock(&pool->num_mutex);
BLI_assert(pool->num >= done);
pool->num -= done;
pool->done += done;
if (pool->num == 0)
BLI_condition_notify_all(&pool->num_cond);
BLI_mutex_unlock(&pool->num_mutex);
}
static void task_pool_num_increase(TaskPool *pool)
{
BLI_mutex_lock(&pool->num_mutex);
pool->num++;
BLI_condition_notify_all(&pool->num_cond);
BLI_mutex_unlock(&pool->num_mutex);
}
static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task)
{
BLI_mutex_lock(&scheduler->queue_mutex);
while (!scheduler->queue.first && !scheduler->do_exit)
BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex);
if (!scheduler->queue.first) {
BLI_mutex_unlock(&scheduler->queue_mutex);
BLI_assert(scheduler->do_exit);
return false;
}
*task = scheduler->queue.first;
BLI_remlink(&scheduler->queue, *task);
BLI_mutex_unlock(&scheduler->queue_mutex);
return true;
}
static void *task_scheduler_thread_run(void *thread_p)
{
TaskThread *thread = (TaskThread *) thread_p;
TaskScheduler *scheduler = thread->scheduler;
int thread_id = thread->id;
Task *task;
/* keep popping off tasks */
while (task_scheduler_thread_wait_pop(scheduler, &task)) {
TaskPool *pool = task->pool;
/* run task */
task->run(pool, task->taskdata, thread_id);
/* delete task */
if (task->free_taskdata)
MEM_freeN(task->taskdata);
MEM_freeN(task);
/* notify pool task was done */
task_pool_num_decrease(pool, 1);
}
return NULL;
}
TaskScheduler *BLI_task_scheduler_create(int num_threads)
{
TaskScheduler *scheduler = MEM_callocN(sizeof(TaskScheduler), "TaskScheduler");
/* multiple places can use this task scheduler, sharing the same
* threads, so we keep track of the number of users. */
scheduler->do_exit = false;
scheduler->queue.first = scheduler->queue.last = NULL;
BLI_mutex_init(&scheduler->queue_mutex);
BLI_condition_init(&scheduler->queue_cond);
if (num_threads == 0) {
/* automatic number of threads will be main thread + num cores */
num_threads = BLI_system_thread_count();
}
/* main thread will also work, so we count it too */
num_threads -= 1;
/* launch threads that will be waiting for work */
if (num_threads > 0) {
int i;
scheduler->num_threads = num_threads;
scheduler->threads = MEM_callocN(sizeof(pthread_t) * num_threads, "TaskScheduler threads");
scheduler->task_threads = MEM_callocN(sizeof(TaskThread) * num_threads, "TaskScheduler task threads");
for (i = 0; i < num_threads; i++) {
TaskThread *thread = &scheduler->task_threads[i];
thread->scheduler = scheduler;
thread->id = i + 1;
if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) {
fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads);
MEM_freeN(thread);
}
}
}
return scheduler;
}
void BLI_task_scheduler_free(TaskScheduler *scheduler)
{
Task *task;
/* stop all waiting threads */
BLI_mutex_lock(&scheduler->queue_mutex);
scheduler->do_exit = true;
BLI_condition_notify_all(&scheduler->queue_cond);
BLI_mutex_unlock(&scheduler->queue_mutex);
/* delete threads */
if (scheduler->threads) {
int i;
for (i = 0; i < scheduler->num_threads; i++) {
if (pthread_join(scheduler->threads[i], NULL) != 0)
fprintf(stderr, "TaskScheduler failed to join thread %d/%d\n", i, scheduler->num_threads);
}
MEM_freeN(scheduler->threads);
}
/* Delete task thread data */
if (scheduler->task_threads) {
MEM_freeN(scheduler->task_threads);
}
/* delete leftover tasks */
for (task = scheduler->queue.first; task; task = task->next) {
if (task->free_taskdata)
MEM_freeN(task->taskdata);
}
BLI_freelistN(&scheduler->queue);
/* delete mutex/condition */
BLI_mutex_end(&scheduler->queue_mutex);
BLI_condition_end(&scheduler->queue_cond);
MEM_freeN(scheduler);
}
int BLI_task_scheduler_num_threads(TaskScheduler *scheduler)
{
return scheduler->num_threads + 1;
}
static void task_scheduler_push(TaskScheduler *scheduler, Task *task, TaskPriority priority)
{
task_pool_num_increase(task->pool);
/* add task to queue */
BLI_mutex_lock(&scheduler->queue_mutex);
if (priority == TASK_PRIORITY_HIGH)
BLI_addhead(&scheduler->queue, task);
else
BLI_addtail(&scheduler->queue, task);
BLI_condition_notify_one(&scheduler->queue_cond);
BLI_mutex_unlock(&scheduler->queue_mutex);
}
static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool)
{
Task *task, *nexttask;
size_t done = 0;
BLI_mutex_lock(&scheduler->queue_mutex);
/* free all tasks from this pool from the queue */
for (task = scheduler->queue.first; task; task = nexttask) {
nexttask = task->next;
if (task->pool == pool) {
if (task->free_taskdata)
MEM_freeN(task->taskdata);
BLI_freelinkN(&scheduler->queue, task);
done++;
}
}
BLI_mutex_unlock(&scheduler->queue_mutex);
/* notify done */
task_pool_num_decrease(pool, done);
}
/* Task Pool */
TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata)
{
TaskPool *pool = MEM_callocN(sizeof(TaskPool), "TaskPool");
pool->scheduler = scheduler;
pool->num = 0;
pool->do_cancel = false;
BLI_mutex_init(&pool->num_mutex);
BLI_condition_init(&pool->num_cond);
pool->userdata = userdata;
BLI_mutex_init(&pool->user_mutex);
/* Ensure malloc will go fine from threads,
*
* This is needed because we could be in main thread here
* and malloc could be non-threda safe at this point because
* no other jobs are running.
*/
BLI_begin_threaded_malloc();
return pool;
}
void BLI_task_pool_free(TaskPool *pool)
{
BLI_task_pool_stop(pool);
BLI_mutex_end(&pool->num_mutex);
BLI_condition_end(&pool->num_cond);
BLI_mutex_end(&pool->user_mutex);
MEM_freeN(pool);
BLI_end_threaded_malloc();
}
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run,
void *taskdata, bool free_taskdata, TaskPriority priority)
{
Task *task = MEM_callocN(sizeof(Task), "Task");
task->run = run;
task->taskdata = taskdata;
task->free_taskdata = free_taskdata;
task->pool = pool;
task_scheduler_push(pool->scheduler, task, priority);
}
void BLI_task_pool_work_and_wait(TaskPool *pool)
{
TaskScheduler *scheduler = pool->scheduler;
BLI_mutex_lock(&pool->num_mutex);
while (pool->num != 0) {
Task *task, *work_task = NULL;
bool found_task = false;
BLI_mutex_unlock(&pool->num_mutex);
BLI_mutex_lock(&scheduler->queue_mutex);
/* find task from this pool. if we get a task from another pool,
* we can get into deadlock */
for (task = scheduler->queue.first; task; task = task->next) {
if (task->pool == pool) {
work_task = task;
found_task = true;
BLI_remlink(&scheduler->queue, task);
break;
}
}
BLI_mutex_unlock(&scheduler->queue_mutex);
/* if found task, do it, otherwise wait until other tasks are done */
if (found_task) {
/* run task */
work_task->run(pool, work_task->taskdata, 0);
/* delete task */
if (work_task->free_taskdata)
MEM_freeN(work_task->taskdata);
MEM_freeN(work_task);
/* notify pool task was done */
task_pool_num_decrease(pool, 1);
}
BLI_mutex_lock(&pool->num_mutex);
if (pool->num == 0)
break;
if (!found_task)
BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
}
BLI_mutex_unlock(&pool->num_mutex);
}
void BLI_task_pool_cancel(TaskPool *pool)
{
pool->do_cancel = true;
task_scheduler_clear(pool->scheduler, pool);
/* wait until all entries are cleared */
BLI_mutex_lock(&pool->num_mutex);
while (pool->num)
BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
BLI_mutex_unlock(&pool->num_mutex);
pool->do_cancel = false;
}
void BLI_task_pool_stop(TaskPool *pool)
{
task_scheduler_clear(pool->scheduler, pool);
BLI_assert(pool->num == 0);
}
bool BLI_task_pool_cancelled(TaskPool *pool)
{
return pool->do_cancel;
}
void *BLI_task_pool_userdata(TaskPool *pool)
{
return pool->userdata;
}
ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool)
{
return &pool->user_mutex;
}
size_t BLI_task_pool_tasks_done(TaskPool *pool)
{
return pool->done;
}

View File

@ -37,6 +37,7 @@
#include "BLI_listbase.h"
#include "BLI_gsqueue.h"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "PIL_time.h"
@ -63,6 +64,9 @@ extern pthread_key_t gomp_tls_key;
static void *thread_tls_data;
#endif
/* We're using one global task scheduler for all kind of tasks. */
static TaskScheduler *task_scheduler = NULL;
/* ********** basic thread control API ************
*
* Many thread cases have an X amount of jobs, and only an Y amount of
@ -151,9 +155,26 @@ void BLI_threadapi_init(void)
void BLI_threadapi_exit(void)
{
if (task_scheduler) {
BLI_task_scheduler_free(task_scheduler);
}
BLI_spin_end(&_malloc_lock);
}
TaskScheduler *BLI_task_scheduler_get(void)
{
if (task_scheduler == NULL) {
int tot_thread = BLI_system_thread_count();
/* Do a lazy initialization, so it happes after
* command line arguments parsing
*/
task_scheduler = BLI_task_scheduler_create(tot_thread);
}
return task_scheduler;
}
/* tot = 0 only initializes malloc mutex in a safe way (see sequence.c)
* problem otherwise: scene render will kill of the mutex!
*/
@ -419,6 +440,11 @@ void BLI_mutex_unlock(ThreadMutex *mutex)
pthread_mutex_unlock(mutex);
}
bool BLI_mutex_trylock(ThreadMutex *mutex)
{
return (pthread_mutex_trylock(mutex) == 0);
}
void BLI_mutex_end(ThreadMutex *mutex)
{
pthread_mutex_destroy(mutex);
@ -563,97 +589,31 @@ void BLI_ticket_mutex_unlock(TicketMutex *ticket)
/* ************************************************ */
typedef struct ThreadedWorker {
ListBase threadbase;
void *(*work_fnct)(void *);
char busy[RE_MAX_THREAD];
int total;
int sleep_time;
} ThreadedWorker;
/* Condition */
typedef struct WorkParam {
ThreadedWorker *worker;
void *param;
int index;
} WorkParam;
static void *exec_work_fnct(void *v_param)
void BLI_condition_init(ThreadCondition *cond)
{
WorkParam *p = (WorkParam *)v_param;
void *value;
value = p->worker->work_fnct(p->param);
p->worker->busy[p->index] = 0;
MEM_freeN(p);
return value;
pthread_cond_init(cond, NULL);
}
ThreadedWorker *BLI_create_worker(void *(*do_thread)(void *), int tot, int sleep_time)
void BLI_condition_wait(ThreadCondition *cond, ThreadMutex *mutex)
{
ThreadedWorker *worker;
(void)sleep_time; /* unused */
worker = MEM_callocN(sizeof(ThreadedWorker), "threadedworker");
if (tot > RE_MAX_THREAD) {
tot = RE_MAX_THREAD;
}
else if (tot < 1) {
tot = 1;
}
worker->total = tot;
worker->work_fnct = do_thread;
BLI_init_threads(&worker->threadbase, exec_work_fnct, tot);
return worker;
pthread_cond_wait(cond, mutex);
}
void BLI_end_worker(ThreadedWorker *worker)
void BLI_condition_notify_one(ThreadCondition *cond)
{
BLI_remove_threads(&worker->threadbase);
pthread_cond_signal(cond);
}
void BLI_destroy_worker(ThreadedWorker *worker)
void BLI_condition_notify_all(ThreadCondition *cond)
{
BLI_end_worker(worker);
BLI_freelistN(&worker->threadbase);
MEM_freeN(worker);
pthread_cond_broadcast(cond);
}
void BLI_insert_work(ThreadedWorker *worker, void *param)
void BLI_condition_end(ThreadCondition *cond)
{
WorkParam *p = MEM_callocN(sizeof(WorkParam), "workparam");
int index;
if (BLI_available_threads(&worker->threadbase) == 0) {
index = worker->total;
while (index == worker->total) {
PIL_sleep_ms(worker->sleep_time);
for (index = 0; index < worker->total; index++) {
if (worker->busy[index] == 0) {
BLI_remove_thread_index(&worker->threadbase, index);
break;
}
}
}
}
else {
index = BLI_available_thread_index(&worker->threadbase);
}
worker->busy[index] = 1;
p->param = param;
p->index = index;
p->worker = worker;
BLI_insert_thread(&worker->threadbase, p);
pthread_cond_destroy(cond);
}
/* ************************************************ */

View File

@ -29,6 +29,7 @@
#include "BLI_graph.h"
#include "BLI_ghash.h"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "reeb.h"
@ -68,7 +69,8 @@ typedef struct RigGraph {
ReebGraph *link_mesh;
struct ThreadedWorker *worker;
TaskScheduler *task_scheduler;
TaskPool *task_pool;
GHash *bones_map; /* map of editbones by name */
GHash *controls_map; /* map of rigcontrols by bone pointer */

View File

@ -83,7 +83,7 @@ static RigGraph *GLOBAL_RIGG = NULL;
/*******************************************************************************************************/
void *exec_retargetArctoArc(void *param);
void exec_retargetArctoArc(TaskPool *pool, void *taskdata, int threadid);
static void RIG_calculateEdgeAngles(RigEdge *edge_first, RigEdge *edge_second);
float rollBoneByQuat(EditBone *bone, float old_up_axis[3], float qrot[4]);
@ -235,9 +235,8 @@ void RIG_freeRigGraph(BGraph *rg)
BNode *node;
BArc *arc;
#ifdef USE_THREADS
BLI_destroy_worker(rigg->worker);
#endif
BLI_task_pool_free(rigg->task_pool);
BLI_task_scheduler_free(rigg->task_scheduler);
if (rigg->link_mesh) {
REEB_freeGraph(rigg->link_mesh);
@ -284,12 +283,14 @@ static RigGraph *newRigGraph(void)
rg->free_node = NULL;
#ifdef USE_THREADS
//totthread = BKE_scene_num_threads(G.scene);
totthread = BLI_system_thread_count();
rg->worker = BLI_create_worker(exec_retargetArctoArc, totthread, 20); /* fix number of threads */
totthread = TASK_SCHEDULER_AUTO_THREADS;
#else
totthread = TASK_SCHEDULER_SINGLE_THREAD;
#endif
rg->task_scheduler = BLI_task_scheduler_create(totthread);
rg->task_pool = BLI_task_pool_create(rg->task_scheduler, NULL);
return rg;
}
@ -2133,7 +2134,6 @@ static void retargetArctoArcLength(bContext *C, RigGraph *rigg, RigArc *iarc, Ri
static void retargetArctoArc(bContext *C, RigGraph *rigg, RigArc *iarc, RigNode *inode_start)
{
#ifdef USE_THREADS
RetargetParam *p = MEM_callocN(sizeof(RetargetParam), "RetargetParam");
p->rigg = rigg;
@ -2141,22 +2141,12 @@ static void retargetArctoArc(bContext *C, RigGraph *rigg, RigArc *iarc, RigNode
p->inode_start = inode_start;
p->context = C;
BLI_insert_work(rigg->worker, p);
#else
RetargetParam p;
p.rigg = rigg;
p.iarc = iarc;
p.inode_start = inode_start;
p.context = C;
exec_retargetArctoArc(&p);
#endif
BLI_task_pool_push(rigg->task_pool, exec_retargetArctoArc, p, true, TASK_PRIORITY_HIGH);
}
void *exec_retargetArctoArc(void *param)
void exec_retargetArctoArc(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid))
{
RetargetParam *p = (RetargetParam *)param;
RetargetParam *p = (RetargetParam *)taskdata;
RigGraph *rigg = p->rigg;
RigArc *iarc = p->iarc;
bContext *C = p->context;
@ -2183,12 +2173,6 @@ void *exec_retargetArctoArc(void *param)
retargetArctoArcLength(C, rigg, iarc, inode_start);
}
}
#ifdef USE_THREADS
MEM_freeN(p);
#endif
return NULL;
}
static void matchMultiResolutionNode(RigGraph *rigg, RigNode *inode, ReebNode *top_node)
@ -2414,9 +2398,7 @@ static void retargetSubgraph(bContext *C, RigGraph *rigg, RigArc *start_arc, Rig
static void finishRetarget(RigGraph *rigg)
{
#ifdef USE_THREADS
BLI_end_worker(rigg->worker);
#endif
BLI_task_pool_work_and_wait(rigg->task_pool);
}
static void adjustGraphs(bContext *C, RigGraph *rigg)

View File

@ -236,7 +236,6 @@ struct Render
struct Object *excludeob;
ListBase render_volumes_inside;
ListBase volumes;
ListBase volume_precache_parts;
#ifdef WITH_FREESTYLE
struct Main freestyle_bmain;

View File

@ -39,6 +39,7 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_voxel.h"
#include "BLI_utildefines.h"
@ -483,81 +484,95 @@ static void *vol_precache_part_test(void *data)
}
#endif
typedef struct VolPrecacheQueue {
ThreadQueue *work;
ThreadQueue *done;
} VolPrecacheQueue;
/* Iterate over the 3d voxel grid, and fill the voxels with scattering information
*
* It's stored in memory as 3 big float grids next to each other, one for each RGB channel.
* I'm guessing the memory alignment may work out better this way for the purposes
* of doing linear interpolation, but I haven't actually tested this theory! :)
*/
static void *vol_precache_part(void *data)
typedef struct VolPrecacheState {
double lasttime;
int totparts;
} VolPrecacheState;
static void vol_precache_part(TaskPool *pool, void *taskdata, int threadid)
{
VolPrecacheQueue *queue = (VolPrecacheQueue *)data;
VolPrecachePart *pa;
VolPrecacheState *state = (VolPrecacheState *)BLI_task_pool_userdata(pool);
VolPrecachePart *pa = (VolPrecachePart *)taskdata;
Render *re = pa->re;
while ((pa = BLI_thread_queue_pop(queue->work))) {
ObjectInstanceRen *obi = pa->obi;
RayObject *tree = pa->tree;
ShadeInput *shi = pa->shi;
float scatter_col[3] = {0.f, 0.f, 0.f};
float co[3], cco[3], view[3];
int x, y, z, i;
int res[3];
ObjectInstanceRen *obi = pa->obi;
RayObject *tree = pa->tree;
ShadeInput *shi = pa->shi;
float scatter_col[3] = {0.f, 0.f, 0.f};
float co[3], cco[3], view[3];
int x, y, z, i;
int res[3];
double time;
if (pa->re->test_break && pa->re->test_break(pa->re->tbh))
break;
if (re->test_break && re->test_break(re->tbh))
return;
printf("thread id %d\n", threadid);
res[0]= pa->res[0];
res[1]= pa->res[1];
res[2]= pa->res[2];
res[0]= pa->res[0];
res[1]= pa->res[1];
res[2]= pa->res[2];
for (z= pa->minz; z < pa->maxz; z++) {
co[2] = pa->bbmin[2] + (pa->voxel[2] * (z + 0.5f));
for (z= pa->minz; z < pa->maxz; z++) {
co[2] = pa->bbmin[2] + (pa->voxel[2] * (z + 0.5f));
for (y= pa->miny; y < pa->maxy; y++) {
co[1] = pa->bbmin[1] + (pa->voxel[1] * (y + 0.5f));
for (y= pa->miny; y < pa->maxy; y++) {
co[1] = pa->bbmin[1] + (pa->voxel[1] * (y + 0.5f));
for (x=pa->minx; x < pa->maxx; x++) {
co[0] = pa->bbmin[0] + (pa->voxel[0] * (x + 0.5f));
for (x=pa->minx; x < pa->maxx; x++) {
co[0] = pa->bbmin[0] + (pa->voxel[0] * (x + 0.5f));
if (pa->re->test_break && pa->re->test_break(pa->re->tbh))
break;
/* convert from world->camera space for shading */
mul_v3_m4v3(cco, pa->viewmat, co);
i = BLI_VOXEL_INDEX(x, y, z, res);
/* don't bother if the point is not inside the volume mesh */
if (!point_inside_obi(tree, obi, cco)) {
obi->volume_precache->data_r[i] = -1.0f;
obi->volume_precache->data_g[i] = -1.0f;
obi->volume_precache->data_b[i] = -1.0f;
continue;
}
copy_v3_v3(view, cco);
normalize_v3(view);
vol_get_scattering(shi, scatter_col, cco, view);
if (re->test_break && re->test_break(re->tbh))
break;
obi->volume_precache->data_r[i] = scatter_col[0];
obi->volume_precache->data_g[i] = scatter_col[1];
obi->volume_precache->data_b[i] = scatter_col[2];
/* convert from world->camera space for shading */
mul_v3_m4v3(cco, pa->viewmat, co);
i = BLI_VOXEL_INDEX(x, y, z, res);
/* don't bother if the point is not inside the volume mesh */
if (!point_inside_obi(tree, obi, cco)) {
obi->volume_precache->data_r[i] = -1.0f;
obi->volume_precache->data_g[i] = -1.0f;
obi->volume_precache->data_b[i] = -1.0f;
continue;
}
copy_v3_v3(view, cco);
normalize_v3(view);
vol_get_scattering(shi, scatter_col, cco, view);
obi->volume_precache->data_r[i] = scatter_col[0];
obi->volume_precache->data_g[i] = scatter_col[1];
obi->volume_precache->data_b[i] = scatter_col[2];
}
}
BLI_thread_queue_push(queue->done, pa);
}
return NULL;
}
time = PIL_check_seconds_timer();
if (time - state->lasttime > 1.0) {
ThreadMutex *mutex = BLI_task_pool_user_mutex(pool);
if (BLI_mutex_trylock(mutex)) {
char str[64];
float ratio = (float)BLI_task_pool_tasks_done(pool)/(float)state->totparts;
BLI_snprintf(str, sizeof(str), IFACE_("Precaching volume: %d%%"), (int)(100.0f * ratio));
re->i.infostr = str;
re->stats_draw(re->sdh, &re->i);
re->i.infostr = NULL;
state->lasttime = time;
BLI_mutex_unlock(mutex);
}
}
}
static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Material *ma, ShadeInput *shi)
{
@ -573,9 +588,12 @@ static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Materi
shi->lay = re->lay;
}
static void precache_init_parts(Render *re, RayObject *tree, ShadeInput *shi, ObjectInstanceRen *obi, int totthread, int *parts)
static void precache_launch_parts(Render *re, RayObject *tree, ShadeInput *shi, ObjectInstanceRen *obi)
{
TaskScheduler *task_scheduler;
TaskPool *task_pool;
VolumePrecache *vp = obi->volume_precache;
VolPrecacheState state;
int i=0, x, y, z;
float voxel[3];
int sizex, sizey, sizez;
@ -584,15 +602,23 @@ static void precache_init_parts(Render *re, RayObject *tree, ShadeInput *shi, Ob
int minx, maxx;
int miny, maxy;
int minz, maxz;
int totthread = re->r.threads;
int parts[3];
if (!vp) return;
BLI_freelistN(&re->volume_precache_parts);
/* currently we just subdivide the box, number of threads per side */
parts[0] = parts[1] = parts[2] = totthread;
res = vp->res;
/* setup task scheduler */
memset(&state, 0, sizeof(state));
state.totparts = parts[0]*parts[1]*parts[2];
state.lasttime = PIL_check_seconds_timer();
task_scheduler = BLI_task_scheduler_create(totthread);
task_pool = BLI_task_pool_create(task_scheduler, &state);
/* using boundbox in worldspace */
global_bounds_obi(re, obi, bbmin, bbmax);
sub_v3_v3v3(voxel, bbmax, bbmin);
@ -636,13 +662,19 @@ static void precache_init_parts(Render *re, RayObject *tree, ShadeInput *shi, Ob
pa->miny = miny; pa->maxy = maxy;
pa->minz = minz; pa->maxz = maxz;
BLI_addtail(&re->volume_precache_parts, pa);
BLI_task_pool_push(task_pool, vol_precache_part, pa, true, TASK_PRIORITY_HIGH);
i++;
}
}
}
/* work and wait until tasks are done */
BLI_task_pool_work_and_wait(task_pool);
/* free */
BLI_task_pool_free(task_pool);
BLI_task_scheduler_free(task_scheduler);
}
/* calculate resolution from bounding box in world space */
@ -678,17 +710,8 @@ static int precache_resolution(Render *re, VolumePrecache *vp, ObjectInstanceRen
static void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Material *ma)
{
VolumePrecache *vp;
VolPrecachePart *pa;
RayObject *tree;
ShadeInput shi;
ListBase threads;
VolPrecacheQueue queue;
int parts[3] = {1, 1, 1}, totparts;
int counter=0;
int totthread = re->r.threads, thread;
double time, lasttime= PIL_check_seconds_timer();
R = *re;
@ -717,49 +740,8 @@ static void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *o
/* Need a shadeinput to calculate scattering */
precache_setup_shadeinput(re, obi, ma, &shi);
precache_init_parts(re, tree, &shi, obi, totthread, parts);
totparts = parts[0] * parts[1] * parts[2];
precache_launch_parts(re, tree, &shi, obi);
/* setup work and done queues */
queue.work = BLI_thread_queue_init();
queue.done = BLI_thread_queue_init();
BLI_thread_queue_nowait(queue.work);
for (pa= re->volume_precache_parts.first; pa; pa= pa->next)
BLI_thread_queue_push(queue.work, pa);
/* launch threads */
BLI_init_threads(&threads, vol_precache_part, totthread);
for (thread= 0; thread<totthread; thread++)
BLI_insert_thread(&threads, &queue);
/* loop waiting for work to be done */
while (counter < totparts) {
if (re->test_break && re->test_break(re->tbh))
break;
if (BLI_thread_queue_pop_timeout(queue.done, 50))
counter++;
time= PIL_check_seconds_timer();
if (time-lasttime>1.0) {
char str[64];
BLI_snprintf(str, sizeof(str), IFACE_("Precaching volume: %d%%"),
(int)(100.0f * ((float)counter / (float)totparts)));
re->i.infostr = str;
re->stats_draw(re->sdh, &re->i);
re->i.infostr = NULL;
lasttime = time;
}
}
/* free */
BLI_end_threads(&threads);
BLI_thread_queue_free(queue.work);
BLI_thread_queue_free(queue.done);
BLI_freelistN(&re->volume_precache_parts);
if (tree) {
/* TODO: makeraytree_object creates a tree and saves it on OBI,
* if we free this tree we should also clear other pointers to it */