Commit: 6f92604e539b2114763150fb1ace60d28e59a889
Author: Sergey Sharybin
Date:   Fri Sep 16 10:28:41 2016 +0200
Branches: master
https://developer.blender.org/rB6f92604e539b2114763150fb1ace60d28e59a889

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.

===================================================================

M       source/blender/editors/render/render_opengl.c
M       source/blender/render/extern/include/RE_pipeline.h
M       source/blender/render/intern/source/render_result.c

===================================================================

diff --git a/source/blender/editors/render/render_opengl.c 
b/source/blender/editors/render/render_opengl.c
index 46353ec..ee2772c 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 3a99be7..7021477 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 6ea46af..e3fc817 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);
+       

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to