Viewport: Reduce unnecessary region redraws when scrolling viewports
This commit is contained in:
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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.
|
||||||
|
@@ -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; };
|
||||||
|
15
src/gfx.cpp
15
src/gfx.cpp
@@ -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.
|
||||||
|
@@ -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);
|
||||||
|
164
src/viewport.cpp
164
src/viewport.cpp
@@ -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 } });
|
||||||
int xo = _vp_move_offs.x;
|
}
|
||||||
int yo = _vp_move_offs.y;
|
|
||||||
|
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 ®ion : _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)
|
||||||
|
{
|
||||||
|
const int xo = _vp_move_offs.x;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user