Viewport: Reduce unnecessary region redraws when scrolling viewports

This commit is contained in:
Jonathan G Rennison
2020-02-09 00:34:47 +00:00
parent 4d9e07e885
commit a152e2327c
11 changed files with 144 additions and 52 deletions

View File

@@ -509,7 +509,7 @@ void Blitter_32bppAnim::CopyToBuffer(const void *video, void *dst, int width, in
} }
} }
void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) void Blitter_32bppAnim::ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y)
{ {
assert(!_screen_disable_anim); assert(!_screen_disable_anim);
assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch); assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);

View File

@@ -44,7 +44,7 @@ public:
void DrawRect(void *video, int width, int height, uint8 colour) override; void DrawRect(void *video, int width, int height, uint8 colour) override;
void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyFromBuffer(void *video, const void *src, int width, int height) override;
void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override;
void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) override;
int BufferSize(int width, int height) override; int BufferSize(int width, int height) override;
void PaletteAnimate(const Palette &palette) override; void PaletteAnimate(const Palette &palette) override;
Blitter::PaletteAnimation UsePaletteAnimation() override; Blitter::PaletteAnimation UsePaletteAnimation() override;

View File

@@ -101,7 +101,7 @@ void Blitter_32bppBase::CopyImageToBuffer(const void *video, void *dst, int widt
} }
} }
void Blitter_32bppBase::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) void Blitter_32bppBase::ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y)
{ {
const uint32 *src; const uint32 *src;
uint32 *dst; uint32 *dst;

View File

@@ -28,7 +28,7 @@ public:
void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyFromBuffer(void *video, const void *src, int width, int height) override;
void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override;
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;
void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) override;
int BufferSize(int width, int height) override; int BufferSize(int width, int height) override;
void PaletteAnimate(const Palette &palette) override; void PaletteAnimate(const Palette &palette) override;
Blitter::PaletteAnimation UsePaletteAnimation() override; Blitter::PaletteAnimation UsePaletteAnimation() override;

View File

@@ -90,7 +90,7 @@ void Blitter_8bppBase::CopyImageToBuffer(const void *video, void *dst, int width
} }
} }
void Blitter_8bppBase::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) void Blitter_8bppBase::ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y)
{ {
const uint8 *src; const uint8 *src;
uint8 *dst; uint8 *dst;

View File

@@ -25,7 +25,7 @@ public:
void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyFromBuffer(void *video, const void *src, int width, int height) override;
void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override;
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;
void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) override;
int BufferSize(int width, int height) override; int BufferSize(int width, int height) override;
void PaletteAnimate(const Palette &palette) override; void PaletteAnimate(const Palette &palette) override;
Blitter::PaletteAnimation UsePaletteAnimation() override; Blitter::PaletteAnimation UsePaletteAnimation() override;

View File

@@ -198,7 +198,7 @@ public:
* @param scroll_x How much to scroll in X. * @param scroll_x How much to scroll in X.
* @param scroll_y How much to scroll in Y. * @param scroll_y How much to scroll in Y.
*/ */
virtual void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) = 0; virtual void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) = 0;
/** /**
* Calculate how much memory there is needed for an image of this size in the video-buffer. * Calculate how much memory there is needed for an image of this size in the video-buffer.

View File

@@ -28,7 +28,7 @@ public:
void CopyFromBuffer(void *video, const void *src, int width, int height) override {}; void CopyFromBuffer(void *video, const void *src, int width, int height) override {};
void CopyToBuffer(const void *video, void *dst, int width, int height) override {}; void CopyToBuffer(const void *video, void *dst, int width, int height) override {};
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override {}; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override {};
void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override {}; void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) override {};
int BufferSize(int width, int height) override { return 0; }; int BufferSize(int width, int height) override { return 0; };
void PaletteAnimate(const Palette &palette) override { }; void PaletteAnimate(const Palette &palette) override { };
Blitter::PaletteAnimation UsePaletteAnimation() override { return Blitter::PALETTE_ANIMATION_NONE; }; Blitter::PaletteAnimation UsePaletteAnimation() override { return Blitter::PALETTE_ANIMATION_NONE; };

View File

@@ -79,21 +79,6 @@ static uint _dirty_bytes_per_line = 0;
static byte *_dirty_blocks = nullptr; static byte *_dirty_blocks = nullptr;
extern uint _dirty_block_colour; extern uint _dirty_block_colour;
void GfxScroll(int left, int top, int width, int height, int xo, int yo)
{
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
if (xo == 0 && yo == 0) return;
if (_cursor.visible) UndrawMouseCursor();
if (_networking) NetworkUndrawChatMessage();
blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
/* This part of the screen is now dirty. */
VideoDriver::GetInstance()->MakeDirty(left, top, width, height);
}
/** /**
* Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen. * Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.

View File

@@ -86,7 +86,6 @@ void UndrawMouseCursor();
static const int DRAW_STRING_BUFFER = 2048; static const int DRAW_STRING_BUFFER = 2048;
void RedrawScreenRect(int left, int top, int right, int bottom); void RedrawScreenRect(int left, int top, int right, int bottom);
void GfxScroll(int left, int top, int width, int height, int xo, int yo);
Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI);
void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr); void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr);

View File

@@ -105,6 +105,7 @@
#include "gui.h" #include "gui.h"
#include "core/container_func.hpp" #include "core/container_func.hpp"
#include "tunnelbridge_map.h" #include "tunnelbridge_map.h"
#include "video/video_driver.hpp"
#include <map> #include <map>
#include <vector> #include <vector>
@@ -391,9 +392,15 @@ void InitializeWindowViewport(Window *w, int x, int y,
static Point _vp_move_offs; static Point _vp_move_offs;
static void DoSetViewportPosition(const Window *w, int left, int top, int width, int height) struct ViewportRedrawRegion {
Rect coords;
};
static std::vector<ViewportRedrawRegion> _vp_redraw_regions;
static void DoViewportRedrawRegions(const Window *w, int left, int top, int width, int height)
{ {
IncrementWindowUpdateNumber(); if (width <= 0 || height <= 0) return;
FOR_ALL_WINDOWS_FROM_BACK_FROM(w, w) { FOR_ALL_WINDOWS_FROM_BACK_FROM(w, w) {
if (left + width > w->left && if (left + width > w->left &&
@@ -402,26 +409,26 @@ static void DoSetViewportPosition(const Window *w, int left, int top, int width,
w->top + w->height > top) { w->top + w->height > top) {
if (left < w->left) { if (left < w->left) {
DoSetViewportPosition(w, left, top, w->left - left, height); DoViewportRedrawRegions(w, left, top, w->left - left, height);
DoSetViewportPosition(w, left + (w->left - left), top, width - (w->left - left), height); DoViewportRedrawRegions(w, left + (w->left - left), top, width - (w->left - left), height);
return; return;
} }
if (left + width > w->left + w->width) { if (left + width > w->left + w->width) {
DoSetViewportPosition(w, left, top, (w->left + w->width - left), height); DoViewportRedrawRegions(w, left, top, (w->left + w->width - left), height);
DoSetViewportPosition(w, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height); DoViewportRedrawRegions(w, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height);
return; return;
} }
if (top < w->top) { if (top < w->top) {
DoSetViewportPosition(w, left, top, width, (w->top - top)); DoViewportRedrawRegions(w, left, top, width, (w->top - top));
DoSetViewportPosition(w, left, top + (w->top - top), width, height - (w->top - top)); DoViewportRedrawRegions(w, left, top + (w->top - top), width, height - (w->top - top));
return; return;
} }
if (top + height > w->top + w->height) { if (top + height > w->top + w->height) {
DoSetViewportPosition(w, left, top, width, (w->top + w->height - top)); DoViewportRedrawRegions(w, left, top, width, (w->top + w->height - top));
DoSetViewportPosition(w, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top)); DoViewportRedrawRegions(w, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top));
return; return;
} }
@@ -429,31 +436,132 @@ static void DoSetViewportPosition(const Window *w, int left, int top, int width,
} }
} }
_vp_redraw_regions.push_back({ { left, top, left + width, top + height } });
}
static void DoSetViewportPositionFillRegion(int left, int top, int width, int height, int xo, int yo) {
int src_left = left - xo;
int src_top = top - yo;
int src_right = src_left + width;
int src_bottom = src_top + height;
for (const auto &region : _vp_redraw_regions) {
if (region.coords.left < src_right &&
region.coords.right > src_left &&
region.coords.top < src_bottom &&
region.coords.bottom > src_top) {
/* can use this region as a source */
if (src_left < region.coords.left) {
DoSetViewportPositionFillRegion(src_left + xo, src_top + yo, region.coords.left - src_left, height, xo, yo);
src_left = region.coords.left;
width = src_right - src_left;
}
if (src_top < region.coords.top) {
DoSetViewportPositionFillRegion(src_left + xo, src_top + yo, width, region.coords.top - src_top, xo, yo);
src_top = region.coords.top;
height = src_bottom - src_top;
}
if (src_right > region.coords.right) {
DoSetViewportPositionFillRegion(region.coords.right + xo, src_top + yo, src_right - region.coords.right, height, xo, yo);
src_right = region.coords.right;
width = src_right - src_left;
}
if (src_bottom > region.coords.bottom) {
DoSetViewportPositionFillRegion(src_left + xo, region.coords.bottom + yo, width, src_bottom - region.coords.bottom, xo, yo);
src_bottom = region.coords.bottom;
height = src_bottom - src_top;
}
if (xo >= 0) {
/* scrolling left, moving pixels right */
width += xo;
} else {
/* scrolling right, moving pixels left */
src_left += xo;
width -= xo;
}
if (yo >= 0) {
/* scrolling down, moving pixels up */
height += yo;
} else {
/* scrolling up, moving pixels down */
src_top += yo;
height -= yo;
}
BlitterFactory::GetCurrentBlitter()->ScrollBuffer(_screen.dst_ptr, src_left, src_top, width, height, xo, yo);
return;
}
}
DrawOverlappedWindowForAll(left, top, left + width, top + height);
};
static void DoSetViewportPosition(const Window *w, const int left, const int top, const int width, const int height)
{ {
int xo = _vp_move_offs.x; const int xo = _vp_move_offs.x;
int yo = _vp_move_offs.y; const int yo = _vp_move_offs.y;
if (xo == 0 && yo == 0) return;
IncrementWindowUpdateNumber();
_vp_redraw_regions.clear();
DoViewportRedrawRegions(w, left, top, width, height);
if (abs(xo) >= width || abs(yo) >= height) { if (abs(xo) >= width || abs(yo) >= height) {
/* fully_outside */ /* fully outside */
RedrawScreenRect(left, top, left + width, top + height); for (ViewportRedrawRegion &vrr : _vp_redraw_regions) {
RedrawScreenRect(vrr.coords.left, vrr.coords.top, vrr.coords.right, vrr.coords.bottom);
}
return; return;
} }
GfxScroll(left, top, width, height, xo, yo); Blitter *blitter = BlitterFactory::GetCurrentBlitter();
if (xo > 0) { if (_cursor.visible) UndrawMouseCursor();
RedrawScreenRect(left, top, xo + left, top + height);
left += xo; if (_networking) NetworkUndrawChatMessage();
width -= xo;
} else if (xo < 0) { std::sort(_vp_redraw_regions.begin(), _vp_redraw_regions.end(), [&](const ViewportRedrawRegion &a, const ViewportRedrawRegion &b) {
RedrawScreenRect(left + width + xo, top, left + width, top + height); if (a.coords.right <= b.coords.left) return xo > 0;
width += xo; if (a.coords.left >= b.coords.right) return xo < 0;
if (a.coords.bottom <= b.coords.top) return yo > 0;
if (a.coords.top >= b.coords.bottom) return yo < 0;
NOT_REACHED();
});
while (!_vp_redraw_regions.empty()) {
const Rect &rect = _vp_redraw_regions.back().coords;
int left = rect.left;
int top = rect.top;
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
_vp_redraw_regions.pop_back();
VideoDriver::GetInstance()->MakeDirty(left, top, width, height);
int fill_width = abs(xo);
int fill_height = abs(yo);
if (fill_width < width && fill_height < height) {
blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
} else {
if (width < fill_width) fill_width = width;
if (height < fill_height) fill_height = height;
} }
if (xo < 0) {
if (yo > 0) { /* scrolling right, moving pixels left, fill in on right */
RedrawScreenRect(left, top, width + left, top + yo); width -= fill_width;
} else if (yo < 0) { DoSetViewportPositionFillRegion(left + width, top, fill_width, height, xo, yo);
RedrawScreenRect(left, top + height + yo, width + left, top + height); } else if (xo > 0) {
/* scrolling left, moving pixels right, fill in on left */
DoSetViewportPositionFillRegion(left, top, fill_width, height, xo, yo);
width -= fill_width;
left += fill_width;
}
if (yo < 0 && width > 0) {
/* scrolling down, moving pixels up, fill in at bottom */
height -= fill_height;
DoSetViewportPositionFillRegion(left, top + height, width, fill_height, xo, yo);
} else if (yo > 0 && width > 0) {
/* scrolling up, moving pixels down, fill in at top */
DoSetViewportPositionFillRegion(left, top, width, fill_height, xo, yo);
} }
} }
} }