Improve performance of tunnel rendering in viewport map mode

This commit is contained in:
Jonathan G Rennison
2020-03-02 01:49:15 +00:00
parent 4108672cf6
commit 8956b1ab02
6 changed files with 102 additions and 46 deletions

View File

@@ -3774,6 +3774,7 @@ bool AfterLoadGame()
/* This needs to be done after conversion. */ /* This needs to be done after conversion. */
RebuildViewportKdtree(); RebuildViewportKdtree();
ViewportMapBuildTunnelCache();
/* Road stops is 'only' updating some caches */ /* Road stops is 'only' updating some caches */
AfterLoadRoadStops(); AfterLoadRoadStops();

View File

@@ -1244,6 +1244,13 @@ static bool CheckYapfRailSignalPenalties(int32)
return true; return true;
} }
static bool ViewportMapShowTunnelModeChanged(int32 p1)
{
extern void ViewportMapBuildTunnelCache();
ViewportMapBuildTunnelCache();
return RedrawScreen(p1);
}
/** Checks if any settings are set to incorrect values, and sets them to correct values in that case. */ /** Checks if any settings are set to incorrect values, and sets them to correct values in that case. */
static void ValidateSettings() static void ValidateSettings()
{ {

View File

@@ -48,6 +48,7 @@ static bool DayLengthChanged(int32 p1);
static bool SimulatedWormholeSignalsChanged(int32 p1); static bool SimulatedWormholeSignalsChanged(int32 p1);
static bool EnableSingleVehSharedOrderGuiChanged(int32 p1); static bool EnableSingleVehSharedOrderGuiChanged(int32 p1);
static bool CheckYapfRailSignalPenalties(int32 p1); static bool CheckYapfRailSignalPenalties(int32 p1);
static bool ViewportMapShowTunnelModeChanged(int32 p1);
static bool UpdateClientName(int32 p1); static bool UpdateClientName(int32 p1);
static bool UpdateServerPassword(int32 p1); static bool UpdateServerPassword(int32 p1);
@@ -4065,7 +4066,7 @@ var = gui.show_tunnels_on_map
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
def = true def = true
str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_TUNNELS str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_TUNNELS
proc = RedrawScreen proc = ViewportMapShowTunnelModeChanged
[SDTC_VAR] [SDTC_VAR]
var = gui.show_vehicle_route var = gui.show_vehicle_route

View File

@@ -999,7 +999,9 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
if(start_tile > end_tile) Swap(tn, ts); if(start_tile > end_tile) Swap(tn, ts);
if (!Tunnel::CanAllocateItem()) return_cmd_error(STR_ERROR_TUNNEL_TOO_MANY); if (!Tunnel::CanAllocateItem()) return_cmd_error(STR_ERROR_TUNNEL_TOO_MANY);
const Tunnel *t = new Tunnel(tn, ts, TileHeight(tn), is_chunnel); const int height = TileHeight(tn);
const Tunnel *t = new Tunnel(tn, ts, height, is_chunnel);
ViewportMapStoreTunnel(tn, ts, height, true);
if (transport_type == TRANSPORT_RAIL) { if (transport_type == TRANSPORT_RAIL) {
if (!IsTunnelTile(start_tile) && c != nullptr) c->infrastructure.rail[railtype] += num_pieces; if (!IsTunnelTile(start_tile) && c != nullptr) c->infrastructure.rail[railtype] += num_pieces;
@@ -1080,6 +1082,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
CommandCost ret = CheckAllowRemoveTunnelBridge(tile); CommandCost ret = CheckAllowRemoveTunnelBridge(tile);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
const Axis axis = DiagDirToAxis(GetTunnelBridgeDirection(tile));
TileIndex endtile = GetOtherTunnelEnd(tile); TileIndex endtile = GetOtherTunnelEnd(tile);
ret = TunnelBridgeIsFree(tile, endtile); ret = TunnelBridgeIsFree(tile, endtile);
@@ -1153,8 +1156,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
DoClearSquare(tile); DoClearSquare(tile);
DoClearSquare(endtile); DoClearSquare(endtile);
} }
ViewportMapInvalidateTunnelCacheByTile(tile); ViewportMapInvalidateTunnelCacheByTile(tile < endtile ? tile : endtile, axis);
ViewportMapInvalidateTunnelCacheByTile(endtile);
} }
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_TUNNEL] * len * (is_chunnel ? 2 : 1)); return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_TUNNEL] * len * (is_chunnel ? 2 : 1));
} }

