Point Cache: use job system for bake operators.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D1731
This commit is contained in:
Kévin Dietrich 2016-01-17 02:01:41 +01:00
parent 75cfc81ec3
commit 45b6893e70
5 changed files with 201 additions and 199 deletions

View File

@ -178,11 +178,9 @@ typedef struct PTCacheBaker {
int anim_init; int anim_init;
int quick_step; int quick_step;
struct PTCacheID *pid; struct PTCacheID *pid;
int (*break_test)(void *data);
void *break_data; void (*update_progress)(void *data, float progress, int *cancel);
void (*progressbar)(void *data, int num); void *bake_job;
void (*progressend)(void *data);
void *progresscontext;
} PTCacheBaker; } PTCacheBaker;
/* PTCacheEditKey->flag */ /* PTCacheEditKey->flag */

View File

@ -3111,33 +3111,18 @@ void BKE_ptcache_quick_cache_all(Main *bmain, Scene *scene)
PTCacheBaker baker; PTCacheBaker baker;
baker.bake=0; baker.bake=0;
baker.break_data=NULL;
baker.break_test=NULL;
baker.pid=NULL; baker.pid=NULL;
baker.progressbar=NULL;
baker.progressend=NULL;
baker.progresscontext=NULL;
baker.render=0; baker.render=0;
baker.anim_init = 0; baker.anim_init = 0;
baker.main=bmain; baker.main=bmain;
baker.scene=scene; baker.scene=scene;
baker.quick_step=scene->physics_settings.quick_cache_step; baker.quick_step=scene->physics_settings.quick_cache_step;
baker.update_progress = NULL;
baker.bake_job = NULL;
BKE_ptcache_bake(&baker); BKE_ptcache_bake(&baker);
} }
/* Simulation thread, no need for interlocks as data written in both threads
* are only unitary integers (I/O assumed to be atomic for them) */
typedef struct {
int break_operation;
int thread_ended;
int endframe;
int step;
int *cfra_ptr;
Main *main;
Scene *scene;
} ptcache_bake_data;
static void ptcache_dt_to_str(char *str, double dtime) static void ptcache_dt_to_str(char *str, double dtime)
{ {
if (dtime > 60.0) { if (dtime > 60.0) {
@ -3150,52 +3135,6 @@ static void ptcache_dt_to_str(char *str, double dtime)
sprintf(str, "%is", ((int)dtime) % 60); sprintf(str, "%is", ((int)dtime) % 60);
} }
static void *ptcache_bake_thread(void *ptr)
{
bool use_timer = false;
int sfra, efra;
double stime, ptime, ctime, fetd;
char run[32], cur[32], etd[32];
ptcache_bake_data *data = (ptcache_bake_data*)ptr;
stime = ptime = PIL_check_seconds_timer();
sfra = *data->cfra_ptr;
efra = data->endframe;
for (; (*data->cfra_ptr <= data->endframe) && !data->break_operation; *data->cfra_ptr+=data->step) {
BKE_scene_update_for_newframe(G.main->eval_ctx, data->main, data->scene, data->scene->lay);
if (G.background) {
printf("bake: frame %d :: %d\n", (int)*data->cfra_ptr, data->endframe);
}
else {
ctime = PIL_check_seconds_timer();
fetd = (ctime-ptime)*(efra-*data->cfra_ptr)/data->step;
if (use_timer || fetd > 60.0) {
use_timer = true;
ptcache_dt_to_str(cur, ctime-ptime);
ptcache_dt_to_str(run, ctime-stime);
ptcache_dt_to_str(etd, fetd);
printf("Baked for %s, current frame: %i/%i (%.3fs), ETC: %s\r", run, *data->cfra_ptr-sfra+1, efra-sfra+1, ctime-ptime, etd);
}
ptime = ctime;
}
}
if (use_timer) {
/* start with newline because of \r above */
ptcache_dt_to_str(run, PIL_check_seconds_timer()-stime);
printf("\nBake %s %s (%i frames simulated).\n", (data->break_operation ? "canceled after" : "finished in"), run, *data->cfra_ptr-sfra);
}
data->thread_ended = true;
return NULL;
}
/* if bake is not given run simulations to current frame */ /* if bake is not given run simulations to current frame */
void BKE_ptcache_bake(PTCacheBaker *baker) void BKE_ptcache_bake(PTCacheBaker *baker)
{ {
@ -3208,18 +3147,9 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
PointCache *cache = NULL; PointCache *cache = NULL;
float frameleno = scene->r.framelen; float frameleno = scene->r.framelen;
int cfrao = CFRA; int cfrao = CFRA;
int startframe = MAXFRAME; int startframe = MAXFRAME, endframe = MAXFRAME;
int bake = baker->bake; int bake = baker->bake;
int render = baker->render; int render = baker->render;
ListBase threads;
ptcache_bake_data thread_data;
int progress, old_progress;
thread_data.endframe = baker->anim_init ? scene->r.sfra : CFRA;
thread_data.step = baker->quick_step;
thread_data.cfra_ptr = &CFRA;
thread_data.scene = baker->scene;
thread_data.main = baker->main;
G.is_break = false; G.is_break = false;
@ -3261,11 +3191,11 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
startframe = MAX2(cache->last_exact, cache->startframe); startframe = MAX2(cache->last_exact, cache->startframe);
if (bake) { if (bake) {
thread_data.endframe = cache->endframe; endframe = cache->endframe;
cache->flag |= PTCACHE_BAKING; cache->flag |= PTCACHE_BAKING;
} }
else { else {
thread_data.endframe = MIN2(thread_data.endframe, cache->endframe); endframe = MIN2(endframe, cache->endframe);
} }
cache->flag &= ~PTCACHE_BAKED; cache->flag &= ~PTCACHE_BAKED;
@ -3300,7 +3230,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
cache->flag |= PTCACHE_BAKING; cache->flag |= PTCACHE_BAKING;
if (bake) if (bake)
thread_data.endframe = MAX2(thread_data.endframe, cache->endframe); endframe = MAX2(endframe, cache->endframe);
} }
cache->flag &= ~PTCACHE_BAKED; cache->flag &= ~PTCACHE_BAKED;
@ -3313,46 +3243,63 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
CFRA = startframe; CFRA = startframe;
scene->r.framelen = 1.0; scene->r.framelen = 1.0;
thread_data.break_operation = false;
thread_data.thread_ended = false;
old_progress = -1;
WM_cursor_wait(1); /* bake */
bool use_timer = false;
double stime, ptime, ctime, fetd;
char run[32], cur[32], etd[32];
int cancel = 0;
stime = ptime = PIL_check_seconds_timer();
for (int fr = CFRA; fr <= endframe; fr += baker->quick_step, CFRA = fr) {
BKE_scene_update_for_newframe(G.main->eval_ctx, bmain, scene, scene->lay);
if (baker->update_progress) {
float progress = ((float)(CFRA - startframe)/(float)(endframe - startframe));
baker->update_progress(baker->bake_job, progress, &cancel);
}
if (G.background) { if (G.background) {
ptcache_bake_thread((void*)&thread_data); printf("bake: frame %d :: %d\n", CFRA, endframe);
} }
else { else {
BLI_init_threads(&threads, ptcache_bake_thread, 1); ctime = PIL_check_seconds_timer();
BLI_insert_thread(&threads, (void*)&thread_data);
while (thread_data.thread_ended == false) { fetd = (ctime - ptime) * (endframe - CFRA) / baker->quick_step;
if (bake) if (use_timer || fetd > 60.0) {
progress = (int)(100.0f * (float)(CFRA - startframe)/(float)(thread_data.endframe-startframe)); use_timer = true;
else
progress = CFRA;
/* NOTE: baking should not redraw whole ui as this slows things down */ ptcache_dt_to_str(cur, ctime - ptime);
if ((baker->progressbar) && (progress != old_progress)) { ptcache_dt_to_str(run, ctime - stime);
baker->progressbar(baker->progresscontext, progress); ptcache_dt_to_str(etd, fetd);
old_progress = progress;
printf("Baked for %s, current frame: %i/%i (%.3fs), ETC: %s\r",
run, CFRA - startframe + 1, endframe - startframe + 1, ctime - ptime, etd);
}
ptime = ctime;
} }
/* Delay to lessen CPU load from UI thread */ /* Delay to lessen CPU load from UI thread */
PIL_sleep_ms(200); PIL_sleep_ms(200);
/* NOTE: breaking baking should leave calculated frames in cache, not clear it */ /* NOTE: breaking baking should leave calculated frames in cache, not clear it */
if (blender_test_break() && !thread_data.break_operation) { if ((cancel || G.is_break)) {
thread_data.break_operation = true; break;
if (baker->progressend)
baker->progressend(baker->progresscontext);
WM_cursor_wait(1);
}
} }
BLI_end_threads(&threads); CFRA += 1;
} }
if (use_timer) {
/* start with newline because of \r above */
ptcache_dt_to_str(run, PIL_check_seconds_timer()-stime);
printf("\nBake %s %s (%i frames simulated).\n", (cancel ? "canceled after" : "finished in"), run, CFRA - startframe);
}
/* clear baking flag */ /* clear baking flag */
if (pid) { if (pid) {
cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED); cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED);
@ -3375,7 +3322,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
cache = pid->cache; cache = pid->cache;
if (thread_data.step > 1) if (baker->quick_step > 1)
cache->flag &= ~(PTCACHE_BAKING|PTCACHE_OUTDATED); cache->flag &= ~(PTCACHE_BAKING|PTCACHE_OUTDATED);
else else
cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED); cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED);
@ -3399,13 +3346,6 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay); BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay);
} }
if (thread_data.break_operation)
WM_cursor_wait(0);
else if (baker->progressend)
baker->progressend(baker->progresscontext);
WM_cursor_wait(0);
/* TODO: call redraw all windows somehow */ /* TODO: call redraw all windows somehow */
} }
/* Helpers */ /* Helpers */

