From 3b47b7b09180684bf0359cb2e2c946dada9acb98 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 29 Sep 2020 00:05:30 +0100 Subject: [PATCH] Plans: Improve performance of viewport plan rendering --- src/plans.cpp | 30 +++++++++++++++++ src/plans_base.h | 3 ++ src/plans_cmd.cpp | 1 + src/saveload/plans_sl.cpp | 2 ++ src/viewport.cpp | 68 ++++++++++++++++++++++++++++----------- 5 files changed, 85 insertions(+), 19 deletions(-) diff --git a/src/plans.cpp b/src/plans.cpp index 174c636a99..1477825e24 100644 --- a/src/plans.cpp +++ b/src/plans.cpp @@ -17,3 +17,33 @@ INSTANTIATE_POOL_METHODS(Plan) Plan *_current_plan = nullptr; Plan *_new_plan = nullptr; + +void PlanLine::UpdateVisualExtents() +{ + if (_network_dedicated) return; + + if (this->tiles.size() < 2) { + this->viewport_extents = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; + return; + } + + int min_x = INT_MAX; + int max_x = INT_MIN; + int min_y = INT_MAX; + int max_y = INT_MIN; + + for (TileIndex t : this->tiles) { + const int tile_x = TileX(t); + const int tile_y = TileY(t); + const int x = tile_y - tile_x; + const int y = tile_y + tile_x; + + if (x < min_x) min_x = x; + if (x > max_x) max_x = x; + if (y < min_y) min_y = y; + if (y > max_y) max_y = y; + } + + this->viewport_extents = { (int)(min_x * TILE_SIZE * 2 * ZOOM_LVL_BASE), (int)(min_y * TILE_SIZE * ZOOM_LVL_BASE), + (int)((max_x + 1) * TILE_SIZE * 2 * ZOOM_LVL_BASE), (int)((max_y + 1) * TILE_SIZE * ZOOM_LVL_BASE) }; +} diff --git a/src/plans_base.h b/src/plans_base.h index b5dc1b6cb4..31b13d5172 100644 --- a/src/plans_base.h +++ b/src/plans_base.h @@ -31,6 +31,7 @@ struct PlanLine { bool visible; bool focused; TileVector tiles; + Rect viewport_extents; PlanLine() { @@ -153,6 +154,8 @@ struct PlanLine { if (count == 0) return INVALID_TILE; return TileXY(x / count, y / count); } + + void UpdateVisualExtents(); }; struct Plan : PlanPool::PoolItem<&_plan_pool> { diff --git a/src/plans_cmd.cpp b/src/plans_cmd.cpp index 791e1d6f67..67ca773cb5 100644 --- a/src/plans_cmd.cpp +++ b/src/plans_cmd.cpp @@ -69,6 +69,7 @@ CommandCost CmdAddPlanLine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 p->lines.pop_back(); return CMD_ERROR; } + pl->UpdateVisualExtents(); if (p->IsListable()) { pl->SetVisibility(p->visible); if (p->visible) pl->MarkDirty(); diff --git a/src/saveload/plans_sl.cpp b/src/saveload/plans_sl.cpp index 2ab51812f0..9e1e51c0ba 100644 --- a/src/saveload/plans_sl.cpp +++ b/src/saveload/plans_sl.cpp @@ -60,6 +60,7 @@ static void Load_PLAN() const size_t tile_count = SlReadUint32(); pl->tiles.resize(tile_count); SlArray(&pl->tiles[0], tile_count, SLE_UINT32); + pl->UpdateVisualExtents(); } p->SetVisibility(false); } @@ -79,6 +80,7 @@ static void Load_PLANLINE() size_t plsz = SlGetFieldLength() / sizeof(TileIndex); pl->tiles.resize(plsz); SlArray(&pl->tiles[0], plsz, SLE_UINT32); + pl->UpdateVisualExtents(); } for (Plan *p : Plan::Iterate()) { diff --git a/src/viewport.cpp b/src/viewport.cpp index 6d5ac2ac00..d2c45099f6 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -2419,27 +2419,57 @@ void ViewportDrawPlans(const Viewport *vp) DrawPixelInfo *old_dpi = _cur_dpi; _cur_dpi = &_dpi_for_text; - for (Plan *p : Plan::Iterate()) { - if (!p->IsVisible()) continue; - for (PlanLineVector::iterator it = p->lines.begin(); it != p->lines.end(); it++) { - PlanLine *pl = *it; - if (!pl->visible) continue; - for (uint i = 1; i < pl->tiles.size(); i++) { - const TileIndex from_tile = pl->tiles[i-1]; - const Point from_pt = RemapCoords2(TileX(from_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(from_tile) * TILE_SIZE + TILE_SIZE / 2); - const int from_x = UnScaleByZoom(from_pt.x, vp->zoom); - const int from_y = UnScaleByZoom(from_pt.y, vp->zoom); + if (Plan::GetNumItems() != 0) { + const Rect bounds = { + ScaleByZoom(_dpi_for_text.left - 2, vp->zoom), + ScaleByZoom(_dpi_for_text.top - 2, vp->zoom), + ScaleByZoom(_dpi_for_text.left + _dpi_for_text.width + 2, vp->zoom), + ScaleByZoom(_dpi_for_text.top + _dpi_for_text.height + 2, vp->zoom) + (int)(ZOOM_LVL_BASE * TILE_HEIGHT * _settings_game.construction.max_heightlevel) + }; - const TileIndex to_tile = pl->tiles[i]; - const Point to_pt = RemapCoords2(TileX(to_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(to_tile) * TILE_SIZE + TILE_SIZE / 2); - const int to_x = UnScaleByZoom(to_pt.x, vp->zoom); - const int to_y = UnScaleByZoom(to_pt.y, vp->zoom); + const int min_coord_delta = bounds.left / (int)(2 * ZOOM_LVL_BASE * TILE_SIZE); + const int max_coord_delta = (bounds.right / (int)(2 * ZOOM_LVL_BASE * TILE_SIZE)) + 1; + + for (Plan *p : Plan::Iterate()) { + if (!p->IsVisible()) continue; + for (PlanLineVector::iterator it = p->lines.begin(); it != p->lines.end(); it++) { + PlanLine *pl = *it; + if (!pl->visible) continue; + + if ( + bounds.left > pl->viewport_extents.right || + bounds.right < pl->viewport_extents.left || + bounds.top > pl->viewport_extents.bottom || + bounds.bottom < pl->viewport_extents.top + ) { + continue; + } - GfxDrawLine(from_x, from_y, to_x, to_y, PC_BLACK, 3); - if (pl->focused) { - GfxDrawLine(from_x, from_y, to_x, to_y, PC_RED, 1); - } else { - GfxDrawLine(from_x, from_y, to_x, to_y, PC_WHITE, 1); + TileIndex to_tile = pl->tiles[0]; + int to_coord_delta = (int)TileY(to_tile) - (int)TileX(to_tile); + for (uint i = 1; i < pl->tiles.size(); i++) { + const TileIndex from_tile = to_tile; + const int from_coord_delta = to_coord_delta; + to_tile = pl->tiles[i]; + to_coord_delta = (int)TileY(to_tile) - (int)TileX(to_tile); + + if (to_coord_delta < min_coord_delta && from_coord_delta < min_coord_delta) continue; + if (to_coord_delta > max_coord_delta && from_coord_delta > max_coord_delta) continue; + + const Point from_pt = RemapCoords2(TileX(from_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(from_tile) * TILE_SIZE + TILE_SIZE / 2); + const int from_x = UnScaleByZoom(from_pt.x, vp->zoom); + const int from_y = UnScaleByZoom(from_pt.y, vp->zoom); + + const Point to_pt = RemapCoords2(TileX(to_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(to_tile) * TILE_SIZE + TILE_SIZE / 2); + const int to_x = UnScaleByZoom(to_pt.x, vp->zoom); + const int to_y = UnScaleByZoom(to_pt.y, vp->zoom); + + GfxDrawLine(from_x, from_y, to_x, to_y, PC_BLACK, 3); + if (pl->focused) { + GfxDrawLine(from_x, from_y, to_x, to_y, PC_RED, 1); + } else { + GfxDrawLine(from_x, from_y, to_x, to_y, PC_WHITE, 1); + } } } }