From fe8fad54b16b01b52811fab7b002ff7ea72cb800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 6 Oct 2014 18:58:41 +0200 Subject: Use the generic task scheduler for threaded particle tasks, i.e. distribution and path caching for child particles. This gives a significant improvement of viewport playback performance with higher child particle counts. Particles previously used their own threads and had a rather high limit for threading. Also threading apparently was disabled because only 1 thread was being used ... --- source/blender/blenkernel/BKE_particle.h | 11 +- source/blender/blenkernel/intern/particle.c | 158 +++++++------- source/blender/blenkernel/intern/particle_system.c | 227 ++++++++++++--------- 3 files changed, 203 insertions(+), 193 deletions(-) (limited to 'source') diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 39cf5505382..15d3c2f5ee8 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -160,11 +160,11 @@ typedef struct ParticleThreadContext { float *vg_effector; } ParticleThreadContext; -typedef struct ParticleThread { +typedef struct ParticleTask { ParticleThreadContext *ctx; struct RNG *rng, *rng_path; - int num, tot; -} ParticleThread; + int begin, end; +} ParticleTask; typedef struct ParticleBillboardData { struct Object *ob; @@ -347,8 +347,9 @@ void psys_get_dupli_texture(struct ParticleSystem *psys, struct ParticleSettings void psys_get_dupli_path_transform(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ChildParticle *cpa, struct ParticleCacheKey *cache, float mat[4][4], float *scale); -ParticleThread *psys_threads_create(struct ParticleSimulationData *sim); -void psys_threads_free(ParticleThread *threads); +void psys_thread_context_init(struct ParticleThreadContext *ctx, struct ParticleSimulationData *sim); +void psys_tasks_create(struct ParticleThreadContext *ctx, int totpart, struct ParticleTask **r_tasks, int *r_numtasks); +void psys_tasks_free(struct ParticleTask *tasks, int numtasks); void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3], float zvec[3], float center[3]); void psys_apply_hair_lattice(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index ed2d5d2cf02..6a5dc5fa66a 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -53,6 +53,7 @@ #include "BLI_utildefines.h" #include "BLI_kdtree.h" #include "BLI_rand.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_linklist.h" @@ -2441,17 +2442,15 @@ static void get_strand_normal(Material *ma, const float surfnor[3], float surfdi copy_v3_v3(nor, vnor); } -static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float cfra, int editupdate) +static bool psys_thread_context_init_path(ParticleThreadContext *ctx, ParticleSimulationData *sim, Scene *scene, float cfra, int editupdate) { - ParticleThreadContext *ctx = threads[0].ctx; -/* Object *ob = ctx->sim.ob; */ - ParticleSystem *psys = ctx->sim.psys; + ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; -/* ParticleEditSettings *pset = &scene->toolsettings->particle; */ int totparent = 0, between = 0; - int steps = (int)pow(2.0, (double)part->draw_step); + int steps = 1 << part->draw_step; int totchild = psys->totchild; - int i, seed, totthread = threads[0].tot; + + psys_thread_context_init(ctx, sim); /*---start figuring out what is actually wanted---*/ if (psys_in_edit_mode(scene, psys)) { @@ -2460,7 +2459,7 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c if (psys->renderdata == 0 && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0) totchild = 0; - steps = (int)pow(2.0, (double)pset->draw_step); + steps = 1 << pset->draw_step; } if (totchild && part->childtype == PART_CHILD_FACES) { @@ -2480,18 +2479,8 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c totparent = MIN2(totparent, totchild); } - if (totchild == 0) return 0; - - /* init random number generator */ - seed = 31415926 + ctx->sim.psys->seed; - - if (ctx->editupdate || totchild < 10000) - totthread = 1; - - for (i = 0; i < totthread; i++) { - threads[i].rng_path = BLI_rng_new(seed); - threads[i].tot = totthread; - } + if (totchild == 0) + return false; /* fill context values */ ctx->between = between; @@ -2514,21 +2503,21 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c if (psys->part->flag & PART_CHILD_EFFECT) ctx->vg_effector = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_EFFECTOR); - /* set correct ipo timing */ -#if 0 // XXX old animation system - if (part->flag & PART_ABS_TIME && part->ipo) { - calc_ipo(part->ipo, cfra); - execute_ipo((ID *)part, part->ipo); - } -#endif // XXX old animation system + return true; +} - return 1; +static void psys_task_init_path(ParticleTask *task, ParticleSimulationData *sim) +{ + /* init random number generator */ + int seed = 31415926 + sim->psys->seed; + + task->rng_path = BLI_rng_new(seed); } /* note: this function must be thread safe, except for branching! */ -static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) +static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) { - ParticleThreadContext *ctx = thread->ctx; + ParticleThreadContext *ctx = task->ctx; Object *ob = ctx->sim.ob; ParticleSystem *psys = ctx->sim.psys; ParticleSettings *part = psys->part; @@ -2794,89 +2783,80 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle child_keys->steps = -1; } -static void *exec_child_path_cache(void *data) +static void exec_child_path_cache(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) { - ParticleThread *thread = (ParticleThread *)data; - ParticleThreadContext *ctx = thread->ctx; + ParticleTask *task = taskdata; + ParticleThreadContext *ctx = task->ctx; ParticleSystem *psys = ctx->sim.psys; ParticleCacheKey **cache = psys->childcache; ChildParticle *cpa; - int i, totchild = ctx->totchild, first = 0; + int i; - if (thread->tot > 1) { - first = ctx->parent_pass ? 0 : ctx->totparent; - totchild = ctx->parent_pass ? ctx->totparent : ctx->totchild; + cpa = psys->child + task->begin; + for (i = task->begin; i < task->end; ++i, ++cpa) { + psys_thread_create_path(task, cpa, cache[i], i); } - - cpa = psys->child + first + thread->num; - for (i = first + thread->num; i < totchild; i += thread->tot, cpa += thread->tot) - psys_thread_create_path(thread, cpa, cache[i], i); - - return 0; } void psys_cache_child_paths(ParticleSimulationData *sim, float cfra, int editupdate) { - ParticleThread *pthreads; - ParticleThreadContext *ctx; - ListBase threads; - int i, totchild, totparent, totthread; - + TaskScheduler *task_scheduler; + TaskPool *task_pool; + ParticleThreadContext ctx; + ParticleTask *tasks_parent, *tasks_child; + int numtasks_parent, numtasks_child; + int i, totchild, totparent; + if (sim->psys->flag & PSYS_GLOBAL_HAIR) return; - - pthreads = psys_threads_create(sim); - - if (!psys_threads_init_path(pthreads, sim->scene, cfra, editupdate)) { - psys_threads_free(pthreads); + + /* create a task pool for child path tasks */ + if (!psys_thread_context_init_path(&ctx, sim, sim->scene, cfra, editupdate)) return; - } - - ctx = pthreads[0].ctx; - totchild = ctx->totchild; - totparent = ctx->totparent; - + + task_scheduler = BLI_task_scheduler_get(); + task_pool = BLI_task_pool_create(task_scheduler, &ctx); + totchild = ctx.totchild; + totparent = ctx.totparent; + if (editupdate && sim->psys->childcache && totchild == sim->psys->totchildcache) { ; /* just overwrite the existing cache */ } else { /* clear out old and create new empty path cache */ free_child_path_cache(sim->psys); - sim->psys->childcache = psys_alloc_path_cache_buffers(&sim->psys->childcachebufs, totchild, ctx->steps + 1); + sim->psys->childcache = psys_alloc_path_cache_buffers(&sim->psys->childcachebufs, totchild, ctx.steps + 1); sim->psys->totchildcache = totchild; } - - totthread = pthreads[0].tot; - - if (totthread > 1) { - - /* make virtual child parents thread safe by calculating them first */ - if (totparent) { - BLI_init_threads(&threads, exec_child_path_cache, totthread); - - for (i = 0; i < totthread; i++) { - pthreads[i].ctx->parent_pass = 1; - BLI_insert_thread(&threads, &pthreads[i]); - } - - BLI_end_threads(&threads); - - for (i = 0; i < totthread; i++) - pthreads[i].ctx->parent_pass = 0; - } - - BLI_init_threads(&threads, exec_child_path_cache, totthread); - - for (i = 0; i < totthread; i++) - BLI_insert_thread(&threads, &pthreads[i]); - - BLI_end_threads(&threads); + + /* cache parent paths */ + ctx.parent_pass = 1; + psys_tasks_create(&ctx, totparent, &tasks_parent, &numtasks_parent); + for (i = 0; i < numtasks_parent; ++i) { + ParticleTask *task = &tasks_parent[i]; + + psys_task_init_path(task, sim); + BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW); } - else - exec_child_path_cache(&pthreads[0]); + BLI_task_pool_work_and_wait(task_pool); + + /* cache child paths */ + ctx.parent_pass = 0; + psys_tasks_create(&ctx, totchild, &tasks_child, &numtasks_child); + for (i = 0; i < numtasks_child; ++i) { + ParticleTask *task = &tasks_child[i]; + + psys_task_init_path(task, sim); + BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW); + } + BLI_task_pool_work_and_wait(task_pool); - psys_threads_free(pthreads); + BLI_task_pool_free(task_pool); + + psys_tasks_free(tasks_parent, numtasks_parent); + psys_tasks_free(tasks_child, numtasks_child); } + /* figure out incremental rotations along path starting from unit quat */ static void cache_key_incremental_rotation(ParticleCacheKey *key0, ParticleCacheKey *key1, ParticleCacheKey *key2, float *prev_tangent, int i) { diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index a54488f15db..cd50d95eb89 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -68,6 +68,7 @@ #include "BLI_kdtree.h" #include "BLI_kdopbvh.h" #include "BLI_sort.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_linklist.h" @@ -789,7 +790,7 @@ static int distribute_binary_search(float *sum, int n, float value) /* note: this function must be thread safe, for from == PART_FROM_CHILD */ #define ONLY_WORKING_WITH_PA_VERTS 0 -static void distribute_threads_exec(ParticleThread *thread, ParticleData *pa, ChildParticle *cpa, int p) +static void distribute_threads_exec(ParticleTask *thread, ParticleData *pa, ChildParticle *cpa, int p) { ParticleThreadContext *ctx= thread->ctx; Object *ob= ctx->sim.ob; @@ -979,36 +980,40 @@ static void distribute_threads_exec(ParticleThread *thread, ParticleData *pa, Ch BLI_rng_skip(thread->rng, rng_skip_tot); } -static void *distribute_threads_exec_cb(void *data) +static void exec_distribute_parent(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) { - ParticleThread *thread= (ParticleThread*)data; - ParticleSystem *psys= thread->ctx->sim.psys; + ParticleTask *task = taskdata; + ParticleSystem *psys= task->ctx->sim.psys; ParticleData *pa; - ChildParticle *cpa; - int p, totpart; - - if (thread->ctx->from == PART_FROM_CHILD) { - totpart= psys->totchild; - cpa= psys->child; - - for (p=0; pctx->skip) /* simplification skip */ - BLI_rng_skip(thread->rng, PSYS_RND_DIST_SKIP * thread->ctx->skip[p]); + int p; + + pa= psys->particles + task->begin; + for (p = task->begin; p < task->end; ++p, ++pa) + distribute_threads_exec(task, pa, NULL, p); +} - if ((p+thread->num) % thread->tot == 0) - distribute_threads_exec(thread, NULL, cpa, p); - else /* thread skip */ - BLI_rng_skip(thread->rng, PSYS_RND_DIST_SKIP); - } +static void exec_distribute_child(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + ParticleTask *task = taskdata; + ParticleSystem *psys = task->ctx->sim.psys; + ChildParticle *cpa; + int p; + + /* RNG skipping at the beginning */ + cpa = psys->child; + for (p = 0; p < task->begin; ++p, ++cpa) { + if (task->ctx->skip) /* simplification skip */ + BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP * task->ctx->skip[p]); + + BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP); } - else { - totpart= psys->totpart; - pa= psys->particles + thread->num; - for (p=thread->num; ptot, pa+=thread->tot) - distribute_threads_exec(thread, pa, NULL, p); + + for (; p < task->end; ++p, ++cpa) { + if (task->ctx->skip) /* simplification skip */ + BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP * task->ctx->skip[p]); + + distribute_threads_exec(task, NULL, cpa, p); } - - return 0; } static int distribute_compare_orig_index(const void *p1, const void *p2, void *user_data) @@ -1061,18 +1066,19 @@ static void distribute_invalid(Scene *scene, ParticleSystem *psys, int from) /* Creates a distribution of coordinates on a DerivedMesh */ /* This is to denote functionality that does not yet work with mesh - only derived mesh */ -static int distribute_threads_init_data(ParticleThread *threads, Scene *scene, DerivedMesh *finaldm, int from) +static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, ParticleSimulationData *sim, int from) { - ParticleThreadContext *ctx= threads[0].ctx; - Object *ob= ctx->sim.ob; - ParticleSystem *psys= ctx->sim.psys; + Scene *scene = sim->scene; + DerivedMesh *finaldm = sim->psmd->dm; + Object *ob = sim->ob; + ParticleSystem *psys= sim->psys; ParticleData *pa=0, *tpars= 0; ParticleSettings *part; ParticleSeam *seams= 0; KDTree *tree=0; DerivedMesh *dm= NULL; float *jit= NULL; - int i, seed, p=0, totthread= threads[0].tot; + int i, p=0; int cfrom=0; int totelem=0, totpart, *particle_element=0, children=0, totseam=0; int jitlevel= 1, distr; @@ -1081,18 +1087,20 @@ static int distribute_threads_init_data(ParticleThread *threads, Scene *scene, D if (ELEM(NULL, ob, psys, psys->part)) return 0; - + part=psys->part; totpart=psys->totpart; if (totpart==0) return 0; - + if (!finaldm->deformedOnly && !finaldm->getTessFaceDataArray(finaldm, CD_ORIGINDEX)) { printf("Can't create particles with the current modifier stack, disable destructive modifiers\n"); // XXX error("Can't paint with the current modifier stack, disable destructive modifiers"); return 0; } - + + psys_thread_context_init(ctx, sim); + /* First handle special cases */ if (from == PART_FROM_CHILD) { /* Simple children */ @@ -1392,52 +1400,54 @@ static int distribute_threads_init_data(ParticleThread *threads, Scene *scene, D alloc_child_particles(psys, totpart); } - if (!children || psys->totchild < 10000) - totthread= 1; - - seed= 31415926 + ctx->sim.psys->seed; - for (i=0; ipsys->seed; + + task->rng = BLI_rng_new(seed); +} + static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) { + TaskScheduler *task_scheduler; + TaskPool *task_pool; + ParticleThreadContext ctx; + ParticleTask *tasks; DerivedMesh *finaldm = sim->psmd->dm; - ListBase threads; - ParticleThread *pthreads; - ParticleThreadContext *ctx; - int i, totthread; - - pthreads= psys_threads_create(sim); - - if (!distribute_threads_init_data(pthreads, sim->scene, finaldm, from)) { - psys_threads_free(pthreads); + int i, totpart, numtasks; + + /* create a task pool for distribution tasks */ + if (!psys_thread_context_init_distribute(&ctx, sim, from)) return; + + task_scheduler = BLI_task_scheduler_get(); + task_pool = BLI_task_pool_create(task_scheduler, &ctx); + + totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart); + psys_tasks_create(&ctx, totpart, &tasks, &numtasks); + for (i = 0; i < numtasks; ++i) { + ParticleTask *task = &tasks[i]; + + psys_task_init_distribute(task, sim); + if (from == PART_FROM_CHILD) + BLI_task_pool_push(task_pool, exec_distribute_child, task, false, TASK_PRIORITY_LOW); + else + BLI_task_pool_push(task_pool, exec_distribute_parent, task, false, TASK_PRIORITY_LOW); } - - totthread= pthreads[0].tot; - if (totthread > 1) { - BLI_init_threads(&threads, distribute_threads_exec_cb, totthread); - - for (i=0; iob, finaldm, sim->psys); - - ctx= pthreads[0].ctx; - if (ctx->dm != finaldm) - ctx->dm->release(ctx->dm); - - psys_threads_free(pthreads); + + if (ctx.dm != finaldm) + ctx.dm->release(ctx.dm); + + psys_tasks_free(tasks, numtasks); } /* ready for future use, to emit particles without geometry */ @@ -1470,35 +1480,55 @@ static void distribute_particles(ParticleSimulationData *sim, int from) } /* threaded child particle distribution and path caching */ -ParticleThread *psys_threads_create(ParticleSimulationData *sim) +void psys_thread_context_init(ParticleThreadContext *ctx, ParticleSimulationData *sim) { - ParticleThread *threads; - ParticleThreadContext *ctx; - int i, totthread = BKE_scene_num_threads(sim->scene); - - threads= MEM_callocN(sizeof(ParticleThread)*totthread, "ParticleThread"); - ctx= MEM_callocN(sizeof(ParticleThreadContext), "ParticleThreadContext"); - + memset(ctx, 0, sizeof(ParticleThreadContext)); ctx->sim = *sim; - ctx->dm= ctx->sim.psmd->dm; - ctx->ma= give_current_material(sim->ob, sim->psys->part->omat); - - memset(threads, 0, sizeof(ParticleThread)*totthread); + ctx->dm = ctx->sim.psmd->dm; + ctx->ma = give_current_material(sim->ob, sim->psys->part->omat); +} - for (i=0; ivg_length) MEM_freeN(ctx->vg_length); @@ -1529,15 +1559,14 @@ void psys_threads_free(ParticleThread *threads) BLI_kdtree_free(ctx->tree); /* threads */ - for (i=0; i