From 6f92604e539b2114763150fb1ace60d28e59a889 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 16 Sep 2016 10:28:41 +0200 Subject: 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. --- source/blender/editors/render/render_opengl.c | 153 +++++++++++++++++++++----- 1 file changed, 127 insertions(+), 26 deletions(-) (limited to 'source/blender/editors/render/render_opengl.c') 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 */ -- cgit v1.2.3