Change how dirty screen, window and viewport areas are tracked for later redrawing

Track dirty viewport areas seperately form general screen redraws.
Maintain a dirty block grid per viewport, with a smaller block size.
Use even smaller block size in viewport map mode.

Use a rectangle array for general screen redraws instead of a block grid.

Add a dirty bit to windows and widgets, to simplify the common case
of repainting a whole window or widget, without catching
neighbouring windows or viewports.
This commit is contained in:
Jonathan G Rennison
2020-02-21 19:45:07 +00:00
parent d740a19e5f
commit 8f442500ea
18 changed files with 626 additions and 182 deletions

View File

@@ -258,7 +258,7 @@ struct ViewportDrawer {
Point foundation_offset[FOUNDATION_PART_END]; ///< Pixel offset for ground sprites on the foundations.
};
static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int right, int bottom);
static void MarkViewportDirty(ViewPort * const vp, int left, int top, int right, int bottom);
static void MarkRouteStepDirty(RouteStepsMap::const_iterator cit);
static void MarkRouteStepDirty(const TileIndex tile, uint order_nr);
@@ -372,6 +372,8 @@ void InitializeWindowViewport(Window *w, int x, int y,
vp->map_type = VPMT_BEGIN;
UpdateViewportSizeZoom(vp);
Point pt;
if (follow_flags & 0x80000000) {
@@ -575,6 +577,15 @@ static void DoSetViewportPosition(const Window *w, const int left, const int top
}
}
inline void UpdateViewportDirtyBlockLeftMargin(ViewPort *vp)
{
if (vp->zoom >= ZOOM_LVL_DRAW_MAP) {
vp->dirty_block_left_margin = 0;
} else {
vp->dirty_block_left_margin = UnScaleByZoomLower((-vp->virtual_left) & 127, vp->zoom);
}
}
static void SetViewportPosition(Window *w, int x, int y, bool force_update_overlay)
{
ViewPort *vp = w->viewport;
@@ -585,6 +596,7 @@ static void SetViewportPosition(Window *w, int x, int y, bool force_update_overl
vp->virtual_left = x;
vp->virtual_top = y;
UpdateViewportDirtyBlockLeftMargin(vp);
if (force_update_overlay || IsViewportOverlayOutsideCachedRegion(w)) RebuildViewportOverlay(w, true);
@@ -1479,8 +1491,8 @@ static void ViewportAddLandscape()
* - Right column is column of upper_right (rounded up) and one column to the right.
* Note: Integer-division does not round down for negative numbers, so ensure rounding with another increment/decrement.
*/
int left_column = (upper_left.y - upper_left.x) / (int)TILE_SIZE - 2;
int right_column = (upper_right.y - upper_right.x) / (int)TILE_SIZE + 2;
int left_column = DivTowardsNegativeInf(upper_left.y - upper_left.x, (int)TILE_SIZE) - 1;
int right_column = DivTowardsPositiveInf(upper_right.y - upper_right.x, (int)TILE_SIZE) + 1;
int potential_bridge_height = ZOOM_LVL_BASE * TILE_HEIGHT * _settings_game.construction.max_bridge_height;
@@ -1488,7 +1500,7 @@ static void ViewportAddLandscape()
* The first row that could possibly be visible is the row above upper_left (if it is at height 0).
* Due to integer-division not rounding down for negative numbers, we need another decrement.
*/
int row = (upper_left.x + upper_left.y) / (int)TILE_SIZE - 2;
int row = DivTowardsNegativeInf(upper_left.y + upper_left.x, (int)TILE_SIZE) - 1;
bool last_row = false;
for (; !last_row; row++) {
last_row = true;
@@ -3003,8 +3015,8 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom
_vd.dpi.pitch = old_dpi->pitch;
_vd.last_child = nullptr;
int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
int x = UnScaleByZoomLower(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
int y = UnScaleByZoomLower(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
_vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top);
@@ -3098,7 +3110,7 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom
* Make sure we don't draw a too big area at a time.
* If we do, the sprite sorter will run into major performance problems and the sprite memory may overflow.
*/
static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom)
void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom)
{
if ((vp->zoom < ZOOM_LVL_DRAW_MAP) && ((int64)ScaleByZoom(bottom - top, vp->zoom) * (int64)ScaleByZoom(right - left, vp->zoom) > (int64)(1000000 * ZOOM_LVL_BASE * ZOOM_LVL_BASE))) {
if ((bottom - top) > (right - left)) {
@@ -3120,7 +3132,7 @@ static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, in
}
}
static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right, int bottom)
static inline void ViewportDraw(ViewPort *vp, int left, int top, int right, int bottom)
{
if (right <= vp->left || bottom <= vp->top) return;
@@ -3134,6 +3146,8 @@ static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right
if (top < vp->top) top = vp->top;
if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
vp->is_drawn = true;
ViewportDrawChk(vp, left, top, right, bottom);
}
@@ -3230,6 +3244,15 @@ void UpdateViewportPosition(Window *w)
}
}
void UpdateViewportSizeZoom(ViewPort *vp)
{
vp->dirty_blocks_per_column = CeilDiv(vp->height, vp->GetDirtyBlockHeight());
vp->dirty_blocks_per_row = CeilDiv(vp->width, vp->GetDirtyBlockWidth());
uint size = vp->dirty_blocks_per_row * vp->dirty_blocks_per_column;
vp->dirty_blocks.assign(size, false);
UpdateViewportDirtyBlockLeftMargin(vp);
}
void UpdateActiveScrollingViewport(Window *w)
{
if (w && (!_settings_client.gui.show_scrolling_viewport_on_map || w->viewport->zoom >= ZOOM_LVL_DRAW_MAP)) w = nullptr;
@@ -3283,7 +3306,7 @@ void UpdateActiveScrollingViewport(Window *w)
* @param bottom Bottom edge of area to repaint
* @ingroup dirty
*/
static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int right, int bottom)
static void MarkViewportDirty(ViewPort * const vp, int left, int top, int right, int bottom)
{
/* Rounding wrt. zoom-out level */
right += (1 << vp->zoom) - 1;
@@ -3305,12 +3328,21 @@ static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int
if (top >= vp->virtual_height) return;
SetDirtyBlocks(
UnScaleByZoomLower(left, vp->zoom) + vp->left,
UnScaleByZoomLower(top, vp->zoom) + vp->top,
UnScaleByZoom(right, vp->zoom) + vp->left + 1,
UnScaleByZoom(bottom, vp->zoom) + vp->top + 1
);
uint x = max<int>(0, UnScaleByZoomLower(left, vp->zoom) - vp->dirty_block_left_margin) >> vp->GetDirtyBlockWidthShift();
uint y = UnScaleByZoomLower(top, vp->zoom) >> vp->GetDirtyBlockHeightShift();
uint w = (max<int>(0, UnScaleByZoomLower(right, vp->zoom) - 1 - vp->dirty_block_left_margin) >> vp->GetDirtyBlockWidthShift()) + 1 - x;
uint h = ((UnScaleByZoom(bottom, vp->zoom) - 1) >> vp->GetDirtyBlockHeightShift()) + 1 - y;
uint column_skip = vp->dirty_blocks_per_column - h;
uint pos = (x * vp->dirty_blocks_per_column) + y;
for (uint i = 0; i < w; i++) {
for (uint j = 0; j < h; j++) {
vp->dirty_blocks[pos] = true;
pos++;
}
pos += column_skip;
}
vp->is_dirty = true;
}
/**
@@ -3324,7 +3356,7 @@ static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int
*/
void MarkAllViewportsDirty(int left, int top, int right, int bottom, const ZoomLevel mark_dirty_if_zoomlevel_is_below)
{
for (const ViewPort * const vp : _viewport_window_cache) {
for (ViewPort * const vp : _viewport_window_cache) {
if (vp->zoom >= mark_dirty_if_zoomlevel_is_below) continue;
MarkViewportDirty(vp, left, top, right, bottom);
}
@@ -3341,7 +3373,7 @@ static void MarkRouteStepDirty(const TileIndex tile, uint order_nr)
assert(tile != INVALID_TILE);
const Point pt = RemapCoords2(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2);
const int char_height = GetCharacterHeight(FS_SMALL) + 1;
for (const ViewPort * const vp : _viewport_window_cache) {
for (ViewPort * const vp : _viewport_window_cache) {
const int half_width = ScaleByZoom((_vp_route_step_width / 2) + 1, vp->zoom);
const int height = ScaleByZoom(_vp_route_step_height_top + char_height * order_nr + _vp_route_step_height_bottom, vp->zoom);
MarkViewportDirty(vp, pt.x - half_width, pt.y - height, pt.x + half_width, pt.y);
@@ -3370,7 +3402,7 @@ void MarkAllViewportMapsDirty(int left, int top, int right, int bottom)
{
Window *w;
FOR_ALL_WINDOWS_FROM_BACK(w) {
const ViewPort *vp = w->viewport;
ViewPort *vp = w->viewport;
if (vp != nullptr && vp->zoom >= ZOOM_LVL_DRAW_MAP) {
assert(vp->width != 0);
MarkViewportDirty(vp, left, top, right, bottom);