View File

@@ -206,6 +206,15 @@ struct PolylineInfo {
uint second_len; ///< size of the second segment - number of track pieces. uint second_len; ///< size of the second segment - number of track pieces.
}; };
struct TunnelToMap {
TunnelBridgeToMap tb;
int y_intercept;
uint8 tunnel_z;
};
struct TunnelToMapStorage {
std::vector<TunnelToMap> tunnels;
};
struct BridgeSetXComparator { struct BridgeSetXComparator {
bool operator() (const TileIndex a, const TileIndex b) const bool operator() (const TileIndex a, const TileIndex b) const
{ {
@@ -229,8 +238,8 @@ struct ViewportDrawer {
ParentSpriteToDrawVector parent_sprites_to_draw; ParentSpriteToDrawVector parent_sprites_to_draw;
ParentSpriteToSortVector parent_sprites_to_sort; ///< Parent sprite pointer array used for sorting ParentSpriteToSortVector parent_sprites_to_sort; ///< Parent sprite pointer array used for sorting
ChildScreenSpriteToDrawVector child_screen_sprites_to_draw; ChildScreenSpriteToDrawVector child_screen_sprites_to_draw;
TunnelBridgeToMapVector tunnel_to_map; TunnelToMapStorage tunnel_to_map_x;
btree::btree_set<TileIndex> tunnel_tiles; TunnelToMapStorage tunnel_to_map_y;
btree::btree_map<TileIndex, TileIndex, BridgeSetXComparator> bridge_to_map_x; btree::btree_map<TileIndex, TileIndex, BridgeSetXComparator> bridge_to_map_x;
btree::btree_map<TileIndex, TileIndex, BridgeSetYComparator> bridge_to_map_y; btree::btree_map<TileIndex, TileIndex, BridgeSetYComparator> bridge_to_map_y;
@@ -1941,7 +1950,7 @@ static void ViewportMapStoreBridge(const ViewPort * const vp, const TileIndex ti
} }
} }
static void ViewportMapStoreTunnel(const ViewPort * const vp, const TileIndex tile) void ViewportMapStoreTunnel(const TileIndex tile, const TileIndex tile_south, const int tunnel_z, const bool insert_sorted)
{ {
extern LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1]; extern LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1];
extern uint _company_to_list_pos[MAX_COMPANIES]; extern uint _company_to_list_pos[MAX_COMPANIES];
@@ -1951,45 +1960,68 @@ static void ViewportMapStoreTunnel(const ViewPort * const vp, const TileIndex ti
const Owner o = GetTileOwner(tile); const Owner o = GetTileOwner(tile);
if (o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) return; if (o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) return;
/* Check if already stored */ const Axis axis = (TileX(tile) == TileX(tile_south)) ? AXIS_Y : AXIS_X;
if (_vd.tunnel_tiles.count(tile)) return; const Point viewport_pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, tunnel_z);
int y_intercept;
/* It's a new one, add it to the list */ if (axis == AXIS_X) {
_vd.tunnel_to_map.emplace_back(); /* NE to SW */
TunnelBridgeToMap &tbtm = _vd.tunnel_to_map.back(); y_intercept = viewport_pt.y + (viewport_pt.x / 2);
TileIndex other_end = GetOtherTunnelBridgeEnd(tile); } else {
_vd.tunnel_tiles.insert(tile); /* NW to SE */
_vd.tunnel_tiles.insert(other_end); y_intercept = viewport_pt.y - (viewport_pt.x / 2);
}
TunnelToMapStorage &storage = (axis == AXIS_X) ? _vd.tunnel_to_map_x : _vd.tunnel_to_map_y;
TunnelToMap *tbtm;
if (insert_sorted) {
auto iter = std::upper_bound(storage.tunnels.begin(), storage.tunnels.end(), y_intercept, [](int a, const TunnelToMap &b) -> bool {
return a < b.y_intercept;
});
tbtm = &(*(storage.tunnels.emplace(iter)));
} else {
storage.tunnels.emplace_back();
tbtm = &(storage.tunnels.back());
}
/* ensure deterministic ordering, to avoid render flicker */ /* ensure deterministic ordering, to avoid render flicker */
if (other_end > tile) { tbtm->tb.from_tile = tile;
tbtm.from_tile = other_end; tbtm->tb.to_tile = tile_south;
tbtm.to_tile = tile; tbtm->y_intercept = y_intercept;
} else { tbtm->tunnel_z = tunnel_z;
tbtm.from_tile = tile;
tbtm.to_tile = other_end;
}
} }
void ViewportMapClearTunnelCache() void ViewportMapClearTunnelCache()
{ {
_vd.tunnel_to_map.clear(); _vd.tunnel_to_map_x.tunnels.clear();
_vd.tunnel_tiles.clear(); _vd.tunnel_to_map_y.tunnels.clear();
} }
void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile) void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile, const Axis axis)
{ {
if (!_vd.tunnel_tiles.count(tile)) return; if (!_settings_client.gui.show_tunnels_on_map) return;
TunnelBridgeToMapVector * const tbtmv = &_vd.tunnel_to_map; std::vector<TunnelToMap> &tbtmv = (axis == AXIS_X) ? _vd.tunnel_to_map_x.tunnels : _vd.tunnel_to_map_y.tunnels;
for (auto tbtm = tbtmv->begin(); tbtm != tbtmv->end(); tbtm++) { for (auto tbtm = tbtmv.begin(); tbtm != tbtmv.end(); tbtm++) {
if (tbtm->from_tile == tile || tbtm->to_tile == tile) { if (tbtm->tb.from_tile == tile) {
*tbtm = tbtmv->back(); tbtmv.erase(tbtm);
tbtmv->pop_back();
return; return;
} }
} }
} }
void ViewportMapBuildTunnelCache()
{
ViewportMapClearTunnelCache();
if (_settings_client.gui.show_tunnels_on_map) {
for (Tunnel *tunnel : Tunnel::Iterate()) {
ViewportMapStoreTunnel(tunnel->tile_n, tunnel->tile_s, tunnel->height, false);
}
auto sorter = [](const TunnelToMap &a, const TunnelToMap &b) -> bool {
return a.y_intercept < b.y_intercept;
};
std::sort(_vd.tunnel_to_map_x.tunnels.begin(), _vd.tunnel_to_map_x.tunnels.end(), sorter);
std::sort(_vd.tunnel_to_map_y.tunnels.begin(), _vd.tunnel_to_map_y.tunnels.end(), sorter);
}
}
/** /**
* Draw/colour the blocks that have been redrawn. * Draw/colour the blocks that have been redrawn.
*/ */
@@ -2592,9 +2624,7 @@ static inline TileIndex ViewportMapGetMostSignificantTileType(const ViewPort * c
*tile_type = ttype; *tile_type = ttype;
if (IsBridgeAbove(from_tile)) ViewportMapStoreBridgeAboveTile(vp, from_tile); if (IsBridgeAbove(from_tile)) ViewportMapStoreBridgeAboveTile(vp, from_tile);
} else { } else {
if (IsTunnel(from_tile)) { if (IsBridge(from_tile)) {
ViewportMapStoreTunnel(vp, from_tile);
} else {
ViewportMapStoreBridge(vp, from_tile); ViewportMapStoreBridge(vp, from_tile);
} }
switch (GetTunnelBridgeTransportType(from_tile)) { switch (GetTunnelBridgeTransportType(from_tile)) {
@@ -2629,9 +2659,7 @@ static inline TileIndex ViewportMapGetMostSignificantTileType(const ViewPort * c
/* Store bridges and tunnels. */ /* Store bridges and tunnels. */
*tile_type = GetTileType(result); *tile_type = GetTileType(result);
if (*tile_type == MP_TUNNELBRIDGE) { if (*tile_type == MP_TUNNELBRIDGE) {
if (IsTunnel(result)) { if (IsBridge(result)) {
ViewportMapStoreTunnel(vp, result);
} else {
ViewportMapStoreBridge(vp, result); ViewportMapStoreBridge(vp, result);
} }
switch (GetTunnelBridgeTransportType(result)) { switch (GetTunnelBridgeTransportType(result)) {
@@ -2838,12 +2866,15 @@ void ViewportMapDraw(const ViewPort * const vp)
b += incr_b; b += incr_b;
} while (++j < h); } while (++j < h);
/* Render tunnels */ auto draw_tunnels = [&](const int y_intercept_min, const int y_intercept_max, const TunnelToMapStorage &storage) {
if (_settings_client.gui.show_tunnels_on_map && _vd.tunnel_to_map.size() != 0) { auto iter = std::lower_bound(storage.tunnels.begin(), storage.tunnels.end(), y_intercept_min, [](const TunnelToMap &a, int b) -> bool {
for (const TunnelBridgeToMap &tbtm : _vd.tunnel_to_map) { // For each tunnel return a.y_intercept < b;
const int tunnel_z = GetTileZ(tbtm.from_tile) * TILE_HEIGHT; });
const Point pt_from = RemapCoords(TileX(tbtm.from_tile) * TILE_SIZE, TileY(tbtm.from_tile) * TILE_SIZE, tunnel_z); for (; iter != storage.tunnels.end() && iter->y_intercept <= y_intercept_max; ++iter) {
const Point pt_to = RemapCoords(TileX(tbtm.to_tile) * TILE_SIZE, TileY(tbtm.to_tile) * TILE_SIZE, tunnel_z); const TunnelToMap &ttm = *iter;
const int tunnel_z = ttm.tunnel_z * TILE_HEIGHT;
const Point pt_from = RemapCoords(TileX(ttm.tb.from_tile) * TILE_SIZE, TileY(ttm.tb.from_tile) * TILE_SIZE, tunnel_z);
const Point pt_to = RemapCoords(TileX(ttm.tb.to_tile) * TILE_SIZE, TileY(ttm.tb.to_tile) * TILE_SIZE, tunnel_z);
/* check if tunnel is wholly outside redrawing area */ /* check if tunnel is wholly outside redrawing area */
const int x_from = UnScaleByZoomLower(pt_from.x - _vd.dpi.left, _vd.dpi.zoom); const int x_from = UnScaleByZoomLower(pt_from.x - _vd.dpi.left, _vd.dpi.zoom);
@@ -2853,8 +2884,20 @@ void ViewportMapDraw(const ViewPort * const vp)
const int y_to = UnScaleByZoomLower(pt_to.y - _vd.dpi.top, _vd.dpi.zoom); const int y_to = UnScaleByZoomLower(pt_to.y - _vd.dpi.top, _vd.dpi.zoom);
if ((y_from < 0 && y_to < 0) || (y_from > h && y_to > h)) continue; if ((y_from < 0 && y_to < 0) || (y_from > h && y_to > h)) continue;
ViewportMapDrawBridgeTunnel(vp, &tbtm, tunnel_z, true, w, h, blitter); ViewportMapDrawBridgeTunnel(vp, &ttm.tb, tunnel_z, true, w, h, blitter);
} }
};
/* Render tunnels */
if (_settings_client.gui.show_tunnels_on_map && _vd.tunnel_to_map_x.tunnels.size() != 0) {
const int y_intercept_min = _vd.dpi.top + (_vd.dpi.left / 2);
const int y_intercept_max = _vd.dpi.top + _vd.dpi.height + ((_vd.dpi.left + _vd.dpi.width) / 2);
draw_tunnels(y_intercept_min, y_intercept_max, _vd.tunnel_to_map_x);
}
if (_settings_client.gui.show_tunnels_on_map && _vd.tunnel_to_map_y.tunnels.size() != 0) {
const int y_intercept_min = _vd.dpi.top - ((_vd.dpi.left + _vd.dpi.width) / 2);
const int y_intercept_max = _vd.dpi.top + _vd.dpi.height - (_vd.dpi.left / 2);
draw_tunnels(y_intercept_min, y_intercept_max, _vd.tunnel_to_map_y);
} }
/* Render bridges */ /* Render bridges */

View File

@@ -112,8 +112,10 @@ Point GetViewportStationMiddle(const ViewPort *vp, const Station *st);
void ShowTooltipForTile(Window *w, const TileIndex tile); void ShowTooltipForTile(Window *w, const TileIndex tile);
void ViewportMapStoreTunnel(const TileIndex tile, const TileIndex tile_south, const int tunnel_z, const bool insert_sorted);
void ViewportMapClearTunnelCache(); void ViewportMapClearTunnelCache();
void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile); void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile, const Axis axis);
void ViewportMapBuildTunnelCache();
void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal); void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal);
void DrawSelectionSprite(SpriteID image, PaletteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part, const SubSprite *sub = nullptr); void DrawSelectionSprite(SpriteID image, PaletteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part, const SubSprite *sub = nullptr);