1
0
Fork 0

Codechange: Store animated tile state in map to improve performance.

This allows animated tiles to be added and removed without searching in the animated tile list, providing a performance improvement when there are lots of animated tiles.

Save game version is bumped so that animated tile state can be converted.
pull/12741/head
Peter Nelson 2024-11-27 19:33:32 +00:00 committed by Peter Nelson
parent 44de8d77bf
commit 6a07f28103
7 changed files with 110 additions and 28 deletions

View File

@ -740,6 +740,7 @@
</li>
</ul>
</li>
<li>m6: bits 1..0: animated tile state</li>
<li>m7 :
<ul>
<li>If <a href="#newhouses">newhouses</a> is activated
@ -1026,6 +1027,7 @@
</li>
<li>m6 bits 6..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint, road waypoint)</li>
<li>m6 bit 2: pbs reservation state for railway stations/waypoints</li>
<li>m6 bits 1..0: animated tile state</li>
<li>m7 bits 4..0: <a href="#OwnershipInfo">owner</a> of road (road stops)</li>
<li>m7: animation frame (railway stations/waypoints, airports)</li>
@ -1479,6 +1481,7 @@
</li>
<li>m6 bits 5..3: random triggers (NewGRF)</li>
<li>m6 bit 2: bit 8 of type (see m5)</li>
<li>m6 bits 1..0: animated tile state</li>
<li>m7: animation frame</li>
</ul>
</td>
@ -1651,6 +1654,7 @@
<li>m2: index into the array of objects, bits 0 to 15 (upper bits in m5)</li>
<li>m3: random bits</li>
<li>m5: index into the array of objects, bits 16 to 23 (lower bits in m2)</li>
<li>m6 bits 1..0: animated tile state</li>
<li>m7: animation counter</li>
</ul>
</td>

View File

@ -159,7 +159,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="used" title="House is complete/in construction (see m5)">1</span> <span class="used" title="House type (m4 + m3[6])">X</span><span class="free">O</span><span class="usable" title="Activated triggers (bits 2..4 don't have a meaning)">XXX</span><span class="used" title="Activated triggers (bits 2..4 don't have a meaning)">XX</span></td>
<td class="bits" rowspan=2><span class="used" title="House type (m4 + m3[6])">XXXX XXXX</span></td>
<td class="bits"><span class="used" title="Age in years, clamped at 255">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="abuse" title="Newhouses activated: periodic processing time remaining; if not, lift position for houses 04 and 05">XXXX XX</span><span class="free">OO</span></td>
<td class="bits" rowspan=2><span class="abuse" title="Newhouses activated: periodic processing time remaining; if not, lift position for houses 04 and 05">XXXX XX</span><span class="used" title="Animated tile state">XX</span></td>
<td class="bits" rowspan=2><span class="abuse" title="If newhouses active, m7 is the current animation frame">XXXX</span> <span class="abuse" title="If newhouses active, m7 is the current animantion frame; if not, lift behaviour for houses 04 and 05">XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOOO OOOO OOOO OOOO</span></td>
</tr>
@ -188,7 +188,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits" rowspan=2><span class="used" title="Random bits">XXXX</span> <span class="free">O</span><span class="used" title="May have pylons">X</span><span class="used" title="May have wires">X</span><span class="used" title="Tile is blocked">X</span></td>
<td class="bits" rowspan=2><span class="used" title="Custom station specifications ID">XXXX XXXX</span></td>
<td class="bits"><span class="used" title="Graphics index">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">O</span><span class="used" title="Station type">XXX X</span><span class="used" title="Reserved track">X</span><span class="free">OO</span></td>
<td class="bits" rowspan=2><span class="free">O</span><span class="used" title="Station type">XXX X</span><span class="used" title="Reserved track">X</span><span class="used" title="Animated tile state">XX</span></td>
<td class="bits" rowspan=2><span class="used" title="Animation frame">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOOO OOOO OO</span><span class="used" title="Railway type">XX XXXX</span></td>
</tr>
@ -201,7 +201,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="used" title="Owner of tram">XXXX</span> <span class="free">OOOO</span></td>
<td class="bits" rowspan=2><span class="free">OO</span><span class="used" title="Roadtype for road stop">XX XXXX</span></td>
<td class="bits" rowspan=2><span class="usable" title="Graphics index">OOOO O</span><span class="used" title="Graphics index: 00 (exit towards NE), 01 (exit towards SE), 02 (exit towards SW), 03 (exit towards NW), 04 (drive through X), 05 (drive through Y)">XXX</span></td>
<td class="bits" rowspan=6><span class="free">O</span><span class="used" title="Station type">XXX X</span><span class="free">OOO</span></td>
<td class="bits" rowspan=6><span class="free">O</span><span class="used" title="Station type">XXX X</span><span class="free">O</span><span class="used" title="Animated tile state">XX</span></td>
<td class="bits" rowspan=2><span class="free">OOO</span><span class="used" title="Owner of road">X XXXX</span></td>
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Tram type">XXXX XX</span> <span class="used" title="Custom road stops specifications ID">XXXXXX</span></td>
</tr>
@ -280,7 +280,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits" rowspan=2><span class="used" title="Random bits">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="used" title="Animation loop">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="used" title="Industry graphics ID (m5 + m6[2])">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OO</span><span class="used" title="Random triggers (NewGRF)">XXX</span> <span class="used" title="Industry graphics ID (m5 + m6[2])">X</span><span class="free">OO</span></td>
<td class="bits" rowspan=2><span class="free">OO</span><span class="used" title="Random triggers (NewGRF)">XXX</span> <span class="used" title="Industry graphics ID (m5 + m6[2])">X</span><span class="used" title="Animated tile state">XX</span></td>
<td class="bits" rowspan=2><span class="used" title="Animation frame">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOOO OOOO OOOO OOOO</span></td>
</tr>
@ -313,7 +313,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="used" title="Random bits">XXXX XXXX</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="pool" title="Object index on pool (m2 + m5)">XXXX XXXX</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OO</span><span class="used" title="Animated tile state">XX</span></td>
<td class="bits"><span class="used" title="Animation counter">XXXX XXXX</span></td>
<td class="bits" rowspan=1><span class="free">OOOO OOOO OOOO OOOO</span></td>
</tr>