View File

@ -31,12 +31,15 @@
#include <stdlib.h> #include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h" #include "BLI_blenlib.h"
#include "BLI_utildefines.h" #include "BLI_utildefines.h"
#include "DNA_scene_types.h" #include "DNA_scene_types.h"
#include "BKE_context.h" #include "BKE_context.h"
#include "BKE_screen.h"
#include "BKE_global.h" #include "BKE_global.h"
#include "BKE_main.h" #include "BKE_main.h"
#include "BKE_particle.h" #include "BKE_particle.h"
@ -52,10 +55,6 @@
#include "physics_intern.h" #include "physics_intern.h"
static int cache_break_test(void *UNUSED(cbd))
{
return (G.is_break == true);
}
static int ptcache_bake_all_poll(bContext *C) static int ptcache_bake_all_poll(bContext *C)
{ {
Scene *scene= CTX_data_scene(C); Scene *scene= CTX_data_scene(C);
@ -72,15 +71,85 @@ static int ptcache_poll(bContext *C)
return (ptr.data && ptr.id.data); return (ptr.data && ptr.id.data);
} }
static void bake_console_progress(void *UNUSED(arg), int nr) typedef struct PointCacheJob {
void *owner;
short *stop, *do_update;
float *progress;
PTCacheBaker *baker;
Object *ob;
ListBase pidlist;
} PointCacheJob;
static void ptcache_job_free(void *customdata)
{ {
printf("\rbake: %3i%%", nr); PointCacheJob *job = customdata;
fflush(stdout); BLI_freelistN(&job->pidlist);
MEM_freeN(job->baker);
MEM_freeN(job);
} }
static void bake_console_progress_end(void *UNUSED(arg)) static int ptcache_job_break(void *customdata)
{ {
printf("\rbake: done!\n"); PointCacheJob *job = customdata;
if (G.is_break) {
return 1;
}
if (job->stop && *(job->stop)) {
return 1;
}
return 0;
}
static void ptcache_job_update(void *customdata, float progress, int *cancel)
{
PointCacheJob *job = customdata;
if (ptcache_job_break(job)) {
*cancel = 1;
}
*(job->do_update) = true;
*(job->progress) = progress;
}
static void ptcache_job_startjob(void *customdata, short *stop, short *do_update, float *progress)
{
PointCacheJob *job = customdata;
job->stop = stop;
job->do_update = do_update;
job->progress = progress;
G.is_break = false;
/* XXX annoying hack: needed to prevent data corruption when changing
* scene frame in separate threads
*/
G.is_rendering = true;
BKE_spacedata_draw_locks(true);
BKE_ptcache_bake(job->baker);
*do_update = true;
*stop = 0;
}
static void ptcache_job_endjob(void *customdata)
{
PointCacheJob *job = customdata;
Scene *scene = job->baker->scene;
G.is_rendering = false;
BKE_spacedata_draw_locks(false);
WM_set_locked_interface(G.main->wm.first, false);
WM_main_add_notifier(NC_SCENE | ND_FRAME, scene);
WM_main_add_notifier(NC_OBJECT | ND_POINTCACHE, job->ob);
} }
static void ptcache_free_bake(PointCache *cache) static void ptcache_free_bake(PointCache *cache)
@ -101,40 +170,40 @@ static int ptcache_bake_all_exec(bContext *C, wmOperator *op)
{ {
Main *bmain = CTX_data_main(C); Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
wmWindow *win = G.background ? NULL : CTX_wm_window(C);
PTCacheBaker baker;
baker.main = bmain; PTCacheBaker *baker = MEM_mallocN(sizeof(PTCacheBaker), "PTCacheBaker");
baker.scene = scene;
baker.pid = NULL;
baker.bake = RNA_boolean_get(op->ptr, "bake");
baker.render = 0;
baker.anim_init = 0;
baker.quick_step = 1;
baker.break_test = cache_break_test;
baker.break_data = NULL;
/* Disabled for now as this doesn't work properly, baker->main = bmain;
* and pointcache baking will be reimplemented with baker->scene = scene;
* the job system soon anyways. */ baker->pid = NULL;
if (win) { baker->bake = RNA_boolean_get(op->ptr, "bake");
baker.progressbar = (void (*)(void *, int))WM_cursor_time; baker->render = 0;
baker.progressend = (void (*)(void *))WM_cursor_modal_restore; baker->anim_init = 0;
baker.progresscontext = win; baker->quick_step = 1;
} baker->update_progress = ptcache_job_update;
else {
baker.progressbar = bake_console_progress;
baker.progressend = bake_console_progress_end;
baker.progresscontext = NULL;
}
BKE_ptcache_bake(&baker); PointCacheJob *job = MEM_mallocN(sizeof(PointCacheJob), "PointCacheJob");
job->baker = baker;
job->ob = NULL;
job->pidlist.first = NULL;
job->pidlist.last = NULL;
WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene); baker->bake_job = job;
WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, NULL);
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Point Cache",
WM_JOB_PROGRESS, WM_JOB_TYPE_POINTCACHE);
WM_jobs_customdata_set(wm_job, job, ptcache_job_free);
WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_POINTCACHE, NC_OBJECT | ND_POINTCACHE);
WM_jobs_callbacks(wm_job, ptcache_job_startjob, NULL, NULL, ptcache_job_endjob);
WM_set_locked_interface(CTX_wm_manager(C), true);
WM_jobs_start(CTX_wm_manager(C), wm_job);
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
} }
static int ptcache_free_bake_all_exec(bContext *C, wmOperator *UNUSED(op)) static int ptcache_free_bake_all_exec(bContext *C, wmOperator *UNUSED(op))
{ {
Scene *scene= CTX_data_scene(C); Scene *scene= CTX_data_scene(C);
@ -189,59 +258,54 @@ void PTCACHE_OT_free_bake_all(wmOperatorType *ot)
/* flags */ /* flags */
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
} }
static int ptcache_bake_exec(bContext *C, wmOperator *op) static int ptcache_bake_exec(bContext *C, wmOperator *op)
{ {
Main *bmain = CTX_data_main(C); Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
wmWindow *win = G.background ? NULL : CTX_wm_window(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache); PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
Object *ob = ptr.id.data; Object *ob = ptr.id.data;
PointCache *cache = ptr.data; PointCache *cache = ptr.data;
PTCacheBaker baker;
PTCacheID *pid;
ListBase pidlist;
BKE_ptcache_ids_from_object(&pidlist, ob, scene, MAX_DUPLI_RECUR); PTCacheBaker *baker = MEM_mallocN(sizeof(PTCacheBaker), "PTCacheBaker");
baker->main = bmain;
baker->scene = scene;
baker->bake = RNA_boolean_get(op->ptr, "bake");
baker->render = 0;
baker->anim_init = 0;
baker->quick_step = 1;
baker->update_progress = ptcache_job_update;
baker->pid = NULL;
for (pid=pidlist.first; pid; pid=pid->next) { PointCacheJob *job = MEM_mallocN(sizeof(PointCacheJob), "PointCacheJob");
if (pid->cache == cache) job->baker = baker;
job->ob = ob;
BKE_ptcache_ids_from_object(&job->pidlist, ob, scene, MAX_DUPLI_RECUR);
for (PTCacheID *pid = job->pidlist.first; pid; pid = pid->next) {
if (pid->cache == cache) {
baker->pid = pid;
break; break;
} }
baker.main = bmain;
baker.scene = scene;
baker.pid = pid;
baker.bake = RNA_boolean_get(op->ptr, "bake");
baker.render = 0;
baker.anim_init = 0;
baker.quick_step = 1;
baker.break_test = cache_break_test;
baker.break_data = NULL;
/* Disabled for now as this doesn't work properly,
* and pointcache baking will be reimplemented with
* the job system soon anyways. */
if (win) {
baker.progressbar = (void (*)(void *, int))WM_cursor_time;
baker.progressend = (void (*)(void *))WM_cursor_modal_restore;
baker.progresscontext = win;
}
else {
printf("\n"); /* empty first line before console reports */
baker.progressbar = bake_console_progress;
baker.progressend = bake_console_progress_end;
baker.progresscontext = NULL;
} }
BKE_ptcache_bake(&baker); baker->bake_job = job;
BLI_freelistN(&pidlist); wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Point Cache",
WM_JOB_PROGRESS, WM_JOB_TYPE_POINTCACHE);
WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene); WM_jobs_customdata_set(wm_job, job, ptcache_job_free);
WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob); WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_POINTCACHE, NC_OBJECT | ND_POINTCACHE);
WM_jobs_callbacks(wm_job, ptcache_job_startjob, NULL, NULL, ptcache_job_endjob);
WM_set_locked_interface(CTX_wm_manager(C), true);
WM_jobs_start(CTX_wm_manager(C), wm_job);
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
} }
static int ptcache_free_bake_exec(bContext *C, wmOperator *UNUSED(op)) static int ptcache_free_bake_exec(bContext *C, wmOperator *UNUSED(op))
{ {
PointerRNA ptr= CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache); PointerRNA ptr= CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);

View File

@ -3050,9 +3050,8 @@ static void update_physics_cache(Render *re, Scene *scene, int UNUSED(anim_init)
baker.render = 1; baker.render = 1;
baker.anim_init = 1; baker.anim_init = 1;
baker.quick_step = 1; baker.quick_step = 1;
baker.break_test = re->test_break; baker.update_progress = NULL;
baker.break_data = re->tbh; baker.bake_job = NULL;
baker.progressbar = NULL;
BKE_ptcache_bake(&baker); BKE_ptcache_bake(&baker);
} }

View File

@ -442,6 +442,7 @@ enum {
WM_JOB_TYPE_CLIP_PREFETCH, WM_JOB_TYPE_CLIP_PREFETCH,
WM_JOB_TYPE_SEQ_BUILD_PROXY, WM_JOB_TYPE_SEQ_BUILD_PROXY,
WM_JOB_TYPE_SEQ_BUILD_PREVIEW, WM_JOB_TYPE_SEQ_BUILD_PREVIEW,
WM_JOB_TYPE_POINTCACHE,
/* add as needed, screencast, seq proxy build /* add as needed, screencast, seq proxy build
* if having hard coded values is a problem */ * if having hard coded values is a problem */
}; };