mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-21 21:16:02 +00:00
Offscreen rendering for windows.
Windows will now render to an offscreen buffer. They only render when something changes, and the re-rendered area is kept to a minimum. When the window needs to be redrawn to the screen, it simply copies the appropriate region from the offscreen buffer. This should greatly improve performance in cases where a window isn't changing its contents, but needs to be redrawn frequently. For example a pop-up dialog being drawn over animated terrain (see #7615).
This commit is contained in:
parent
18c2ad3bf8
commit
0eada04f55
@ -378,10 +378,14 @@ void widget::draw_background()
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Rect dest = calculate_blitting_rectangle();
|
||||
SDL_Rect clip = calculate_clipping_rectangle();
|
||||
clip.x -= dest.x; clip.y -= dest.y;
|
||||
// Set viewport and clip so we can draw in local coordinates.
|
||||
rect dest = calculate_blitting_rectangle();
|
||||
rect clip = calculate_clipping_rectangle();
|
||||
// Presumably we are drawing to our window's render buffer.
|
||||
point window_origin = get_window()->get_origin();
|
||||
dest.shift(-window_origin);
|
||||
auto view_setter = draw::set_viewport(dest);
|
||||
clip.shift(-get_origin());
|
||||
auto clip_setter = draw::reduce_clip(clip);
|
||||
|
||||
draw_debug_border();
|
||||
@ -396,10 +400,14 @@ void widget::draw_children()
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Rect dest = calculate_blitting_rectangle();
|
||||
SDL_Rect clip = calculate_clipping_rectangle();
|
||||
clip.x -= dest.x; clip.y -= dest.y;
|
||||
// Set viewport and clip so we can draw in local coordinates.
|
||||
rect dest = calculate_blitting_rectangle();
|
||||
rect clip = calculate_clipping_rectangle();
|
||||
// Presumably we are drawing to our window's render buffer.
|
||||
point window_origin = get_window()->get_origin();
|
||||
dest.shift(-window_origin);
|
||||
auto view_setter = draw::set_viewport(dest);
|
||||
clip.shift(-get_origin());
|
||||
auto clip_setter = draw::reduce_clip(clip);
|
||||
|
||||
impl_draw_children();
|
||||
@ -413,10 +421,14 @@ void widget::draw_foreground()
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Rect dest = calculate_blitting_rectangle();
|
||||
SDL_Rect clip = calculate_clipping_rectangle();
|
||||
clip.x -= dest.x; clip.y -= dest.y;
|
||||
// Set viewport and clip so we can draw in local coordinates.
|
||||
rect dest = calculate_blitting_rectangle();
|
||||
rect clip = calculate_clipping_rectangle();
|
||||
// Presumably we are drawing to our window's render buffer.
|
||||
point window_origin = get_window()->get_origin();
|
||||
dest.shift(-window_origin);
|
||||
auto view_setter = draw::set_viewport(dest);
|
||||
clip.shift(-get_origin());
|
||||
auto clip_setter = draw::reduce_clip(clip);
|
||||
|
||||
impl_draw_foreground();
|
||||
@ -452,6 +464,7 @@ void widget::queue_redraw()
|
||||
|
||||
void widget::queue_redraw(const rect& region)
|
||||
{
|
||||
get_window()->queue_rerender(region);
|
||||
draw_manager::invalidate_region(region);
|
||||
}
|
||||
|
||||
|
@ -646,15 +646,84 @@ void window::hide()
|
||||
hidden_ = true;
|
||||
}
|
||||
|
||||
void window::update_render_textures()
|
||||
{
|
||||
point draw = get_size();
|
||||
point render = draw * video::get_pixel_scale();
|
||||
|
||||
// Check that the render buffer size is correct.
|
||||
point buf_raw = render_buffer_.get_raw_size();
|
||||
point buf_draw = render_buffer_.draw_size();
|
||||
bool raw_size_changed = buf_raw.x != render.x || buf_raw.y != render.y;
|
||||
bool draw_size_changed = buf_draw.x != draw.x || buf_draw.y != draw.y;
|
||||
if (!raw_size_changed && !draw_size_changed) {
|
||||
// buffers are fine
|
||||
return;
|
||||
}
|
||||
|
||||
if(raw_size_changed) {
|
||||
LOG_DP << "regenerating window render buffer as " << render;
|
||||
render_buffer_ = texture(render.x, render.y, SDL_TEXTUREACCESS_TARGET);
|
||||
}
|
||||
if(raw_size_changed || draw_size_changed) {
|
||||
LOG_DP << "updating window render buffer draw size to " << draw;
|
||||
render_buffer_.set_draw_size(draw);
|
||||
}
|
||||
|
||||
// Clear the entire texture, just in case
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
auto setter = draw::set_render_target(render_buffer_);
|
||||
draw::fill(0,0,0,0);
|
||||
}
|
||||
|
||||
queue_rerender();
|
||||
}
|
||||
|
||||
void window::queue_rerender()
|
||||
{
|
||||
queue_rerender(get_rectangle());
|
||||
}
|
||||
|
||||
void window::queue_rerender(const rect& screen_region)
|
||||
{
|
||||
// More than one region updating per-frame should be rare.
|
||||
// Just rerender the minimal area that covers everything.
|
||||
rect local_region = screen_region;
|
||||
local_region.shift(-get_origin());
|
||||
awaiting_rerender_.expand_to_cover(local_region);
|
||||
}
|
||||
|
||||
void window::render()
|
||||
{
|
||||
update_render_textures();
|
||||
if (awaiting_rerender_.empty()) {
|
||||
return;
|
||||
}
|
||||
DBG_DP << "window::render() local " << awaiting_rerender_;
|
||||
auto target_setter = draw::set_render_target(render_buffer_);
|
||||
auto clip_setter = draw::override_clip(awaiting_rerender_);
|
||||
draw();
|
||||
awaiting_rerender_ = sdl::empty_rect;
|
||||
}
|
||||
|
||||
bool window::expose(const rect& region)
|
||||
{
|
||||
DBG_DP << "window::expose " << region;
|
||||
rect i = get_rectangle().intersect(region);
|
||||
i.clip(draw::get_clip());
|
||||
if (i.empty()) {
|
||||
|
||||
// Calculate the destination region we need to draw.
|
||||
rect dst = get_rectangle().intersect(region);
|
||||
dst.clip(draw::get_clip());
|
||||
if (dst.empty()) {
|
||||
return false;
|
||||
}
|
||||
draw();
|
||||
|
||||
// Blit from the pre-rendered buffer.
|
||||
rect src = dst;
|
||||
src.shift(-get_origin());
|
||||
render_buffer_.set_src(src);
|
||||
draw::blit(render_buffer_, dst);
|
||||
render_buffer_.clear_src();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -159,12 +159,44 @@ public:
|
||||
*/
|
||||
virtual void layout() override;
|
||||
|
||||
/** Called by draw_manager when it believes a redraw is necessary. */
|
||||
/** Ensure the window's internal render buffer is up-to-date.
|
||||
*
|
||||
* This renders the window to an off-screen texture, which is then
|
||||
* copied to the screen during expose().
|
||||
*/
|
||||
virtual void render() override;
|
||||
|
||||
private:
|
||||
/** The internal render buffer used by render() and expose(). */
|
||||
texture render_buffer_ = {};
|
||||
|
||||
/** The part of the window (if any) currently marked for rerender. */
|
||||
rect awaiting_rerender_;
|
||||
|
||||
/** Ensure render textures are valid and correct. */
|
||||
void update_render_textures();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Called by draw_manager when it believes a redraw is necessary.
|
||||
* Can be called multiple times per vsync.
|
||||
*/
|
||||
virtual bool expose(const rect& region) override;
|
||||
|
||||
/** The current draw location of the window, on the screen. */
|
||||
virtual rect screen_location() override;
|
||||
|
||||
/**
|
||||
* Queue a rerender of the internal render buffer.
|
||||
*
|
||||
* This does not request a repaint. Ordinarily use queue_redraw()
|
||||
* on a widget, which will call this automatically.
|
||||
*
|
||||
* @param region The region to rerender in screen coordinates.
|
||||
*/
|
||||
void queue_rerender(const rect& region);
|
||||
void queue_rerender();
|
||||
|
||||
/** The status of the window. */
|
||||
enum class status {
|
||||
NEW, /**< The window is new and not yet shown. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user