diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 12d5efe281..dcdb2e3936 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -36,6 +36,7 @@ #include "game/game.hpp" #include "goal_base.h" #include "story_base.h" +#include "zoning.h" #include "table/strings.h" @@ -119,6 +120,7 @@ void SetLocalCompany(CompanyID new_company) /* ... and redraw the whole screen. */ MarkWholeScreenDirty(); InvalidateWindowClassesData(WC_SIGN_LIST, -1); + ClearZoningCaches(); } /** diff --git a/src/misc.cpp b/src/misc.cpp index 5d4189f1bd..dc2a8edabe 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -33,6 +33,7 @@ #include "viewport_func.h" #include "bridge_signal_map.h" #include "command_func.h" +#include "zoning.h" #include "safeguards.h" @@ -90,6 +91,8 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin FreeSignalPrograms(); FreeSignalDependencies(); + ClearZoningCaches(); + ResetPersistentNewGRFData(); InitializeSound(); diff --git a/src/openttd.cpp b/src/openttd.cpp index 3e6542d23f..b4c1accb91 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -69,6 +69,7 @@ #include "viewport_func.h" #include "thread/thread.h" #include "bridge_signal_map.h" +#include "zoning.h" #include "linkgraph/linkgraphschedule.h" #include "tracerestrict.h" @@ -348,6 +349,8 @@ static void ShutdownGame() FreeSignalPrograms(); FreeSignalDependencies(); + ClearZoningCaches(); + /* No NewGRFs were loaded when it was still bootstrapping. */ if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData(); diff --git a/src/zoning.h b/src/zoning.h index 1ddd068269..4ea457245d 100644 --- a/src/zoning.h +++ b/src/zoning.h @@ -29,6 +29,17 @@ enum ZoningEvaluationMode { ZEM_TRACERESTRICT, ///< Check for restricted signals }; +/** + * Zoning evaluation modes + */ +enum ZoningModeMask { + ZMM_NOTHING = 0, ///< No zoning mask + ZMM_INNER, ///< Inner + ZMM_OUTER, ///< Outer + ZMM_ALL = ZMM_INNER | ZMM_OUTER, +}; +DECLARE_ENUM_AS_BIT_SET(ZoningModeMask) + /** * Global Zoning state structure */ @@ -47,12 +58,13 @@ void DrawTileZoning(const TileInfo *ti); void ShowZoningToolbar(); -void ZoningMarkDirtyStationCoverageArea(const Station *st); +void ZoningMarkDirtyStationCoverageArea(const Station *st, ZoningModeMask mask = ZMM_ALL); inline void ZoningMarkDirtyStationCoverageArea(const Waypoint *st) { } // no-op -inline void ZoningStationWindowOpenClose(const Station *st) -{ - if (_zoning.inner == ZEM_STA_CATCH_WIN || _zoning.outer == ZEM_STA_CATCH_WIN) ZoningMarkDirtyStationCoverageArea(st); -} +void ZoningStationWindowOpenClose(const Station *st); + +void SetZoningMode(bool inner, ZoningEvaluationMode mode); + +void ClearZoningCaches(); #endif /* ZONING_H */ diff --git a/src/zoning_cmd.cpp b/src/zoning_cmd.cpp index 37d80fd461..d3c07b0d1b 100644 --- a/src/zoning_cmd.cpp +++ b/src/zoning_cmd.cpp @@ -26,10 +26,14 @@ #include "tracerestrict.h" #include "window_func.h" #include "zoning.h" +#include "3rdparty/cpp-btree/btree_set.h" Zoning _zoning; static const SpriteID ZONING_INVALID_SPRITE_ID = UINT_MAX; +static btree::btree_set _zoning_cache_inner; +static btree::btree_set _zoning_cache_outer; + /** * Draw the zoning sprites. * @@ -338,6 +342,40 @@ SpriteID TileZoningSpriteEvaluation(TileIndex tile, Owner owner, ZoningEvaluatio } } +inline SpriteID TileZoningSpriteEvaluationCached(TileIndex tile, Owner owner, ZoningEvaluationMode ev_mode, bool is_inner) +{ + if (ev_mode >= ZEM_STA_CATCH && ev_mode <= ZEM_IND_UNSER) { + // cacheable + btree::btree_set &cache = is_inner ? _zoning_cache_inner : _zoning_cache_outer; + auto iter = cache.lower_bound(tile << 3); + if (iter != cache.end() && *iter >> 3 == tile) { + switch (*iter & 7) { + case 0: return ZONING_INVALID_SPRITE_ID; + case 1: return SPR_ZONING_INNER_HIGHLIGHT_RED; + case 2: return SPR_ZONING_INNER_HIGHLIGHT_ORANGE; + case 3: return SPR_ZONING_INNER_HIGHLIGHT_BLACK; + case 4: return SPR_ZONING_INNER_HIGHLIGHT_LIGHT_BLUE; + default: NOT_REACHED(); + } + } else { + SpriteID s = TileZoningSpriteEvaluation(tile, owner, ev_mode); + uint val = tile << 3; + switch (s) { + case ZONING_INVALID_SPRITE_ID: val |= 0; break; + case SPR_ZONING_INNER_HIGHLIGHT_RED: val |= 1; break; + case SPR_ZONING_INNER_HIGHLIGHT_ORANGE: val |= 2; break; + case SPR_ZONING_INNER_HIGHLIGHT_BLACK: val |= 3; break; + case SPR_ZONING_INNER_HIGHLIGHT_LIGHT_BLUE: val |= 4; break; + default: NOT_REACHED(); + } + cache.insert(iter, val); + return s; + } + } else { + return TileZoningSpriteEvaluation(tile, owner, ev_mode); + } +} + /** * Draw the the zoning on the tile. * @@ -351,11 +389,11 @@ void DrawTileZoning(const TileInfo *ti) } if (_zoning.outer != ZEM_NOTHING) { - DrawZoningSprites(SPR_SELECT_TILE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.outer), ti); + DrawZoningSprites(SPR_SELECT_TILE, TileZoningSpriteEvaluationCached(ti->tile, _local_company, _zoning.outer, false), ti); } if (_zoning.inner != ZEM_NOTHING) { - DrawZoningSprites(SPR_ZONING_INNER_HIGHLIGHT_BASE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.inner), ti); + DrawZoningSprites(SPR_ZONING_INNER_HIGHLIGHT_BASE, TileZoningSpriteEvaluationCached(ti->tile, _local_company, _zoning.inner, true), ti); } } @@ -376,18 +414,57 @@ static uint GetZoningModeDependantStationCoverageRadius(const Station *st, Zonin * @param const Station *st * The station to use */ -void ZoningMarkDirtyStationCoverageArea(const Station *st) +void ZoningMarkDirtyStationCoverageArea(const Station *st, ZoningModeMask mask) { if (st->rect.IsEmpty()) return; - uint radius = max(GetZoningModeDependantStationCoverageRadius(st, _zoning.outer), GetZoningModeDependantStationCoverageRadius(st, _zoning.inner)); + uint outer_radius = mask & ZMM_OUTER ? GetZoningModeDependantStationCoverageRadius(st, _zoning.outer) : 0; + uint inner_radius = mask & ZMM_INNER ? GetZoningModeDependantStationCoverageRadius(st, _zoning.inner) : 0; + uint radius = max(outer_radius, inner_radius); if (radius > 0) { Rect rect = st->GetCatchmentRectUsingRadius(radius); - for (int x = rect.left; x <= rect.right; x++) { - for (int y = rect.top; y <= rect.bottom; y++) { + for (int y = rect.top; y <= rect.bottom; y++) { + for (int x = rect.left; x <= rect.right; x++) { MarkTileDirtyByTile(TileXY(x, y)); } } + auto invalidate_cache_rect = [&](btree::btree_set &cache) { + for (int y = rect.top; y <= rect.bottom; y++) { + auto iter = cache.lower_bound(TileXY(rect.left, y) << 3); + auto end_iter = iter; + uint end = TileXY(rect.right, y) << 3; + while (end_iter != cache.end() && *end_iter < end) ++end_iter; + cache.erase(iter, end_iter); + } + }; + if (outer_radius) invalidate_cache_rect(_zoning_cache_outer); + if (inner_radius) invalidate_cache_rect(_zoning_cache_inner); } } + +void ZoningStationWindowOpenClose(const Station *st) +{ + ZoningModeMask mask = ZMM_NOTHING; + if (_zoning.inner == ZEM_STA_CATCH_WIN) mask |= ZMM_INNER; + if (_zoning.outer == ZEM_STA_CATCH_WIN) mask |= ZMM_OUTER; + if (mask != ZMM_NOTHING) ZoningMarkDirtyStationCoverageArea(st, mask); +} + +void ClearZoningCaches() +{ + _zoning_cache_inner.clear(); + _zoning_cache_outer.clear(); +} + +void SetZoningMode(bool inner, ZoningEvaluationMode mode) +{ + ZoningEvaluationMode ¤t_mode = inner ? _zoning.inner : _zoning.outer; + btree::btree_set &cache = inner ? _zoning_cache_inner : _zoning_cache_outer; + + if (current_mode == mode) return; + + current_mode = mode; + cache.clear(); + MarkWholeScreenDirty(); +} diff --git a/src/zoning_gui.cpp b/src/zoning_gui.cpp index 8907f83dee..b02cdafa16 100644 --- a/src/zoning_gui.cpp +++ b/src/zoning_gui.cpp @@ -103,15 +103,14 @@ struct ZoningWindow : public Window { { switch(widget) { case ZTW_OUTER_DROPDOWN: - _zoning.outer = DropDownIndexToZoningEvaluationMode(index); + SetZoningMode(false, DropDownIndexToZoningEvaluationMode(index)); break; case ZTW_INNER_DROPDOWN: - _zoning.inner = DropDownIndexToZoningEvaluationMode(index); + SetZoningMode(true, DropDownIndexToZoningEvaluationMode(index)); break; } this->InvalidateData(); - MarkWholeScreenDirty(); } virtual void SetStringParameters(int widget) const