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.
This commit is contained in:
Tommy 2023-10-29 03:24:52 +13:00
parent 8d4deeef02
commit 7695ee7159
3 changed files with 34 additions and 11 deletions

View File

@ -48,6 +48,7 @@ std::vector<rect> 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 {

View File

@ -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.
*

View File

@ -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({});