View File

@ -50,6 +50,7 @@ add_files(
airport_gui.cpp
animated_tile.cpp
animated_tile_func.h
animated_tile_map.h
articulated_vehicles.cpp
articulated_vehicles.h
autocompletion.cpp

View File

@ -8,7 +8,7 @@
/** @file animated_tile.cpp Everything related to animated tiles. */
#include "stdafx.h"
#include "core/container_func.hpp"
#include "animated_tile_map.h"
#include "tile_cmd.h"
#include "viewport_func.h"
#include "framerate_type.h"
@ -19,16 +19,13 @@
std::vector<TileIndex> _animated_tiles;
/**
* Removes the given tile from the animated tile table.
* Stops animation on the given tile.
* @param tile the tile to remove
*/
void DeleteAnimatedTile(TileIndex tile)
{
auto to_remove = std::ranges::find(_animated_tiles, tile);
if (to_remove != _animated_tiles.end()) {
/* The order of the remaining elements must stay the same, otherwise the animation loop may miss a tile. */
_animated_tiles.erase(to_remove);
}
/* If the tile was animated, mark it for deletion from the tile list on the next animation loop. */
if (GetAnimatedTileState(tile) == AnimatedTileState::Animated) SetAnimatedTileState(tile, AnimatedTileState::Deleted);
}
/**
@ -39,7 +36,17 @@ void DeleteAnimatedTile(TileIndex tile)
void AddAnimatedTile(TileIndex tile, bool mark_dirty)
{
if (mark_dirty) MarkTileDirtyByTile(tile);
include(_animated_tiles, tile);
const AnimatedTileState state = GetAnimatedTileState(tile);
/* Tile is already animated so nothing needs to happen. */
if (state == AnimatedTileState::Animated) return;
/* Tile has no previous animation state, so add to the tile list. If the state is anything
* other than None then the tile will still be in the list and does not need to be added again. */
if (state == AnimatedTileState::None) _animated_tiles.push_back(tile);
SetAnimatedTileState(tile, AnimatedTileState::Animated);
}
/**
@ -47,22 +54,29 @@ void AddAnimatedTile(TileIndex tile, bool mark_dirty)
*/
void AnimateAnimatedTiles()
{
PerformanceAccumulator framerate(PFE_GL_LANDSCAPE);
PerformanceAccumulator landscape_framerate(PFE_GL_LANDSCAPE);
const TileIndex *ti = _animated_tiles.data();
while (ti < _animated_tiles.data() + _animated_tiles.size()) {
const TileIndex curr = *ti;
AnimateTile(curr);
/* During the AnimateTile call, DeleteAnimatedTile could have been called,
* deleting an element we've already processed and pushing the rest one
* slot to the left. We can detect this by checking whether the index
* in the current slot has changed - if it has, an element has been deleted,
* and we should process the current slot again instead of going forward.
* NOTE: this will still break if more than one animated tile is being
* deleted during the same AnimateTile call, but no code seems to
* be doing this anyway.
*/
if (*ti == curr) ++ti;
for (auto it = std::begin(_animated_tiles); it != std::end(_animated_tiles); /* nothing */) {
TileIndex &tile = *it;
if (GetAnimatedTileState(tile) != AnimatedTileState::Animated) {
/* Tile should not be animated any more, mark it as not animated and erase it from the list. */
SetAnimatedTileState(tile, AnimatedTileState::None);
/* Removing the last entry, no need to swap and continue. */
if (std::next(it) == std::end(_animated_tiles)) {
_animated_tiles.pop_back();
break;
}
/* Replace the current list entry with the back of the list to avoid moving elements. */
*it = _animated_tiles.back();
_animated_tiles.pop_back();
continue;
}
AnimateTile(tile);
++it;
}
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file animated_tile_map.h Maps accessors for animated tiles. */
#ifndef ANIMATED_TILE_MAP_H
#define ANIMATED_TILE_MAP_H
#include "core/bitmath_func.hpp"
#include "map_func.h"
/**
* Animation state of a possibly-animated tile.
*/
enum class AnimatedTileState : uint8_t {
None = 0, ///< Tile is not animated.
Deleted = 1, ///< Tile was animated but should be removed.
Animated = 3, ///< Tile is animated.
};
/**
* Get the animated state of a tile.
* @param t The tile.
* @returns true iff the tile is animated.
*/
inline AnimatedTileState GetAnimatedTileState(Tile t)
{
return static_cast<AnimatedTileState>(GB(t.m6(), 0, 2));
}
/**
* Set the animated state of a tile.
* @param t The tile.
*/
inline void SetAnimatedTileState(Tile t, AnimatedTileState state)
{
SB(t.m6(), 0, 2, to_underlying(state));
}
#endif /* ANIMATED_TILE_MAP_H */

View File

@ -43,6 +43,7 @@
#include "../game/game.hpp"
#include "../town.h"
#include "../economy_base.h"
#include "../animated_tile_map.h"
#include "../animated_tile_func.h"
#include "../subsidy_base.h"
#include "../subsidy_func.h"
@ -2225,6 +2226,23 @@ bool AfterLoadGame()
}
}
if (IsSavegameVersionBefore(SLV_ANIMATED_TILE_STATE_IN_MAP)) {
/* Animated tile state is stored in the map array, allowing
* quicker addition and deletion of animated tiles. */
extern std::vector<TileIndex> _animated_tiles;
for (auto t : Map::Iterate()) {
/* Ensure there is no spurious animated tile state. */
if (MayAnimateTile(t)) SetAnimatedTileState(t, AnimatedTileState::None);
}
/* Set animated flag for all valid animated tiles. */
for (const TileIndex &tile : _animated_tiles) {
if (tile != INVALID_TILE) SetAnimatedTileState(tile, AnimatedTileState::Animated);
}
}
if (IsSavegameVersionBefore(SLV_124) && !IsSavegameVersionBefore(SLV_1)) {
/* The train station tile area was added, but for really old (TTDPatch) it's already valid. */
for (Waypoint *wp : Waypoint::Iterate()) {

View File

@ -393,6 +393,7 @@ enum SaveLoadVersion : uint16_t {
SLV_NONFLOODING_WATER_TILES, ///< 345 PR#13013 Store water tile non-flooding state.
SLV_PATH_CACHE_FORMAT, ///< 346 PR#12345 Vehicle path cache format changed.
SLV_ANIMATED_TILE_STATE_IN_MAP, ///< 347 PR#13082 Animated tile state saved for improved performance.
SL_MAX_VERSION, ///< Highest possible saveload version
};