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 quick_step;
struct PTCacheID *pid;
int (*break_test)(void *data);
void *break_data;
void (*progressbar)(void *data, int num);
void (*progressend)(void *data);
void *progresscontext;
void (*update_progress)(void *data, float progress, int *cancel);
void *bake_job;
} PTCacheBaker;
/* PTCacheEditKey->flag */

View File

@ -3111,33 +3111,18 @@ void BKE_ptcache_quick_cache_all(Main *bmain, Scene *scene)
PTCacheBaker baker;
baker.bake=0;
baker.break_data=NULL;
baker.break_test=NULL;
baker.pid=NULL;
baker.progressbar=NULL;
baker.progressend=NULL;
baker.progresscontext=NULL;
baker.render=0;
baker.anim_init = 0;
baker.main=bmain;
baker.scene=scene;
baker.quick_step=scene->physics_settings.quick_cache_step;
baker.update_progress = NULL;
baker.bake_job = NULL;
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)
{
if (dtime > 60.0) {
@ -3150,52 +3135,6 @@ static void ptcache_dt_to_str(char *str, double dtime)
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 */
void BKE_ptcache_bake(PTCacheBaker *baker)
{
@ -3208,19 +3147,10 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
PointCache *cache = NULL;
float frameleno = scene->r.framelen;
int cfrao = CFRA;
int startframe = MAXFRAME;
int startframe = MAXFRAME, endframe = MAXFRAME;
int bake = baker->bake;
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;
/* set caches to baking mode and figure out start frame */
@ -3261,11 +3191,11 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
startframe = MAX2(cache->last_exact, cache->startframe);
if (bake) {
thread_data.endframe = cache->endframe;
endframe = cache->endframe;
cache->flag |= PTCACHE_BAKING;
}
else {
thread_data.endframe = MIN2(thread_data.endframe, cache->endframe);
endframe = MIN2(endframe, cache->endframe);
}
cache->flag &= ~PTCACHE_BAKED;
@ -3300,7 +3230,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
cache->flag |= PTCACHE_BAKING;
if (bake)
thread_data.endframe = MAX2(thread_data.endframe, cache->endframe);
endframe = MAX2(endframe, cache->endframe);
}
cache->flag &= ~PTCACHE_BAKED;
@ -3313,46 +3243,63 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
CFRA = startframe;
scene->r.framelen = 1.0;
thread_data.break_operation = false;
thread_data.thread_ended = false;
old_progress = -1;
WM_cursor_wait(1);
if (G.background) {
ptcache_bake_thread((void*)&thread_data);
}
else {
BLI_init_threads(&threads, ptcache_bake_thread, 1);
BLI_insert_thread(&threads, (void*)&thread_data);
/* bake */
while (thread_data.thread_ended == false) {
bool use_timer = false;
double stime, ptime, ctime, fetd;
char run[32], cur[32], etd[32];
int cancel = 0;
if (bake)
progress = (int)(100.0f * (float)(CFRA - startframe)/(float)(thread_data.endframe-startframe));
else
progress = CFRA;
stime = ptime = PIL_check_seconds_timer();
/* NOTE: baking should not redraw whole ui as this slows things down */
if ((baker->progressbar) && (progress != old_progress)) {
baker->progressbar(baker->progresscontext, progress);
old_progress = progress;
}
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);
/* Delay to lessen CPU load from UI thread */
PIL_sleep_ms(200);
/* NOTE: breaking baking should leave calculated frames in cache, not clear it */
if (blender_test_break() && !thread_data.break_operation) {
thread_data.break_operation = true;
if (baker->progressend)
baker->progressend(baker->progresscontext);
WM_cursor_wait(1);
}
if (baker->update_progress) {
float progress = ((float)(CFRA - startframe)/(float)(endframe - startframe));
baker->update_progress(baker->bake_job, progress, &cancel);
}
BLI_end_threads(&threads);
if (G.background) {
printf("bake: frame %d :: %d\n", CFRA, endframe);
}
else {
ctime = PIL_check_seconds_timer();
fetd = (ctime - ptime) * (endframe - CFRA) / baker->quick_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, CFRA - startframe + 1, endframe - startframe + 1, ctime - ptime, etd);
}
ptime = ctime;
}
/* Delay to lessen CPU load from UI thread */
PIL_sleep_ms(200);
/* NOTE: breaking baking should leave calculated frames in cache, not clear it */
if ((cancel || G.is_break)) {
break;
}
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 */
if (pid) {
cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED);
@ -3375,7 +3322,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
cache = pid->cache;
if (thread_data.step > 1)
if (baker->quick_step > 1)
cache->flag &= ~(PTCACHE_BAKING|PTCACHE_OUTDATED);
else
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);
}
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 */
}
/* Helpers */

View File

@ -31,12 +31,15 @@
#include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "DNA_scene_types.h"
#include "BKE_context.h"
#include "BKE_screen.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_particle.h"
@ -52,10 +55,6 @@
#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)
{
Scene *scene= CTX_data_scene(C);
@ -72,15 +71,85 @@ static int ptcache_poll(bContext *C)
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);
fflush(stdout);
PointCacheJob *job = customdata;
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)
@ -100,41 +169,41 @@ static void ptcache_free_bake(PointCache *cache)
static int ptcache_bake_all_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene= CTX_data_scene(C);
wmWindow *win = G.background ? NULL : CTX_wm_window(C);
PTCacheBaker baker;
Scene *scene = CTX_data_scene(C);
baker.main = bmain;
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;
PTCacheBaker *baker = MEM_mallocN(sizeof(PTCacheBaker), "PTCacheBaker");
/* 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 {
baker.progressbar = bake_console_progress;
baker.progressend = bake_console_progress_end;
baker.progresscontext = NULL;
}
baker->main = bmain;
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->update_progress = ptcache_job_update;
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);
WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, NULL);
baker->bake_job = job;
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;
}
static int ptcache_free_bake_all_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene= CTX_data_scene(C);
@ -189,59 +258,54 @@ void PTCACHE_OT_free_bake_all(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
}
static int ptcache_bake_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(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);
Object *ob= ptr.id.data;
PointCache *cache= ptr.data;
PTCacheBaker baker;
PTCacheID *pid;
ListBase pidlist;
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
Object *ob = ptr.id.data;
PointCache *cache = ptr.data;
BKE_ptcache_ids_from_object(&pidlist, ob, scene, MAX_DUPLI_RECUR);
for (pid=pidlist.first; pid; pid=pid->next) {
if (pid->cache == cache)
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;
PointCacheJob *job = MEM_mallocN(sizeof(PointCacheJob), "PointCacheJob");
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;
}
}
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;
baker->bake_job = job;
/* 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;
}
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Point Cache",
WM_JOB_PROGRESS, WM_JOB_TYPE_POINTCACHE);
BKE_ptcache_bake(&baker);
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);
BLI_freelistN(&pidlist);
WM_set_locked_interface(CTX_wm_manager(C), true);
WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
WM_jobs_start(CTX_wm_manager(C), wm_job);
return OPERATOR_FINISHED;
}
static int ptcache_free_bake_exec(bContext *C, wmOperator *UNUSED(op))
{
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.anim_init = 1;
baker.quick_step = 1;
baker.break_test = re->test_break;
baker.break_data = re->tbh;
baker.progressbar = NULL;
baker.update_progress = NULL;
baker.bake_job = NULL;
BKE_ptcache_bake(&baker);
}

View File

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