From 40ac21e5a54c8a1b1687b02efe4fe3be6def1b75 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 30 Jan 2025 17:14:35 +0100 Subject: [PATCH] Refactor: Move OGLRender data structure towards C++ https://projects.blender.org/blender/blender/pulls/133828 --- .../blender/editors/render/render_opengl.cc | 236 ++++++++---------- 1 file changed, 102 insertions(+), 134 deletions(-) diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 952d4606e83..9042dcec091 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -6,8 +6,10 @@ * \ingroup render */ +#include #include #include +#include #include "MEM_guardedalloc.h" @@ -17,7 +19,6 @@ #include "BLI_string.h" #include "BLI_task.h" #include "BLI_task.hh" -#include "BLI_threads.h" #include "BLI_utildefines.h" #include "BLI_vector.hh" @@ -77,78 +78,67 @@ #include "render_intern.hh" -/* Define this to get timing information. */ -// #define DEBUG_TIME - -#ifdef DEBUG_TIME -# include "BLI_time.h" -#endif - /* TODO(sergey): Find better approximation of the scheduled frames. * For really high-resolution renders it might fail still. */ #define MAX_SCHEDULED_FRAMES 8 struct OGLRender : public RenderJobBase { - Main *bmain; - Render *re; - WorkSpace *workspace; - ViewLayer *view_layer; - Depsgraph *depsgraph; + Main *bmain = nullptr; + Render *re = nullptr; + WorkSpace *workspace = nullptr; + ViewLayer *view_layer = nullptr; + Depsgraph *depsgraph = nullptr; - View3D *v3d; - RegionView3D *rv3d; - ARegion *region; + View3D *v3d = nullptr; + RegionView3D *rv3d = nullptr; + ARegion *region = nullptr; - ScrArea *prevsa; - ARegion *prevar; + int views_len = 0; /* multi-view views */ - int views_len; /* multi-view views */ - - bool is_sequencer; - SpaceSeq *sseq; + bool is_sequencer = false; + SpaceSeq *sseq = nullptr; struct { - ImBuf **ibufs_arr; + ImBuf **ibufs_arr = nullptr; } seq_data; - Image *ima; - ImageUser iuser; + Image *ima = nullptr; + ImageUser iuser = {}; - GPUOffScreen *ofs; - int sizex, sizey; - int write_still; + GPUOffScreen *ofs = nullptr; + int sizex = 0; + int sizey = 0; + int write_still = false; - GPUViewport *viewport; + GPUViewport *viewport = nullptr; - ReportList *reports; - int cfrao, nfra; + std::mutex reports_mutex; + ReportList *reports = nullptr; - int totvideos; + int cfrao = 0; + int nfra = 0; + + int totvideos = 0; /* For only rendering frames that have a key in animation data. */ - BLI_bitmap *render_frames; + BLI_bitmap *render_frames = nullptr; /* quick lookup */ - int view_id; + int view_id = 0; /* wm vars for timer and progress cursor */ - wmWindowManager *wm; - wmWindow *win; + wmWindowManager *wm = nullptr; + wmWindow *win = nullptr; blender::Vector movie_writers; - TaskPool *task_pool; - bool pool_ok; - bool is_animation; + TaskPool *task_pool = nullptr; + bool pool_ok = true; + bool is_animation = false; - eImageFormatDepth color_depth; - SpinLock reports_lock; - uint num_scheduled_frames; - ThreadMutex task_mutex; - ThreadCondition task_condition; - -#ifdef DEBUG_TIME - double time_start; -#endif + eImageFormatDepth color_depth = R_IMF_CHAN_DEPTH_32; + uint num_scheduled_frames = 0; + std::mutex task_mutex; + std::condition_variable task_condition; wmJob *wm_job = nullptr; }; @@ -700,8 +690,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) WorkSpace *workspace = CTX_wm_workspace(C); Scene *scene = CTX_data_scene(C); - ScrArea *prevsa = CTX_wm_area(C); - ARegion *prevar = CTX_wm_region(C); GPUOffScreen *ofs; OGLRender *oglrender; int sizex, sizey; @@ -800,9 +788,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->seq_data.ibufs_arr = ibufs_arr; } - oglrender->prevsa = prevsa; - oglrender->prevar = prevar; - if (is_view_context) { /* Prefer rendering camera in quad view if possible. */ if (!ED_view3d_context_user_region(C, &oglrender->v3d, &oglrender->region)) { @@ -849,8 +834,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->wm = wm; oglrender->win = win; - oglrender->totvideos = 0; - if (is_animation) { if (is_render_keyed_only) { gather_frames_to_render(C, oglrender); @@ -862,28 +845,14 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) else { oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_HIGH); } - oglrender->pool_ok = true; - BLI_spin_init(&oglrender->reports_lock); } - else { - oglrender->task_pool = nullptr; - } - oglrender->num_scheduled_frames = 0; - BLI_mutex_init(&oglrender->task_mutex); - BLI_condition_init(&oglrender->task_condition); - -#ifdef DEBUG_TIME - oglrender->time_start = BLI_time_now_seconds(); -#endif return true; } -static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) +static void screen_opengl_render_end(OGLRender *oglrender) { - Scene *scene = oglrender->scene; - - if (oglrender->is_animation) { + if (oglrender->task_pool) { /* Trickery part for movie output: * * We MUST write frames in an exact order, so we only let background @@ -893,28 +862,22 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) * After this loop is done work_and_wait() will have nothing to do, * so we don't run into wrong order of frames written to the stream. */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - BLI_mutex_lock(&oglrender->task_mutex); + if (BKE_imtype_is_movie(oglrender->scene->r.im_format.imtype)) { + std::unique_lock lock(oglrender->task_mutex); while (oglrender->num_scheduled_frames > 0) { - BLI_condition_wait(&oglrender->task_condition, &oglrender->task_mutex); + oglrender->task_condition.wait(lock); } - BLI_mutex_unlock(&oglrender->task_mutex); } + BLI_task_pool_work_and_wait(oglrender->task_pool); BLI_task_pool_free(oglrender->task_pool); - BLI_spin_end(&oglrender->reports_lock); + oglrender->task_pool = nullptr; } - BLI_mutex_end(&oglrender->task_mutex); - BLI_condition_end(&oglrender->task_condition); - -#ifdef DEBUG_TIME - printf("Total render time: %f\n", BLI_time_now_seconds() - oglrender->time_start); -#endif MEM_SAFE_FREE(oglrender->render_frames); if (!oglrender->movie_writers.is_empty()) { - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + if (BKE_imtype_is_movie(oglrender->scene->r.im_format.imtype)) { for (MovieWriter *writer : oglrender->movie_writers) { MOV_write_end(writer); } @@ -922,31 +885,29 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) oglrender->movie_writers.clear_and_shrink(); } - if (oglrender->wm_job) { /* exec will not have a job */ - Depsgraph *depsgraph = oglrender->depsgraph; - scene->r.cfra = oglrender->cfrao; - BKE_scene_graph_update_for_newframe(depsgraph); + if (oglrender->ofs || oglrender->viewport) { + DRW_gpu_context_enable(); + GPU_offscreen_free(oglrender->ofs); + GPU_viewport_free(oglrender->viewport); + DRW_gpu_context_disable(); + + oglrender->ofs = nullptr; + oglrender->viewport = nullptr; } - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene); - - DRW_gpu_context_enable(); - GPU_offscreen_free(oglrender->ofs); - GPU_viewport_free(oglrender->viewport); - DRW_gpu_context_disable(); - - if (oglrender->is_sequencer) { - MEM_freeN(oglrender->seq_data.ibufs_arr); - } + MEM_SAFE_FREE(oglrender->seq_data.ibufs_arr); memset(&oglrender->scene->customdata_mask_modal, 0, sizeof(oglrender->scene->customdata_mask_modal)); - CTX_wm_area_set(C, oglrender->prevsa); - CTX_wm_region_set(C, oglrender->prevar); + if (oglrender->wm_job) { /* exec will not have a job */ + Depsgraph *depsgraph = oglrender->depsgraph; + oglrender->scene->r.cfra = oglrender->cfrao; + BKE_scene_graph_update_for_newframe(depsgraph); + } - MEM_delete(oglrender); + WM_main_add_notifier(NC_SCENE | ND_RENDER_RESULT, oglrender->scene); G.is_rendering = false; } @@ -955,12 +916,16 @@ static void screen_opengl_render_cancel(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); OGLRender *oglrender = static_cast(op->customdata); - WM_jobs_kill_type(wm, oglrender->scene, WM_JOB_TYPE_RENDER); - screen_opengl_render_end(C, oglrender); + if (oglrender->is_animation) { + WM_jobs_kill_type(wm, oglrender->scene, WM_JOB_TYPE_RENDER); + } + + screen_opengl_render_end(oglrender); + MEM_delete(oglrender); } /* share between invoke and exec */ -static bool screen_opengl_render_anim_init(bContext *C, wmOperator *op) +static bool screen_opengl_render_anim_init(wmOperator *op) { /* initialize animation */ OGLRender *oglrender = static_cast(op->customdata); @@ -990,7 +955,8 @@ static bool screen_opengl_render_anim_init(bContext *C, wmOperator *op) suffix); if (writer == nullptr) { BKE_report(oglrender->reports, RPT_ERROR, "Movie format unsupported"); - screen_opengl_render_end(C, oglrender); + screen_opengl_render_end(oglrender); + MEM_delete(oglrender); return false; } oglrender->movie_writers.append(writer); @@ -1021,10 +987,9 @@ static void write_result(TaskPool *__restrict pool, WriteTaskData *task_data) /* Don't attempt to write if we've got an error. */ if (!oglrender->pool_ok) { RE_FreeRenderResult(rr); - BLI_mutex_lock(&oglrender->task_mutex); + std::scoped_lock lock(oglrender->task_mutex); oglrender->num_scheduled_frames--; - BLI_condition_notify_all(&oglrender->task_condition); - BLI_mutex_unlock(&oglrender->task_mutex); + oglrender->task_condition.notify_all(); return; } /* Construct local thread0safe copy of reports structure which we can @@ -1069,23 +1034,24 @@ static void write_result(TaskPool *__restrict pool, WriteTaskData *task_data) } if (reports.list.first != nullptr) { /* TODO: Should rather use new #BKE_reports_move_to_reports ? */ - BLI_spin_lock(&oglrender->reports_lock); + std::unique_lock lock(oglrender->reports_mutex); for (Report *report = static_cast(reports.list.first); report != nullptr; report = report->next) { BKE_report(oglrender->reports, static_cast(report->type), report->message); } - BLI_spin_unlock(&oglrender->reports_lock); } BKE_reports_free(&reports); if (!ok) { oglrender->pool_ok = false; } RE_FreeRenderResult(rr); - BLI_mutex_lock(&oglrender->task_mutex); - oglrender->num_scheduled_frames--; - BLI_condition_notify_all(&oglrender->task_condition); - BLI_mutex_unlock(&oglrender->task_mutex); + + { + std::unique_lock lock(oglrender->task_mutex); + oglrender->num_scheduled_frames--; + oglrender->task_condition.notify_all(); + } } static void write_result_func(TaskPool *__restrict pool, void *task_data_v) @@ -1107,12 +1073,13 @@ static bool schedule_write_result(OGLRender *oglrender, RenderResult *rr) WriteTaskData *task_data = MEM_cnew("write task data"); task_data->rr = rr; memcpy(&task_data->tmp_scene, scene, sizeof(task_data->tmp_scene)); - BLI_mutex_lock(&oglrender->task_mutex); - oglrender->num_scheduled_frames++; - if (oglrender->num_scheduled_frames > MAX_SCHEDULED_FRAMES) { - BLI_condition_wait(&oglrender->task_condition, &oglrender->task_mutex); + { + std::unique_lock lock(oglrender->task_mutex); + oglrender->num_scheduled_frames++; + if (oglrender->num_scheduled_frames > MAX_SCHEDULED_FRAMES) { + oglrender->task_condition.wait(lock); + } } - BLI_mutex_unlock(&oglrender->task_mutex); BLI_task_pool_push(oglrender->task_pool, write_result_func, task_data, true, nullptr); return true; } @@ -1149,9 +1116,10 @@ static bool screen_opengl_render_anim_step(OGLRender *oglrender) nullptr); if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(filepath)) { - BLI_spin_lock(&oglrender->reports_lock); - BKE_reportf(oglrender->reports, RPT_INFO, "Skipping existing frame \"%s\"", filepath); - BLI_spin_unlock(&oglrender->reports_lock); + { + std::unique_lock lock(oglrender->reports_mutex); + BKE_reportf(oglrender->reports, RPT_INFO, "Skipping existing frame \"%s\"", filepath); + } ok = true; goto finally; } @@ -1206,19 +1174,20 @@ finally: /* Step the frame and bail early if needed */ static int screen_opengl_render_modal(bContext *C, wmOperator *op, const wmEvent *event) { OGLRender *oglrender = static_cast(op->customdata); - const bool anim = RNA_boolean_get(op->ptr, "animation"); /* Still render completes immediately, but still modal to show some feedback * in case render initialization takes a while. */ - if (!anim) { + if (!oglrender->is_animation) { screen_opengl_render_apply(oglrender); - screen_opengl_render_end(C, oglrender); + screen_opengl_render_end(oglrender); + MEM_delete(oglrender); return OPERATOR_FINISHED; } /* no running blender, remove handler and pass through */ if (0 == WM_jobs_test(CTX_wm_manager(C), oglrender->scene, WM_JOB_TYPE_RENDER)) { - screen_opengl_render_end(C, oglrender); + screen_opengl_render_end(oglrender); + MEM_delete(oglrender); return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; } @@ -1267,7 +1236,6 @@ static void opengl_render_freejob(void * /*customdata*/) static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - OGLRender *oglrender; const bool anim = RNA_boolean_get(op->ptr, "animation"); if (!screen_opengl_render_init(C, op)) { @@ -1275,12 +1243,12 @@ static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEven } if (anim) { - if (!screen_opengl_render_anim_init(C, op)) { + if (!screen_opengl_render_anim_init(op)) { return OPERATOR_CANCELLED; } } - oglrender = static_cast(op->customdata); + OGLRender *oglrender = static_cast(op->customdata); render_view_open(C, event->xy[0], event->xy[1], op->reports); /* View may be changed above #USER_RENDER_DISPLAY_WINDOW. */ @@ -1312,24 +1280,23 @@ static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEven /* executes blocking render */ static int screen_opengl_render_exec(bContext *C, wmOperator *op) { - const bool is_animation = RNA_boolean_get(op->ptr, "animation"); - if (!screen_opengl_render_init(C, op)) { return OPERATOR_CANCELLED; } OGLRender *oglrender = static_cast(op->customdata); - if (!is_animation) { /* same as invoke */ + if (!oglrender->is_animation) { /* same as invoke */ screen_opengl_render_apply(oglrender); - screen_opengl_render_end(C, oglrender); + screen_opengl_render_end(oglrender); + MEM_delete(oglrender); return OPERATOR_FINISHED; } bool ret = true; - if (!screen_opengl_render_anim_init(C, op)) { + if (!screen_opengl_render_anim_init(op)) { return OPERATOR_CANCELLED; } @@ -1337,7 +1304,8 @@ static int screen_opengl_render_exec(bContext *C, wmOperator *op) ret = screen_opengl_render_anim_step(oglrender); } - screen_opengl_render_end(C, oglrender); + screen_opengl_render_end(oglrender); + MEM_delete(oglrender); return OPERATOR_FINISHED; }