Refactor: Introduce ViewRender for viewport renders #110244

Merged
Sergey Sharybin merged 8 commits from Sergey/blender:view_render into main 2023-07-19 12:12:18 +02:00
15 changed files with 212 additions and 137 deletions

View File

@ -2372,7 +2372,7 @@ bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const RegionView3D *rv3d)
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
/* Regular mesh only draws from PBVH without modifiers and shape keys, or for
* external engines that do not have access to the PBVH like Eevee does. */
const bool external_engine = rv3d && rv3d->render_engine != nullptr;
const bool external_engine = rv3d && rv3d->view_render != nullptr;
return !(ss->shapekey_active || ss->deform_modifiers_active || external_engine);
}

View File

@ -1343,7 +1343,7 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa
BLO_read_data_address(reader, &rv3d->localvd);
BLO_read_data_address(reader, &rv3d->clipbb);
rv3d->render_engine = nullptr;
rv3d->view_render = nullptr;
rv3d->sms = nullptr;
rv3d->smooth_timer = nullptr;

View File

@ -244,16 +244,20 @@ static void external_draw_scene_do_v3d(void *vedata)
GPU_apply_state();
/* Create render engine. */
if (!rv3d->render_engine) {
RenderEngine *render_engine = nullptr;
if (!rv3d->view_render) {
RenderEngineType *engine_type = draw_ctx->engine_type;
if (!(engine_type->view_update && engine_type->view_draw)) {
return;
}
RenderEngine *engine = RE_engine_create(engine_type);
engine_type->view_update(engine, draw_ctx->evil_C, draw_ctx->depsgraph);
rv3d->render_engine = engine;
rv3d->view_render = RE_NewViewRender(engine_type);
render_engine = RE_view_engine_get(rv3d->view_render);
engine_type->view_update(render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
}
else {
render_engine = RE_view_engine_get(rv3d->view_render);
}
/* Rendered draw. */
@ -262,8 +266,8 @@ static void external_draw_scene_do_v3d(void *vedata)
ED_region_pixelspace(region);
/* Render result draw. */
const RenderEngineType *type = rv3d->render_engine->type;
type->view_draw(rv3d->render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
const RenderEngineType *type = render_engine->type;
type->view_draw(render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
GPU_bgl_end();
@ -272,8 +276,8 @@ static void external_draw_scene_do_v3d(void *vedata)
/* Set render info. */
EXTERNAL_Data *data = static_cast<EXTERNAL_Data *>(vedata);
if (rv3d->render_engine->text[0] != '\0') {
STRNCPY(data->info, rv3d->render_engine->text);
if (render_engine->text[0] != '\0') {
STRNCPY(data->info, render_engine->text);
}
else {
data->info[0] = '\0';

View File

@ -72,7 +72,7 @@ void ED_render_view3d_update(Depsgraph *depsgraph,
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
RenderEngine *engine = rv3d->render_engine;
RenderEngine *engine = rv3d->view_render ? RE_view_engine_get(rv3d->view_render) : nullptr;
/* call update if the scene changed, or if the render engine
* tagged itself for update (e.g. because it was busy at the

View File

@ -216,7 +216,7 @@ void ED_view3d_stop_render_preview(wmWindowManager *wm, ARegion *region)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
if (rv3d->render_engine) {
if (rv3d->view_render) {
#ifdef WITH_PYTHON
BPy_BEGIN_ALLOW_THREADS;
#endif
@ -227,8 +227,8 @@ void ED_view3d_stop_render_preview(wmWindowManager *wm, ARegion *region)
BPy_END_ALLOW_THREADS;
#endif
RE_engine_free(rv3d->render_engine);
rv3d->render_engine = nullptr;
RE_FreeViewRender(rv3d->view_render);
rv3d->view_render = nullptr;
}
/* A bit overkill but this make sure the viewport is reset completely. (fclem) */
@ -1082,8 +1082,8 @@ static void view3d_main_region_free(ARegion *region)
MEM_freeN(rv3d->clipbb);
}
if (rv3d->render_engine) {
RE_engine_free(rv3d->render_engine);
if (rv3d->view_render) {
RE_FreeViewRender(rv3d->view_render);
}
if (rv3d->sms) {
@ -1110,7 +1110,7 @@ static void *view3d_main_region_duplicate(void *poin)
new_rv3d->clipbb = static_cast<BoundBox *>(MEM_dupallocN(rv3d->clipbb));
}
new_rv3d->render_engine = nullptr;
new_rv3d->view_render = nullptr;
new_rv3d->sms = nullptr;
new_rv3d->smooth_timer = nullptr;

View File

@ -279,7 +279,7 @@ void ED_view3d_smooth_view_ex(
}
/* Skip smooth viewing for external render engine draw. */
if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) {
if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->view_render)) {
/* original values */
if (sview->camera_old) {

View File

@ -10,7 +10,7 @@
struct BoundBox;
struct Object;
struct RenderEngine;
struct ViewRender;
struct SmoothView3DStore;
struct SpaceLink;
struct bGPdata;
@ -58,7 +58,7 @@ typedef struct RegionView3D {
/** Allocated backup of itself while in local-view. */
struct RegionView3D *localvd;
struct RenderEngine *render_engine;
struct ViewRender *view_render;
/** Animated smooth view. */
struct SmoothView3DStore *sms;

View File

@ -34,6 +34,7 @@ set(SRC
intern/multires_bake.cc
intern/pipeline.cc
intern/render_result.cc
intern/render_types.cc
intern/texture_image.c
intern/texture_margin.cc
intern/texture_pointdensity.c

View File

@ -32,6 +32,7 @@ struct RenderResult;
struct ReportList;
struct Scene;
struct ViewLayer;
struct ViewRender;
struct bNode;
struct bNodeTree;
@ -242,6 +243,7 @@ void RE_engine_register_pass(struct RenderEngine *engine,
bool RE_engine_use_persistent_data(struct RenderEngine *engine);
struct RenderEngine *RE_engine_get(const struct Render *re);
struct RenderEngine *RE_view_engine_get(const struct ViewRender *view_render);
/* Acquire render engine for drawing via its `draw()` callback.
*

View File

@ -160,6 +160,9 @@ struct Scene;
struct Render *RE_NewSceneRender(const struct Scene *scene);
struct Render *RE_GetSceneRender(const struct Scene *scene);
struct RenderEngineType;
struct ViewRender *RE_NewViewRender(struct RenderEngineType *engine_type);
/* Assign default dummy callbacks. */
/**
@ -174,6 +177,7 @@ void RE_InitRenderCB(struct Render *re);
* Only call this while you know it will remove the link too.
*/
void RE_FreeRender(struct Render *re);
void RE_FreeViewRender(struct ViewRender *view_render);
/**
* Only called on exit.
*/

View File

@ -1219,14 +1219,34 @@ RenderEngine *RE_engine_get(const Render *re)
return re->engine;
}
RenderEngine *RE_view_engine_get(const ViewRender *view_render)
{
return view_render->engine;
}
bool RE_engine_draw_acquire(Render *re)
{
BLI_mutex_lock(&re->engine_draw_mutex);
RenderEngine *engine = re->engine;
if (engine == nullptr || engine->type->draw == nullptr ||
(engine->flag & RE_ENGINE_CAN_DRAW) == 0) {
if (!engine) {
/* \No engine-side drawing if the engine does not exist. */
return false;
}
if (!engine->type->draw) {
/* Required callbacks are not implemented on the engine side. */
return false;
}
/* Lock before checking the flag, to avoid possible conflicts with the render thread. */
BLI_mutex_lock(&re->engine_draw_mutex);
if ((engine->flag & RE_ENGINE_CAN_DRAW) == 0) {
/* The rendering is not started yet, or has finished.
*
* In the former case there will nothing to be drawn, so can simply use RenderResult drawing
* pipeline. In the latter case the engine has destroyed its display-only resources (textures,
* graphics interops, etc..) so need to use use the RenderResult drawing pipeline. */
BLI_mutex_unlock(&re->engine_draw_mutex);
return false;
}

View File

@ -12,6 +12,7 @@
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <forward_list>
#include "DNA_anim_types.h"
#include "DNA_collection_types.h"
@ -127,8 +128,8 @@
/* here we store all renders */
static struct {
ListBase renderlist;
} RenderGlobal = {{nullptr, nullptr}};
std::forward_list<Render *> render_list;
} RenderGlobal;
/** \} */
@ -297,7 +298,7 @@ static bool render_scene_has_layers_to_render(Scene *scene, ViewLayer *single_la
Render *RE_GetRender(const char *name)
{
/* search for existing renders */
LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
for (Render *re : RenderGlobal.render_list) {
if (STREQLEN(re->name, name, RE_MAXNAME)) {
return re;
}
@ -496,13 +497,9 @@ Render *RE_NewRender(const char *name)
if (re == nullptr) {
/* new render data struct */
re = MEM_cnew<Render>("new render");
BLI_addtail(&RenderGlobal.renderlist, re);
re = MEM_new<Render>("new render");
RenderGlobal.render_list.push_front(re);
STRNCPY(re->name, name);
BLI_rw_mutex_init(&re->resultmutex);
BLI_mutex_init(&re->engine_draw_mutex);
BLI_mutex_init(&re->highlighted_tiles_mutex);
BLI_mutex_init(&re->gpu_compositor_mutex);
}
RE_InitRenderCB(re);
@ -510,6 +507,13 @@ Render *RE_NewRender(const char *name)
return re;
}
ViewRender *RE_NewViewRender(RenderEngineType *engine_type)
{
ViewRender *view_render = MEM_new<ViewRender>("new view render");
view_render->engine = RE_engine_create(engine_type);
return view_render;
}
/* MAX_ID_NAME + sizeof(Library->name) + space + null-terminator. */
#define MAX_SCENE_RENDER_NAME (MAX_ID_NAME + 1024 + 2)
@ -558,41 +562,20 @@ void RE_InitRenderCB(Render *re)
void RE_FreeRender(Render *re)
{
if (re->engine) {
RE_engine_free(re->engine);
}
RenderGlobal.render_list.remove(re);
RE_compositor_free(*re);
MEM_delete(re);
}
RE_blender_gpu_context_free(re);
RE_system_gpu_context_free(re);
BLI_rw_mutex_end(&re->resultmutex);
BLI_mutex_end(&re->engine_draw_mutex);
BLI_mutex_end(&re->highlighted_tiles_mutex);
BLI_mutex_end(&re->gpu_compositor_mutex);
BKE_curvemapping_free_data(&re->r.mblur_shutter_curve);
if (re->highlighted_tiles != nullptr) {
BLI_gset_free(re->highlighted_tiles, MEM_freeN);
}
/* main dbase can already be invalid now, some database-free code checks it */
re->main = nullptr;
re->scene = nullptr;
render_result_free(re->result);
render_result_free(re->pushedresult);
BLI_remlink(&RenderGlobal.renderlist, re);
MEM_freeN(re);
void RE_FreeViewRender(ViewRender *view_render)
{
MEM_delete(view_render);
}
void RE_FreeAllRender()
{
while (RenderGlobal.renderlist.first) {
RE_FreeRender(static_cast<Render *>(RenderGlobal.renderlist.first));
while (!RenderGlobal.render_list.empty()) {
RE_FreeRender(static_cast<Render *>(RenderGlobal.render_list.front()));
}
#ifdef WITH_FREESTYLE
@ -603,7 +586,7 @@ void RE_FreeAllRender()
void RE_FreeAllRenderResults()
{
LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
for (Render *re : RenderGlobal.render_list) {
render_result_free(re->result);
render_result_free(re->pushedresult);
@ -615,7 +598,7 @@ void RE_FreeAllRenderResults()
void RE_FreeAllPersistentData()
{
LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
for (Render *re : RenderGlobal.render_list) {
if (re->engine != nullptr) {
BLI_assert(!(re->engine->flag & RE_ENGINE_RENDERING));
RE_engine_free(re->engine);
@ -644,7 +627,7 @@ static void re_gpu_texture_caches_free(Render *re)
void RE_FreeGPUTextureCaches()
{
LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
for (Render *re : RenderGlobal.render_list) {
re_gpu_texture_caches_free(re);
}
}
@ -655,7 +638,7 @@ void RE_FreeUnusedGPUResources()
wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
for (Render *re : RenderGlobal.render_list) {
bool do_free = true;
LISTBASE_FOREACH (const wmWindow *, win, &wm->windows) {
@ -728,7 +711,7 @@ void RE_FreePersistentData(const Scene *scene)
}
}
else {
LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
for (Render *re : RenderGlobal.render_list) {
re_free_persistent_data(re);
}
}
@ -1949,7 +1932,6 @@ static bool use_eevee_for_freestyle_render(Render *re)
void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, const bool render)
{
re->result_ok = false;
if (render_init_from_main(re, &scene->r, bmain, scene, nullptr, nullptr, false, false)) {
if (render) {
char scene_engine[32];
@ -1963,7 +1945,6 @@ void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, const bool
change_renderdata_engine(re, scene_engine);
}
}
re->result_ok = true;
}
void RE_RenderFreestyleExternal(Render *re)

View File

@ -0,0 +1,49 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup render
*/
#include "render_types.h"
#include "BLI_ghash.h"
#include "BKE_colortools.h"
#include "RE_compositor.hh"
#include "RE_engine.h"
#include "render_result.h"
BaseRender::~BaseRender()
{
if (engine) {
RE_engine_free(engine);
}
render_result_free(result);
BLI_rw_mutex_end(&resultmutex);
BLI_mutex_end(&engine_draw_mutex);
}
Render::~Render()
{
RE_compositor_free(*this);
RE_blender_gpu_context_free(this);
RE_system_gpu_context_free(this);
BLI_mutex_end(&highlighted_tiles_mutex);
BLI_mutex_end(&gpu_compositor_mutex);
BKE_curvemapping_free_data(&r.mblur_shutter_curve);
if (highlighted_tiles != nullptr) {
BLI_gset_free(highlighted_tiles, MEM_freeN);
}
render_result_free(pushedresult);
}

View File

@ -30,109 +30,123 @@ struct ReportList;
extern "C" {
#endif
typedef struct HighlightedTile {
struct HighlightedTile {
rcti rect;
} HighlightedTile;
};
/* controls state of render, everything that's read-only during render stage */
struct Render {
struct Render *next, *prev;
char name[RE_MAXNAME];
int slot;
struct BaseRender {
BaseRender() = default;
virtual ~BaseRender();
/* state settings */
short flag;
bool ok, result_ok;
/* Result of rendering */
RenderResult *result = nullptr;
/* result of rendering */
RenderResult *result;
/* if render with single-layer option, other rendered layers are stored here */
RenderResult *pushedresult;
/** A list of #RenderResults, for full-samples. */
ListBase fullresult;
/* read/write mutex, all internal code that writes to re->result must use a
* write lock, all external code must use a read lock. internal code is assumed
* to not conflict with writes, so no lock used for that */
ThreadRWMutex resultmutex;
/* True if result has GPU textures, to quickly skip cache clear. */
bool result_has_gpu_texture_caches;
/* Read/write mutex, all internal code that writes to the `result` must use a
* write lock, all external code must use a read lock. Internal code is assumed
* to not conflict with writes, so no lock used for that. */
ThreadRWMutex resultmutex = BLI_RWLOCK_INITIALIZER;
/* Render engine. */
struct RenderEngine *engine = nullptr;
/* Guard for drawing render result using engine's `draw()` callback. */
ThreadMutex engine_draw_mutex;
ThreadMutex engine_draw_mutex = BLI_MUTEX_INITIALIZER;
};
struct ViewRender : public BaseRender {
};
/* Controls state of render, everything that's read-only during render stage */
struct Render : public BaseRender {
/* NOTE: Currently unused, provision for the future.
* Add these now to allow the guarded memory allocator to catch C-specific function calls. */
Render() = default;
virtual ~Render();
char name[RE_MAXNAME] = "";
int slot = 0;
/* state settings */
short flag = 0;
bool ok = false;
/* if render with single-layer option, other rendered layers are stored here */
RenderResult *pushedresult = nullptr;
/** A list of #RenderResults, for full-samples. */
ListBase fullresult = {nullptr, nullptr};
/* True if result has GPU textures, to quickly skip cache clear. */
bool result_has_gpu_texture_caches = false;
/** Window size, display rect, viewplane.
* \note Buffer width and height with percentage applied
* without border & crop. convert to long before multiplying together to avoid overflow. */
int winx, winy;
rcti disprect; /* part within winx winy */
rctf viewplane; /* mapped on winx winy */
int winx = 0, winy = 0;
rcti disprect = {0, 0, 0, 0}; /* part within winx winy */
rctf viewplane = {0, 0, 0, 0}; /* mapped on winx winy */
/* final picture width and height (within disprect) */
int rectx, recty;
int rectx = 0, recty = 0;
/* Camera transform, only used by Freestyle. */
float winmat[4][4];
float winmat[4][4] = {{0}};
/* Clipping. */
float clip_start;
float clip_end;
float clip_start = 0.0f;
float clip_end = 0.0f;
/* main, scene, and its full copy of renderdata and world */
struct Main *main;
Scene *scene;
RenderData r;
char single_view_layer[MAX_NAME];
struct Object *camera_override;
struct Main *main = nullptr;
Scene *scene = nullptr;
RenderData r = {};
char single_view_layer[MAX_NAME] = "";
struct Object *camera_override = nullptr;
ThreadMutex highlighted_tiles_mutex;
struct GSet *highlighted_tiles;
/* render engine */
struct RenderEngine *engine;
ThreadMutex highlighted_tiles_mutex = BLI_MUTEX_INITIALIZER;
struct GSet *highlighted_tiles = nullptr;
/* NOTE: This is a minimal dependency graph and evaluated scene which is enough to access view
* layer visibility and use for postprocessing (compositor and sequencer). */
struct Depsgraph *pipeline_depsgraph;
Scene *pipeline_scene_eval;
struct Depsgraph *pipeline_depsgraph = nullptr;
Scene *pipeline_scene_eval = nullptr;
/* Realtime GPU Compositor. */
blender::render::RealtimeCompositor *gpu_compositor;
ThreadMutex gpu_compositor_mutex;
blender::render::RealtimeCompositor *gpu_compositor = nullptr;
ThreadMutex gpu_compositor_mutex = BLI_MUTEX_INITIALIZER;
/* callbacks */
void (*display_init)(void *handle, RenderResult *rr);
void *dih;
void (*display_clear)(void *handle, RenderResult *rr);
void *dch;
void (*display_update)(void *handle, RenderResult *rr, rcti *rect);
void *duh;
void (*current_scene_update)(void *handle, struct Scene *scene);
void *suh;
void (*display_init)(void *handle, RenderResult *rr) = nullptr;
void *dih = nullptr;
void (*display_clear)(void *handle, RenderResult *rr) = nullptr;
void *dch = nullptr;
void (*display_update)(void *handle, RenderResult *rr, rcti *rect) = nullptr;
void *duh = nullptr;
void (*current_scene_update)(void *handle, struct Scene *scene) = nullptr;
void *suh = nullptr;
void (*stats_draw)(void *handle, RenderStats *ri);
void *sdh;
void (*progress)(void *handle, float i);
void *prh;
void (*stats_draw)(void *handle, RenderStats *ri) = nullptr;
void *sdh = nullptr;
void (*progress)(void *handle, float i) = nullptr;
void *prh = nullptr;
void (*draw_lock)(void *handle, bool lock);
void *dlh;
bool (*test_break)(void *handle);
void *tbh;
void (*draw_lock)(void *handle, bool lock) = nullptr;
void *dlh = nullptr;
bool (*test_break)(void *handle) = nullptr;
void *tbh = nullptr;
RenderStats i;
RenderStats i = {};
/**
* Optional report list which may be null (borrowed memory).
* Callers to rendering functions are responsible for setting can clearing, see: #RE_SetReports.
*/
struct ReportList *reports;
struct ReportList *reports = nullptr;
void **movie_ctx_arr;
char viewname[MAX_NAME];
void **movie_ctx_arr = nullptr;
char viewname[MAX_NAME] = "";
/* TODO: replace by a whole draw manager. */
void *system_gpu_context;
void *blender_gpu_context;
void *system_gpu_context = nullptr;
void *blender_gpu_context = nullptr;
};
/* **************** defines ********************* */

View File

@ -406,7 +406,7 @@ static bool wm_draw_region_stereo_set(Main *bmain,
View3D *v3d = area->spacedata.first;
if (v3d->camera && v3d->camera->type == OB_CAMERA) {
RegionView3D *rv3d = region->regiondata;
RenderEngine *engine = rv3d->render_engine;
RenderEngine *engine = rv3d->view_render ? RE_view_engine_get(rv3d->view_render) : NULL;
if (engine && !(engine->type->flag & RE_USE_STEREO_VIEWPORT)) {
return false;
}
@ -495,7 +495,7 @@ static void wm_region_test_render_do_draw(const Scene *scene,
/* tag region for redraw from render engine preview running inside of it */
if (area->spacetype == SPACE_VIEW3D && region->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = region->regiondata;
RenderEngine *engine = rv3d->render_engine;
RenderEngine *engine = rv3d->view_render ? RE_view_engine_get(rv3d->view_render) : NULL;
GPUViewport *viewport = WM_draw_region_get_viewport(region);
if (engine && (engine->flag & RE_ENGINE_DO_DRAW)) {