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:
@@ -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();
|
||||
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user