From 8efff3b2d74b49326ea848179b4ae4d7d53f0d3d Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 8 Dec 2023 12:51:15 +0100 Subject: [PATCH] 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 --- .../engines/gpencil/gpencil_antialiasing.hh | 14 +- .../draw/engines/gpencil/gpencil_engine.cc | 216 +++++++++++++++--- .../draw/engines/gpencil/gpencil_object.hh | 4 +- 3 files changed, 189 insertions(+), 45 deletions(-) diff --git a/source/blender/draw/engines/gpencil/gpencil_antialiasing.hh b/source/blender/draw/engines/gpencil/gpencil_antialiasing.hh index ce7ad025d6b..da7db7bf731 100644 --- a/source/blender/draw/engines/gpencil/gpencil_antialiasing.hh +++ b/source/blender/draw/engines/gpencil/gpencil_antialiasing.hh @@ -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(); diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.cc b/source/blender/draw/engines/gpencil/gpencil_engine.cc index ca94a9d93db..6634e5d4f68 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.cc +++ b/source/blender/draw/engines/gpencil/gpencil_engine.cc @@ -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 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(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(vedata)->instance->begin_sync(*manager); + reinterpret_cast(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(vedata)->instance->object_sync(*manager, ref); + reinterpret_cast(vedata)->instance->object_sync(ref); } static void gpencil_cache_finish(void *vedata) { - draw::Manager *manager = DRW_manager_get(); - reinterpret_cast(vedata)->instance->end_sync(*manager); + reinterpret_cast(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(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(color_tx.read(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" { diff --git a/source/blender/draw/engines/gpencil/gpencil_object.hh b/source/blender/draw/engines/gpencil/gpencil_object.hh index 1897b651309..34cd34db0e2 100644 --- a/source/blender/draw/engines/gpencil/gpencil_object.hh +++ b/source/blender/draw/engines/gpencil/gpencil_object.hh @@ -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);