Refactor: Move OGLRender data structure towards C++

https://projects.blender.org/blender/blender/pulls/133828
This commit is contained in:
Brecht Van Lommel
2025-01-30 17:14:35 +01:00
parent 7e5749c679
commit 40ac21e5a5

View File

@@ -6,8 +6,10 @@
* \ingroup render
*/
#include <condition_variable>
#include <cstddef>
#include <cstring>
#include <mutex>
#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<MovieWriter *> 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<OGLRender *>(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<OGLRender *>(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<Report *>(reports.list.first); report != nullptr;
report = report->next)
{
BKE_report(oglrender->reports, static_cast<eReportType>(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<WriteTaskData>("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<OGLRender *>(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<OGLRender *>(op->customdata);
OGLRender *oglrender = static_cast<OGLRender *>(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<OGLRender *>(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;
}