[Bf-blender-cvs] [6f92604] master: OpenGL render: Move file writing to a separate thread

Sergey Sharybin noreply at git.blender.org
Fri Sep 16 10:32:03 CEST 2016


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. @@




More information about the Bf-blender-cvs mailing list