diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2016-09-16 11:28:41 +0300 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2016-09-16 11:31:37 +0300 |
commit | 6f92604e539b2114763150fb1ace60d28e59a889 (patch) | |
tree | ddc52f46db88ca341773089c3b8f436e1b048e91 | |
parent | 04bfea0d6747b48cf8e792586cd6df8df0f00dcc (diff) |
OpenGL render: Move file writing to a separate thread
The idea is to have a dedicated thread which is responsive for all the
file writing to a separate thread, so slow disk will not slow down
OpenGL itself.
Gives really nice speedup around 1.5x when exporting barber shop layout
file to h264 video.
-rw-r--r-- | source/blender/editors/render/render_opengl.c | 153 | ||||
-rw-r--r-- | source/blender/render/extern/include/RE_pipeline.h | 2 | ||||
-rw-r--r-- | source/blender/render/intern/source/render_result.c | 81 |
3 files changed, 210 insertions, 26 deletions
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 46353ec4a44..ee2772ce31a 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -40,6 +40,7 @@ #include "BLI_utildefines.h" #include "BLI_jitter.h" #include "BLI_threads.h" +#include "BLI_task.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -130,6 +131,10 @@ typedef struct OGLRender { wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/ void **movie_ctx_arr; + TaskPool *task_pool; + bool pool_ok; + SpinLock reports_lock; + #ifdef DEBUG_TIME double time_start; #endif @@ -685,6 +690,24 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->mh = NULL; oglrender->movie_ctx_arr = NULL; + if (is_animation) { + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + oglrender->task_pool = BLI_task_pool_create_background(task_scheduler, + oglrender); + BLI_pool_set_num_threads(oglrender->task_pool, 1); + } + else { + oglrender->task_pool = BLI_task_pool_create(task_scheduler, + oglrender); + } + } + else { + oglrender->task_pool = NULL; + } + oglrender->pool_ok = true; + BLI_spin_init(&oglrender->reports_lock); + #ifdef DEBUG_TIME oglrender->time_start = PIL_check_seconds_timer(); #endif @@ -698,6 +721,10 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) Scene *scene = oglrender->scene; int i; + BLI_task_pool_work_and_wait(oglrender->task_pool); + BLI_task_pool_free(oglrender->task_pool); + BLI_spin_end(&oglrender->reports_lock); + #ifdef DEBUG_TIME printf("Total render time: %f\n", PIL_check_seconds_timer() - oglrender->time_start); #endif @@ -796,6 +823,102 @@ static bool screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) return true; } +typedef struct WriteTaskData { + RenderResult *rr; + int cfra; +} WriteTaskData; + +static void write_result_func(TaskPool * __restrict pool, + void *task_data_v, + int UNUSED(thread_id)) +{ + OGLRender *oglrender = (OGLRender *) BLI_task_pool_userdata(pool); + WriteTaskData *task_data = (WriteTaskData *) task_data_v; + Scene *scene = oglrender->scene; + RenderResult *rr = task_data->rr; + const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype); + const int cfra = task_data->cfra; + bool ok; + /* Construct local thread0safe copy of reports structure which we can + * safely pass to the underlying functions. + */ + ReportList reports; + BKE_reports_init(&reports, oglrender->reports->flag & ~RPT_PRINT); + /* Do actual save logic here, depending on the file format. */ + if (is_movie) { + /* We have to construct temporary scene with proper scene->r.cfra. + * This is because underlying calls do not use r.cfra but use scene + * for that. + */ + Scene tmp_scene = *scene; + tmp_scene.r.cfra = cfra; + ok = RE_WriteRenderViewsMovie(&reports, + rr, + &tmp_scene, + &tmp_scene.r, + oglrender->mh, + oglrender->movie_ctx_arr, + oglrender->totvideos, + PRVRANGEON != 0); + } + else { + /* TODO(sergey): We can in theory save some CPU ticks here because we + * calculate file name again here. + */ + char name[FILE_MAX]; + BKE_image_path_from_imformat(name, + scene->r.pic, + oglrender->bmain->name, + cfra, + &scene->r.im_format, + (scene->r.scemode & R_EXTENSION) != 0, + true, + NULL); + + BKE_render_result_stamp_info(scene, scene->camera, rr, false); + ok = RE_WriteRenderViewsImage(NULL, rr, scene, true, name); + if (!ok) { + BKE_reportf(&reports, + RPT_ERROR, + "Write error: cannot save %s", + name); + } + } + if (reports.list.first != NULL) { + BLI_spin_lock(&oglrender->reports_lock); + for (Report *report = reports.list.first; + report != NULL; + report = report->next) + { + BKE_report(oglrender->reports, + report->type, + report->message); + } + BLI_spin_unlock(&oglrender->reports_lock); + } + if (!ok) { + oglrender->pool_ok = false; + } + RE_FreeRenderResult(rr); +} + +static bool schedule_write_result(OGLRender *oglrender, RenderResult *rr) +{ + if (!oglrender->pool_ok) { + return false; + } + Scene *scene = oglrender->scene; + WriteTaskData *task_data = MEM_mallocN(sizeof(WriteTaskData), "write task data"); + task_data->rr = rr; + task_data->cfra = scene->r.cfra; + BLI_task_pool_push(oglrender->task_pool, + write_result_func, + task_data, + true, + TASK_PRIORITY_LOW); + return true; +} + static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -828,7 +951,9 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) { + BLI_spin_lock(&oglrender->reports_lock); BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name); + BLI_spin_unlock(&oglrender->reports_lock); ok = true; goto finally; } @@ -856,34 +981,10 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) /* save to disk */ rr = RE_AcquireResultRead(oglrender->re); - - if (is_movie) { - ok = RE_WriteRenderViewsMovie(oglrender->reports, rr, scene, &scene->r, oglrender->mh, - oglrender->movie_ctx_arr, oglrender->totvideos, PRVRANGEON != 0); - if (ok) { - printf("Append frame %d", scene->r.cfra); - BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra); - } - } - else { - BKE_render_result_stamp_info(scene, scene->camera, rr, false); - ok = RE_WriteRenderViewsImage(op->reports, rr, scene, true, name); - if (ok) { - printf("Saved: %s", name); - BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name); - } - else { - printf("Write error: cannot save %s\n", name); - BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name); - } - } - + RenderResult *new_rr = RE_DuplicateRenderResult(rr); RE_ReleaseResult(oglrender->re); - - /* movie stats prints have no line break */ - printf("\n"); - + ok = schedule_write_result(oglrender, new_rr); finally: /* Step the frame and bail early if needed */ diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index 3a99be706bf..7021477a702 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -391,6 +391,8 @@ bool RE_RenderResult_is_stereo(RenderResult *res); struct RenderView *RE_RenderViewGetById(struct RenderResult *res, const int view_id); struct RenderView *RE_RenderViewGetByName(struct RenderResult *res, const char *viewname); +RenderResult *RE_DuplicateRenderResult(RenderResult *rr); + /******* Debug pass helper functions *********/ #ifdef WITH_CYCLES_DEBUG diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index 6ea46af6f7e..e3fc8174bd6 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -1665,3 +1665,84 @@ RenderView *RE_RenderViewGetByName(RenderResult *res, const char *viewname) BLI_assert(res->views.first); return rv ? rv : res->views.first; } + +static RenderPass *duplicate_render_pass(RenderPass *rpass) +{ + RenderPass *new_rpass = MEM_mallocN(sizeof(RenderPass), "new render pass"); + *new_rpass = *rpass; + new_rpass->next = new_rpass->prev = NULL; + if (new_rpass->rect != NULL) { + new_rpass->rect = MEM_dupallocN(new_rpass->rect); + } + return new_rpass; +} + +static RenderLayer *duplicate_render_layer(RenderLayer *rl) +{ + RenderLayer *new_rl = MEM_mallocN(sizeof(RenderLayer), "new render layer"); + *new_rl = *rl; + new_rl->next = new_rl->prev = NULL; + new_rl->passes.first = new_rl->passes.last = NULL; + new_rl->exrhandle = NULL; + if (new_rl->acolrect != NULL) { + new_rl->acolrect = MEM_dupallocN(new_rl->acolrect); + } + if (new_rl->scolrect != NULL) { + new_rl->scolrect = MEM_dupallocN(new_rl->scolrect); + } + if (new_rl->display_buffer != NULL) { + new_rl->display_buffer = MEM_dupallocN(new_rl->display_buffer); + } + for (RenderPass *rpass = rl->passes.first; rpass != NULL; rpass = rpass->next) { + RenderPass *new_rpass = duplicate_render_pass(rpass); + BLI_addtail(&new_rl->passes, new_rpass); + } + return new_rl; +} + +static RenderView *duplicate_render_view(RenderView *rview) +{ + RenderView *new_rview = MEM_mallocN(sizeof(RenderView), "new render view"); + *new_rview = *rview; + if (new_rview->rectf != NULL) { + new_rview->rectf = MEM_dupallocN(new_rview->rectf); + } + if (new_rview->rectf != NULL) { + new_rview->rectf = MEM_dupallocN(new_rview->rectf); + } + if (new_rview->rectz != NULL) { + new_rview->rectz = MEM_dupallocN(new_rview->rectz); + } + if (new_rview->rect32 != NULL) { + new_rview->rect32 = MEM_dupallocN(new_rview->rect32); + } + return new_rview; +} + +RenderResult *RE_DuplicateRenderResult(RenderResult *rr) +{ + RenderResult *new_rr = MEM_mallocN(sizeof(RenderResult), "new render result"); + *new_rr = *rr; + new_rr->next = new_rr->prev = NULL; + new_rr->layers.first = new_rr->layers.last = NULL; + new_rr->views.first = new_rr->views.last = NULL; + for (RenderLayer *rl = rr->layers.first; rl != NULL; rl = rl->next) { + RenderLayer *new_rl = duplicate_render_layer(rl); + BLI_addtail(&new_rr->layers, new_rl); + } + for (RenderView *rview = rr->views.first; rview != NULL; rview = rview->next) { + RenderView *new_rview = duplicate_render_view(rview); + BLI_addtail(&new_rr->views, new_rview); + } + if (new_rr->rect32 != NULL) { + new_rr->rect32 = MEM_dupallocN(new_rr->rect32); + } + if (new_rr->rectf != NULL) { + new_rr->rectf = MEM_dupallocN(new_rr->rectf); + } + if (new_rr->rectz != NULL) { + new_rr->rectz = MEM_dupallocN(new_rr->rectz); + } + new_rr->stamp_data = MEM_dupallocN(new_rr->stamp_data); + return new_rr; +} |