Improve performance of tunnel rendering in viewport map mode

pull/135/head
Jonathan G Rennison 4 years ago
parent 4108672cf6
commit 8956b1ab02

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

@ -1244,6 +1244,13 @@ static bool CheckYapfRailSignalPenalties(int32)
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. */
static void ValidateSettings()
{

@ -48,6 +48,7 @@ static bool DayLengthChanged(int32 p1);
static bool SimulatedWormholeSignalsChanged(int32 p1);
static bool EnableSingleVehSharedOrderGuiChanged(int32 p1);
static bool CheckYapfRailSignalPenalties(int32 p1);
static bool ViewportMapShowTunnelModeChanged(int32 p1);
static bool UpdateClientName(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
def = true
str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_TUNNELS
proc = RedrawScreen
proc = ViewportMapShowTunnelModeChanged
[SDTC_VAR]
var = gui.show_vehicle_route

@ -999,7 +999,9 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
if(start_tile > end_tile) Swap(tn, ts);
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 (!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);
if (ret.Failed()) return ret;
const Axis axis = DiagDirToAxis(GetTunnelBridgeDirection(tile));
TileIndex endtile = GetOtherTunnelEnd(tile);
ret = TunnelBridgeIsFree(tile, endtile);
@ -1153,8 +1156,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
DoClearSquare(tile);
DoClearSquare(endtile);
}
ViewportMapInvalidateTunnelCacheByTile(tile);
ViewportMapInvalidateTunnelCacheByTile(endtile);
ViewportMapInvalidateTunnelCacheByTile(tile < endtile ? tile : endtile, axis);
}
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_TUNNEL] * len * (is_chunnel ? 2 : 1));
}

@ -206,6 +206,15 @@ struct PolylineInfo {
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 {
bool operator() (const TileIndex a, const TileIndex b) const
{
@ -229,8 +238,8 @@ struct ViewportDrawer {
ParentSpriteToDrawVector parent_sprites_to_draw;
ParentSpriteToSortVector parent_sprites_to_sort; ///< Parent sprite pointer array used for sorting
ChildScreenSpriteToDrawVector child_screen_sprites_to_draw;
TunnelBridgeToMapVector tunnel_to_map;
btree::btree_set<TileIndex> tunnel_tiles;
TunnelToMapStorage tunnel_to_map_x;
TunnelToMapStorage tunnel_to_map_y;
btree::btree_map<TileIndex, TileIndex, BridgeSetXComparator> bridge_to_map_x;
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 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);
if (o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) return;
/* Check if already stored */
if (_vd.tunnel_tiles.count(tile)) return;
/* It's a new one, add it to the list */
_vd.tunnel_to_map.emplace_back();
TunnelBridgeToMap &tbtm = _vd.tunnel_to_map.back();
TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
_vd.tunnel_tiles.insert(tile);
_vd.tunnel_tiles.insert(other_end);
/* ensure deterministic ordering, to avoid render flicker */
if (other_end > tile) {
tbtm.from_tile = other_end;
tbtm.to_tile = tile;
const Axis axis = (TileX(tile) == TileX(tile_south)) ? AXIS_Y : AXIS_X;
const Point viewport_pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, tunnel_z);
int y_intercept;
if (axis == AXIS_X) {
/* NE to SW */
y_intercept = viewport_pt.y + (viewport_pt.x / 2);
} else {
tbtm.from_tile = tile;
tbtm.to_tile = other_end;
/* NW to SE */
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 */
tbtm->tb.from_tile = tile;
tbtm->tb.to_tile = tile_south;
tbtm->y_intercept = y_intercept;
tbtm->tunnel_z = tunnel_z;
}
void ViewportMapClearTunnelCache()
{
_vd.tunnel_to_map.clear();
_vd.tunnel_tiles.clear();
_vd.tunnel_to_map_x.tunnels.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;
TunnelBridgeToMapVector * const tbtmv = &_vd.tunnel_to_map;
for (auto tbtm = tbtmv->begin(); tbtm != tbtmv->end(); tbtm++) {
if (tbtm->from_tile == tile || tbtm->to_tile == tile) {
*tbtm = tbtmv->back();
tbtmv->pop_back();
if (!_settings_client.gui.show_tunnels_on_map) return;
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++) {
if (tbtm->tb.from_tile == tile) {
tbtmv.erase(tbtm);
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.
*/
@ -2592,9 +2624,7 @@ static inline TileIndex ViewportMapGetMostSignificantTileType(const ViewPort * c
*tile_type = ttype;
if (IsBridgeAbove(from_tile)) ViewportMapStoreBridgeAboveTile(vp, from_tile);
} else {
if (IsTunnel(from_tile)) {
ViewportMapStoreTunnel(vp, from_tile);
} else {
if (IsBridge(from_tile)) {
ViewportMapStoreBridge(vp, from_tile);
}
switch (GetTunnelBridgeTransportType(from_tile)) {
@ -2629,9 +2659,7 @@ static inline TileIndex ViewportMapGetMostSignificantTileType(const ViewPort * c
/* Store bridges and tunnels. */
*tile_type = GetTileType(result);
if (*tile_type == MP_TUNNELBRIDGE) {
if (IsTunnel(result)) {
ViewportMapStoreTunnel(vp, result);
} else {
if (IsBridge(result)) {
ViewportMapStoreBridge(vp, result);
}
switch (GetTunnelBridgeTransportType(result)) {
@ -2838,12 +2866,15 @@ void ViewportMapDraw(const ViewPort * const vp)
b += incr_b;
} while (++j < h);
/* Render tunnels */
if (_settings_client.gui.show_tunnels_on_map && _vd.tunnel_to_map.size() != 0) {
for (const TunnelBridgeToMap &tbtm : _vd.tunnel_to_map) { // For each tunnel
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);
const Point pt_to = RemapCoords(TileX(tbtm.to_tile) * TILE_SIZE, TileY(tbtm.to_tile) * TILE_SIZE, tunnel_z);
auto draw_tunnels = [&](const int y_intercept_min, const int y_intercept_max, const TunnelToMapStorage &storage) {
auto iter = std::lower_bound(storage.tunnels.begin(), storage.tunnels.end(), y_intercept_min, [](const TunnelToMap &a, int b) -> bool {
return a.y_intercept < b;
});
for (; iter != storage.tunnels.end() && iter->y_intercept <= y_intercept_max; ++iter) {
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 */
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);
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 */

@ -112,8 +112,10 @@ Point GetViewportStationMiddle(const ViewPort *vp, const Station *st);
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 ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile);
void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile, const Axis axis);
void ViewportMapBuildTunnelCache();
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);

Loading…
Cancel
Save