GPv3: Render to Image

This PR refactors parts of the gpv3 engine and implements the `render_to_image` engine callback.

Pull Request: https://projects.blender.org/blender/blender/pulls/115904
This commit is contained in:
Falk David
2023-12-08 12:51:15 +01:00
committed by Falk David
parent 91319641eb
commit 8efff3b2d7
3 changed files with 189 additions and 45 deletions

View File

@@ -44,7 +44,6 @@ class AntiAliasing {
Framebuffer blend_weight_fb_ = {"blend_weight_fb"};
PassSimple blend_weight_ps_ = {"blend_weight_ps"};
Framebuffer output_fb_ = {"output_fb"};
PassSimple resolve_ps_ = {"resolve_ps"};
bool draw_wireframe_ = false;
@@ -71,7 +70,7 @@ class AntiAliasing {
anti_aliasing_enabled_ = true; // GPENCIL_SIMPLIFY_AA(scene);
}
void begin_sync(TextureFromPool &color_tx, TextureFromPool &reveal_tx)
void begin_sync(TextureFromPool &color_tx, Framebuffer &scene_fb, TextureFromPool &reveal_tx)
{
/* TODO(fclem): No global access. */
const float *size = DRW_viewport_size_get();
@@ -83,7 +82,7 @@ class AntiAliasing {
/* Resolve pass. */
PassSimple &pass = resolve_ps_;
pass.init();
pass.framebuffer_set(&output_fb_);
pass.framebuffer_set(&scene_fb);
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
pass.shader_set(shaders_.static_shader_get(ANTIALIASING_RESOLVE));
/** \note use color_tx as dummy if AA is disabled. */
@@ -96,24 +95,21 @@ class AntiAliasing {
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
void draw(Manager &manager, GPUTexture *dst_color_tx)
void draw(Manager &manager, const int2 render_resolution)
{
int2 render_size = {GPU_texture_width(dst_color_tx), GPU_texture_height(dst_color_tx)};
DRW_stats_group_start("Anti-Aliasing");
if (anti_aliasing_enabled_) {
edge_detect_tx_.acquire(render_size, GPU_RG8);
edge_detect_tx_.acquire(render_resolution, GPU_RG8);
edge_detect_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(edge_detect_tx_));
manager.submit(edge_detect_ps_);
blend_weight_tx_.acquire(render_size, GPU_RGBA8);
blend_weight_tx_.acquire(render_resolution, GPU_RGBA8);
blend_weight_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(blend_weight_tx_));
manager.submit(blend_weight_ps_);
edge_detect_tx_.release();
}
output_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(dst_color_tx));
manager.submit(resolve_ps_);
blend_weight_tx_.release();

View File

@@ -7,16 +7,25 @@
*/
#include "BKE_gpencil_modifier_legacy.h"
#include "BLI_listbase_wrapper.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_shader_fx_types.h"
#include "DRW_engine.h"
#include "DRW_render.h"
#include "ED_screen.hh"
#include "ED_view3d.hh"
#include "GPU_capabilities.h"
#include "IMB_imbuf_types.h"
#include "RE_pipeline.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
@@ -55,6 +64,10 @@ class Instance {
TextureFromPool reveal_tx_ = {"gp_reveal_tx"};
Framebuffer main_fb_ = {"gp_main_fb"};
/** Underlying scene pixel. Used to composite the output of the grease pencil render onto the
* scene (including merging the depth buffers). */
Framebuffer scene_fb_ = {"gp_scene_fb"};
/** Texture format for all intermediate buffers. */
eGPUTextureFormat texture_format_ = GPU_RGBA16F;
@@ -76,6 +89,8 @@ class Instance {
/** Context. */
Depsgraph *depsgraph_ = nullptr;
Object *camera_ = nullptr;
Manager *manager_ = nullptr;
draw::View view_ = {"MainView"};
/** \note Needs not to be temporary variable since it is dereferenced later. */
std::array<float4, 2> clear_colors_ = {float4(0.0f, 0.0f, 0.0f, 0.0f),
@@ -88,9 +103,18 @@ class Instance {
vfx(shaders),
anti_aliasing(shaders){};
void init(Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d)
void init(Depsgraph *depsgraph,
Manager *manager,
const DRWView *viewport_draw_view,
const View3D *v3d,
const RegionView3D *rv3d)
{
depsgraph_ = depsgraph;
manager_ = manager;
if (viewport_draw_view != nullptr) {
view_.sync(viewport_draw_view);
}
const Scene *scene = DEG_get_evaluated_scene(depsgraph_);
const bool is_viewport = (v3d != nullptr);
@@ -108,12 +132,9 @@ class Instance {
anti_aliasing.init(v3d, scene);
}
void begin_sync(Manager & /*manager*/)
void begin_sync()
{
/* TODO(fclem): Remove global draw manager access. */
View main_view("GPencil_MainView", DRW_view_default_get());
objects.begin_sync(depsgraph_, main_view);
objects.begin_sync(depsgraph_, view_);
layers.begin_sync();
materials.begin_sync();
lights.begin_sync(depsgraph_);
@@ -133,14 +154,15 @@ class Instance {
materials.bind_resources(sub);
lights.bind_resources(sub);
anti_aliasing.begin_sync(color_tx_, reveal_tx_);
anti_aliasing.begin_sync(color_tx_, scene_fb_, reveal_tx_);
}
void object_sync(Manager &manager, ObjectRef &object_ref)
void object_sync(ObjectRef &object_ref)
{
switch (object_ref.object->type) {
case OB_GREASE_PENCIL:
objects.sync_grease_pencil(manager, object_ref, main_fb_, depth_tx_, main_ps_);
objects.sync_grease_pencil(
*manager_, object_ref, main_fb_, scene_fb_, depth_tx_, main_ps_);
break;
case OB_LAMP:
lights.sync(object_ref);
@@ -150,7 +172,7 @@ class Instance {
}
}
void end_sync(Manager & /*manager*/)
void end_sync()
{
objects.end_sync();
layers.end_sync();
@@ -158,39 +180,68 @@ class Instance {
lights.end_sync();
}
void draw_viewport(Manager &manager,
View &view,
GPUTexture *dst_depth_tx,
GPUTexture *dst_color_tx)
void render_sync(RenderEngine *engine, Depsgraph *depsgraph)
{
/* TODO: Remove old draw manager calls. */
DRW_cache_restart();
manager_->begin_sync();
begin_sync();
auto object_sync_render =
[](void *vedata, Object *ob, RenderEngine * /*engine*/, Depsgraph * /*depsgraph*/) {
Instance &inst = *reinterpret_cast<Instance *>(vedata);
ObjectRef ob_ref = DRW_object_ref_get(ob);
inst.object_sync(ob_ref);
};
/* HACK: We pass `this` here so we have access to the `Instance` in `object_sync_render`. */
DRW_render_object_iter(this, engine, depsgraph, object_sync_render);
end_sync();
manager_->end_sync();
/* TODO: Remove old draw manager calls. */
DRW_render_instance_buffer_finish();
}
void draw(GPUTexture *dst_color_tx, GPUTexture *dst_depth_tx, const int2 render_resolution)
{
if (!objects.scene_has_visible_gpencil_object()) {
return;
}
int2 render_size = {GPU_texture_width(dst_depth_tx), GPU_texture_height(dst_depth_tx)};
scene_fb_.ensure(GPU_ATTACHMENT_TEXTURE(dst_depth_tx), GPU_ATTACHMENT_TEXTURE(dst_color_tx));
depth_tx_.acquire(render_size, GPU_DEPTH24_STENCIL8);
color_tx_.acquire(render_size, texture_format_);
reveal_tx_.acquire(render_size, texture_format_);
depth_tx_.acquire(render_resolution, GPU_DEPTH24_STENCIL8);
color_tx_.acquire(render_resolution, texture_format_);
reveal_tx_.acquire(render_resolution, texture_format_);
main_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_),
GPU_ATTACHMENT_TEXTURE(color_tx_),
GPU_ATTACHMENT_TEXTURE(reveal_tx_));
scene_buf_.render_size = float2(render_size);
scene_buf_.render_size = float2(render_resolution);
scene_buf_.push_update();
objects.acquire_temporary_buffers(render_size, texture_format_);
objects.acquire_temporary_buffers(render_resolution, texture_format_);
manager.submit(main_ps_, view);
manager_->submit(main_ps_, view_);
objects.release_temporary_buffers();
anti_aliasing.draw(manager, dst_color_tx);
anti_aliasing.draw(*manager_, render_resolution);
depth_tx_.release();
color_tx_.release();
reveal_tx_.release();
}
draw::View &view()
{
return view_;
}
};
} // namespace blender::draw::greasepencil
@@ -219,9 +270,12 @@ static void gpencil_engine_init(void *vedata)
ved->instance = new draw::greasepencil::Instance();
}
draw::Manager *manager = DRW_manager_get();
const DRWContextState *ctx_state = DRW_context_state_get();
const DRWView *default_view = DRW_view_default_get();
ved->instance->init(ctx_state->depsgraph, ctx_state->v3d, ctx_state->rv3d);
ved->instance->init(
ctx_state->depsgraph, manager, default_view, ctx_state->v3d, ctx_state->rv3d);
}
static void gpencil_draw_scene(void *vedata)
@@ -232,33 +286,29 @@ static void gpencil_draw_scene(void *vedata)
}
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const DRWView *default_view = DRW_view_default_get();
draw::Manager *manager = DRW_manager_get();
draw::View view("DefaultView", default_view);
ved->instance->draw_viewport(*manager, view, dtxl->depth, dtxl->color);
const float2 viewport_size = DRW_viewport_size_get();
ved->instance->view().sync(default_view);
ved->instance->draw(dtxl->color, dtxl->depth, int2(viewport_size));
}
static void gpencil_cache_init(void *vedata)
{
draw::Manager *manager = DRW_manager_get();
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->begin_sync(*manager);
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->begin_sync();
}
static void gpencil_cache_populate(void *vedata, Object *object)
{
draw::Manager *manager = DRW_manager_get();
draw::ObjectRef ref;
ref.object = object;
ref.dupli_object = DRW_object_get_dupli(object);
ref.dupli_parent = DRW_object_get_dupli_parent(object);
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->object_sync(*manager, ref);
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->object_sync(ref);
}
static void gpencil_cache_finish(void *vedata)
{
draw::Manager *manager = DRW_manager_get();
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->end_sync(*manager);
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->end_sync();
}
static void gpencil_instance_free(void *instance)
@@ -271,11 +321,107 @@ static void gpencil_engine_free()
blender::draw::greasepencil::ShaderModule::module_free();
}
/** Get the color and depth textures of the render result in the render layer. */
static void get_render_result_textures(RenderEngine *engine,
RenderLayer *render_layer,
const draw::View &view,
const int2 render_resolution,
draw::Texture &r_color_tx,
draw::Texture &r_depth_tx)
{
/* Create depth texture & color texture from render result. */
const char *viewname = RE_GetActiveRenderView(engine->re);
RenderPass *rpass_z_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname);
RenderPass *rpass_col_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
float *pix_z = (rpass_z_src) ? rpass_z_src->ibuf->float_buffer.data : nullptr;
float *pix_col = (rpass_col_src) ? rpass_col_src->ibuf->float_buffer.data : nullptr;
if (!pix_z || !pix_col) {
RE_engine_set_error_message(engine,
"Warning: To render grease pencil, enable Combined and Z passes.");
}
if (pix_z) {
/* Depth need to be remapped to [0..1] range. */
pix_z = static_cast<float *>(MEM_dupallocN(pix_z));
int pix_num = rpass_z_src->rectx * rpass_z_src->recty;
if (view.is_persp()) {
for (int i = 0; i < pix_num; i++) {
pix_z[i] = (-view.winmat()[3][2] / -pix_z[i]) - view.winmat()[2][2];
pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f);
}
}
else {
/* Keep in mind, near and far distance are negatives. */
float near = view.near_clip();
float far = view.far_clip();
float range_inv = 1.0f / fabsf(far - near);
for (int i = 0; i < pix_num; i++) {
pix_z[i] = (pix_z[i] + near) * range_inv;
pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f);
}
}
}
/* FIXME(fclem): we have a precision loss in the depth buffer because of this re-upload.
* Find where it comes from! */
const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
r_depth_tx.ensure_2d(GPU_DEPTH_COMPONENT24, render_resolution, usage, pix_z);
r_color_tx.ensure_2d(GPU_RGBA16F, render_resolution, usage, pix_col);
}
static void gpencil_render_to_image(void * /*vedata*/,
RenderEngine * /*engine*/,
RenderLayer * /*layer*/,
RenderEngine *engine,
RenderLayer *render_layer,
const rcti * /*rect*/)
{
draw::greasepencil::Instance instance;
draw::Manager &manager = *DRW_manager_get();
Render *render = engine->re;
Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
Scene *scene = DRW_context_state_get()->scene;
Object *camera_original_ob = RE_GetCamera(render);
const char *viewname = RE_GetActiveRenderView(render);
const int2 render_resolution = int2(engine->resolution_x, engine->resolution_y);
instance.init(depsgraph, &manager, nullptr, nullptr, nullptr);
float4x4 viewinv, winmat;
Object *camera_eval = DEG_get_evaluated_object(depsgraph, camera_original_ob);
RE_GetCameraModelMatrix(render, camera_eval, viewinv.ptr());
float4x4 viewmat = math::invert(viewinv);
RE_GetCameraWindow(render, camera_eval, winmat.ptr());
instance.view().sync(viewmat, winmat);
instance.render_sync(engine, depsgraph);
draw::Texture color_tx;
draw::Texture depth_tx;
/* TODO: Support `R_BORDER` render mode. */
get_render_result_textures(
engine, render_layer, instance.view(), render_resolution, color_tx, depth_tx);
instance.draw(color_tx, depth_tx, render_resolution);
RenderPass *rp = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
if (!rp) {
return;
}
float *result = reinterpret_cast<float *>(color_tx.read<float4>(GPU_DATA_FLOAT));
if (result) {
BLI_mutex_lock(&engine->update_render_passes_mutex);
/* WORKAROUND: We use texture read to avoid using a frame-buffer to get the render result.
* However, on some implementation, we need a buffer with a few extra bytes for the read to
* happen correctly (see #GLTexture::read()). So we need a custom memory allocation. */
/* Avoid `memcpy()`, replace the pointer directly. */
RE_pass_set_buffer_data(rp, result);
BLI_mutex_unlock(&engine->update_render_passes_mutex);
}
}
extern "C" {

View File

@@ -110,6 +110,7 @@ class ObjectModule {
void sync_grease_pencil(Manager &manager,
ObjectRef &object_ref,
Framebuffer &main_fb,
Framebuffer &scene_fb,
TextureFromPool &depth_tx,
PassSortable &main_ps)
{
@@ -196,9 +197,10 @@ class ObjectModule {
object_subpass.draw(geom, handle);
}
/** Merging the object depth buffer into the scene depth buffer. */
float4x4 plane_mat = get_object_plane_mat(*object);
ResourceHandle handle_plane_mat = manager.resource_handle(plane_mat);
object_subpass.framebuffer_set(&DRW_viewport_framebuffer_list_get()->depth_only_fb);
object_subpass.framebuffer_set(&scene_fb);
object_subpass.state_set(DRW_STATE_DEPTH_LESS | DRW_STATE_WRITE_DEPTH);
object_subpass.shader_set(shaders_.static_shader_get(DEPTH_MERGE));
object_subpass.bind_texture("depthBuf", (object_has_vfx) ? nullptr : &depth_tx);