From 7695ee7159704e6d46629c6bbfa28bbaa175ae3a Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 29 Oct 2023 03:24:52 +1300 Subject: [PATCH] Enable multi-pass rendering. An extra rendering pass can be requested during render or expose by calling draw_manager::request_extra_render_pass(). This is used for blur effects. --- src/draw_manager.cpp | 17 ++++++++++++++++- src/draw_manager.hpp | 11 +++++++++++ src/gui/core/canvas.cpp | 17 +++++++---------- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/draw_manager.cpp b/src/draw_manager.cpp index 23e25ed2d39..7e611b1482e 100644 --- a/src/draw_manager.cpp +++ b/src/draw_manager.cpp @@ -48,6 +48,7 @@ std::vector invalidated_regions_; bool drawing_ = false; bool tlds_need_tidying_ = false; uint32_t last_sparkle_ = 0; +bool extra_pass_requested_ = false; } // namespace namespace draw_manager { @@ -123,6 +124,11 @@ void invalidate_all() invalidate_region(video::game_canvas()); } +void request_extra_render_pass() +{ + extra_pass_requested_ = true; +} + void sparkle() { if (drawing_) { @@ -153,7 +159,16 @@ void sparkle() draw_manager::render(); // Draw to the screen. - if (draw_manager::expose()) { + bool drew_something = draw_manager::expose(); + + // If extra render passes are requested, render and draw again. + while (extra_pass_requested_) { + extra_pass_requested_ = false; + draw_manager::render(); + drew_something |= draw_manager::expose(); + } + + if (drew_something) { // We only need to flip the screen if something was drawn. video::render_screen(); } else { diff --git a/src/draw_manager.hpp b/src/draw_manager.hpp index 8860bb0cc93..a15044cc540 100644 --- a/src/draw_manager.hpp +++ b/src/draw_manager.hpp @@ -92,6 +92,17 @@ void invalidate_region(const rect& region); /** Mark the entire screen as requiring redraw. */ void invalidate_all(); +/** + * Request an extra render pass. + * + * This is used for blur effects, which need to first render what's + * underneath so that it can be blurred. + * + * There is not currently any limit to the number of extra render passes, + * but do try to keep it finite. + */ +void request_extra_render_pass(); + /** * Ensure that everything which needs to be drawn is drawn. * diff --git a/src/gui/core/canvas.cpp b/src/gui/core/canvas.cpp index 71defea43c4..4b3caaee65d 100644 --- a/src/gui/core/canvas.cpp +++ b/src/gui/core/canvas.cpp @@ -24,6 +24,7 @@ #include "gui/core/canvas_private.hpp" #include "draw.hpp" +#include "draw_manager.hpp" #include "font/text.hpp" #include "formatter.hpp" #include "gettext.hpp" @@ -526,23 +527,19 @@ bool canvas::update_blur(const rect& screen_region, bool force) // We could use the previous render frame, but there could well have been // another element there last frame such as a popup window which we // don't want to be part of the blur. - // The only stable solution is to render in multiple passes. - // For now, we defer rendering of translucent elements to the next frame, - // so one frame is rendered without the element, then the element captures - // the result from that frame and renders itself on the next frame. - // Ultimately even with hardware acceleration of the blur effect - // a similar solution will need to be retained. The difference with a - // better future implementation would be that the multiple rendering - // passes can be managed at a higher level, and that they can be done - // within a single frame. + // The stable solution is to render in multiple passes, + // so that is what we shall do. + // For the first pass, this element and its children are not rendered. if(!deferred_) { DBG_GUI_D << "Deferring blur at " << screen_region; deferred_ = true; + draw_manager::request_extra_render_pass(); return false; } - // Read and blur pixels from the previous render frame. + // For the second pass we read the result of the first pass at + // this widget's location, and blur it. DBG_GUI_D << "Blurring " << screen_region << " depth " << blur_depth_; rect read_region = screen_region; auto setter = draw::set_render_target({});