mirror of https://github.com/OpenTTD/OpenTTD
Codechange: Replace path cache queues with vectors.
Ship and RoadVehicle path caches use a std::deque, which is quite memory hungry, especially for RoadVehicle which has two. std::deque was used to be able to push/pop from either end. Change to use a single std::vector each, which is now push/popped from the back.pull/13096/head
parent
d4f8453c22
commit
c39810ff6a
|
@ -401,8 +401,7 @@ public:
|
|||
while (pNode->parent != nullptr) {
|
||||
steps--;
|
||||
if (pNode->GetIsChoice() && steps < YAPF_ROADVEH_PATH_CACHE_SEGMENTS) {
|
||||
path_cache.td.push_front(pNode->GetTrackdir());
|
||||
path_cache.tile.push_front(pNode->GetTile());
|
||||
path_cache.emplace_back(pNode->GetTrackdir(), pNode->GetTile());
|
||||
}
|
||||
pNode = pNode->parent;
|
||||
}
|
||||
|
@ -410,13 +409,9 @@ public:
|
|||
Node &best_next_node = *pNode;
|
||||
assert(best_next_node.GetTile() == tile);
|
||||
next_trackdir = best_next_node.GetTrackdir();
|
||||
/* remove last element for the special case when tile == dest_tile */
|
||||
if (path_found && !path_cache.empty() && tile == v->dest_tile) {
|
||||
path_cache.td.pop_back();
|
||||
path_cache.tile.pop_back();
|
||||
}
|
||||
|
||||
/* Check if target is a station, and cached path ends within 8 tiles of the dest tile */
|
||||
/* Check if target is a station, and cached path leads to within YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT
|
||||
* tiles of the dest tile */
|
||||
const Station *st = Yapf().GetDestinationStation();
|
||||
if (st) {
|
||||
const RoadStop *stop = st->GetPrimaryRoadStop(v);
|
||||
|
@ -425,10 +420,10 @@ public:
|
|||
* trim end of path cache within a number of tiles of road stop tile area */
|
||||
TileArea non_cached_area = v->IsBus() ? st->bus_station : st->truck_station;
|
||||
non_cached_area.Expand(YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT);
|
||||
while (!path_cache.empty() && non_cached_area.Contains(path_cache.tile.back())) {
|
||||
path_cache.td.pop_back();
|
||||
path_cache.tile.pop_back();
|
||||
}
|
||||
|
||||
/* Find the first tile not contained by the non-cachable area, and remove from the cache. */
|
||||
auto it = std::find_if(std::begin(path_cache), std::end(path_cache), [&non_cached_area](const auto &pc) { return !non_cached_area.Contains(pc.tile); });
|
||||
path_cache.erase(std::begin(path_cache), it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,8 +204,11 @@ public:
|
|||
|
||||
if (path_cache.empty()) return INVALID_TRACKDIR;
|
||||
|
||||
const Trackdir result = path_cache.front();
|
||||
path_cache.pop_front();
|
||||
/* Reverse the path so we can take from the end. */
|
||||
std::reverse(std::begin(path_cache), std::end(path_cache));
|
||||
|
||||
const Trackdir result = path_cache.back().trackdir;
|
||||
path_cache.pop_back();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -258,7 +261,7 @@ public:
|
|||
/* The cached path must always lead to a region patch that's on the high level path.
|
||||
* This is what can happen when that's not the case https://github.com/OpenTTD/OpenTTD/issues/12176. */
|
||||
if (add_full_path || !node_water_patch_on_high_level_path || node_water_patch == start_water_patch) {
|
||||
path_cache.push_front(node->GetTrackdir());
|
||||
path_cache.push_back(node->GetTrackdir());
|
||||
} else {
|
||||
path_cache.clear();
|
||||
}
|
||||
|
@ -278,8 +281,8 @@ public:
|
|||
if (path_cache.empty()) return CreateRandomPath(v, path_cache, 1);
|
||||
|
||||
/* Take out the last trackdir as the result. */
|
||||
const Trackdir result = path_cache.front();
|
||||
path_cache.pop_front();
|
||||
const Trackdir result = path_cache.back().trackdir;
|
||||
path_cache.pop_back();
|
||||
|
||||
/* Clear path cache when in final water region patch. This is to allow ships to spread over different docking tiles dynamically. */
|
||||
if (start_water_patch == end_water_patch) path_cache.clear();
|
||||
|
|
|
@ -81,25 +81,17 @@ static const uint8_t RV_OVERTAKE_TIMEOUT = 35;
|
|||
void RoadVehUpdateCache(RoadVehicle *v, bool same_length = false);
|
||||
void GetRoadVehSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type);
|
||||
|
||||
struct RoadVehPathCache {
|
||||
std::deque<Trackdir> td;
|
||||
std::deque<TileIndex> tile;
|
||||
/** Element of the RoadVehPathCache. */
|
||||
struct RoadVehPathElement {
|
||||
Trackdir trackdir; ///< Trackdir for this element.
|
||||
TileIndex tile; ///< Tile for this element.
|
||||
|
||||
inline bool empty() const { return this->td.empty(); }
|
||||
|
||||
inline size_t size() const
|
||||
{
|
||||
assert(this->td.size() == this->tile.size());
|
||||
return this->td.size();
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
this->td.clear();
|
||||
this->tile.clear();
|
||||
}
|
||||
constexpr RoadVehPathElement() : trackdir(INVALID_TRACKDIR), tile(INVALID_TILE) {}
|
||||
constexpr RoadVehPathElement(Trackdir trackdir, TileIndex tile) : trackdir(trackdir), tile(tile) {}
|
||||
};
|
||||
|
||||
using RoadVehPathCache = std::vector<RoadVehPathElement>;
|
||||
|
||||
/**
|
||||
* Buses, trucks and trams belong to this class.
|
||||
*/
|
||||
|
|
|
@ -957,7 +957,7 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
|
|||
|
||||
/* Only one track to choose between? */
|
||||
if (KillFirstBit(trackdirs) == TRACKDIR_BIT_NONE) {
|
||||
if (!v->path.empty() && v->path.tile.front() == tile) {
|
||||
if (!v->path.empty() && v->path.back().tile == tile) {
|
||||
/* Vehicle expected a choice here, invalidate its path. */
|
||||
v->path.clear();
|
||||
}
|
||||
|
@ -966,15 +966,14 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
|
|||
|
||||
/* Attempt to follow cached path. */
|
||||
if (!v->path.empty()) {
|
||||
if (v->path.tile.front() != tile) {
|
||||
if (v->path.back().tile != tile) {
|
||||
/* Vehicle didn't expect a choice here, invalidate its path. */
|
||||
v->path.clear();
|
||||
} else {
|
||||
Trackdir trackdir = v->path.td.front();
|
||||
Trackdir trackdir = v->path.back().trackdir;
|
||||
|
||||
if (HasBit(trackdirs, trackdir)) {
|
||||
v->path.td.pop_front();
|
||||
v->path.tile.pop_front();
|
||||
v->path.pop_back();
|
||||
return_track(trackdir);
|
||||
}
|
||||
|
||||
|
@ -1281,8 +1280,7 @@ again:
|
|||
if (u != nullptr) {
|
||||
v->cur_speed = u->First()->cur_speed;
|
||||
/* We might be blocked, prevent pathfinding rerun as we already know where we are heading to. */
|
||||
v->path.tile.push_front(tile);
|
||||
v->path.td.push_front(dir);
|
||||
v->path.emplace_back(dir, tile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1397,8 +1395,7 @@ again:
|
|||
if (u != nullptr) {
|
||||
v->cur_speed = u->First()->cur_speed;
|
||||
/* We might be blocked, prevent pathfinding rerun as we already know where we are heading to. */
|
||||
v->path.tile.push_front(v->tile);
|
||||
v->path.td.push_front(dir);
|
||||
v->path.emplace_back(dir, v->tile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,7 +390,9 @@ enum SaveLoadVersion : uint16_t {
|
|||
SLV_WATER_TILE_TYPE, ///< 342 PR#13030 Simplify water tile type.
|
||||
SLV_PRODUCTION_HISTORY, ///< 343 PR#10541 Industry production history.
|
||||
SLV_ROAD_TYPE_LABEL_MAP, ///< 344 PR#13021 Add road type label map to allow upgrade/conversion of road types.
|
||||
|
||||
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.
|
||||
|
||||
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||
};
|
||||
|
@ -946,6 +948,16 @@ inline constexpr bool SlCheckVarSize(SaveLoadType cmd, VarType type, size_t leng
|
|||
*/
|
||||
#define SLE_CONDREFLIST(base, variable, type, from, to) SLE_GENERAL(SL_REFLIST, base, variable, type, 0, from, to, 0)
|
||||
|
||||
/**
|
||||
* Storage of a vector of #SL_VAR elements in some savegame versions.
|
||||
* @param base Name of the class or struct containing the list.
|
||||
* @param variable Name of the variable in the class or struct referenced by \a base.
|
||||
* @param type Storage of the data in memory and in the savegame.
|
||||
* @param from First savegame version that has the list.
|
||||
* @param to Last savegame version that has the list.
|
||||
*/
|
||||
#define SLE_CONDVECTOR(base, variable, type, from, to) SLE_GENERAL(SL_VECTOR, base, variable, type, 0, from, to, 0)
|
||||
|
||||
/**
|
||||
* Storage of a deque of #SL_VAR elements in some savegame versions.
|
||||
* @param base Name of the class or struct containing the list.
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "saveload.h"
|
||||
#include "compat/vehicle_sl_compat.h"
|
||||
|
||||
#include "../debug.h"
|
||||
#include "../vehicle_func.h"
|
||||
#include "../train.h"
|
||||
#include "../roadveh.h"
|
||||
|
@ -821,8 +822,23 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class SlVehicleRoadVehPath : public VectorSaveLoadHandler<SlVehicleRoadVehPath, RoadVehicle, RoadVehPathElement> {
|
||||
public:
|
||||
inline static const SaveLoad description[] = {
|
||||
SLE_VAR(RoadVehPathElement, trackdir, SLE_UINT8),
|
||||
SLE_VAR(RoadVehPathElement, tile, SLE_UINT32),
|
||||
};
|
||||
inline const static SaveLoadCompatTable compat_description = {};
|
||||
|
||||
std::vector<RoadVehPathElement> &GetVector(RoadVehicle *rv) const override { return rv->path; }
|
||||
};
|
||||
|
||||
class SlVehicleRoadVeh : public DefaultSaveLoadHandler<SlVehicleRoadVeh, Vehicle> {
|
||||
public:
|
||||
/* RoadVehicle path is stored in std::pair which cannot be directly saved. */
|
||||
static inline std::vector<Trackdir> rv_path_td;
|
||||
static inline std::vector<TileIndex> rv_path_tile;
|
||||
|
||||
inline static const SaveLoad description[] = {
|
||||
SLEG_STRUCT("common", SlVehicleCommon),
|
||||
SLE_VAR(RoadVehicle, state, SLE_UINT8),
|
||||
|
@ -832,12 +848,32 @@ public:
|
|||
SLE_VAR(RoadVehicle, overtaking_ctr, SLE_UINT8),
|
||||
SLE_VAR(RoadVehicle, crashed_ctr, SLE_UINT16),
|
||||
SLE_VAR(RoadVehicle, reverse_ctr, SLE_UINT8),
|
||||
SLE_CONDDEQUE(RoadVehicle, path.td, SLE_UINT8, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION),
|
||||
SLE_CONDDEQUE(RoadVehicle, path.tile, SLE_UINT32, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION),
|
||||
SLEG_CONDVECTOR("path.td", rv_path_td, SLE_UINT8, SLV_ROADVEH_PATH_CACHE, SLV_PATH_CACHE_FORMAT),
|
||||
SLEG_CONDVECTOR("path.tile", rv_path_tile, SLE_UINT32, SLV_ROADVEH_PATH_CACHE, SLV_PATH_CACHE_FORMAT),
|
||||
SLEG_CONDSTRUCTLIST("path", SlVehicleRoadVehPath, SLV_PATH_CACHE_FORMAT, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(RoadVehicle, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION),
|
||||
};
|
||||
inline const static SaveLoadCompatTable compat_description = _vehicle_roadveh_sl_compat;
|
||||
|
||||
static void ConvertPathCache(RoadVehicle &rv)
|
||||
{
|
||||
/* The two vectors should be the same size, but if not we can just ignore the cache and not cause more issues. */
|
||||
if (rv_path_td.size() != rv_path_tile.size()) {
|
||||
Debug(sl, 1, "Found RoadVehicle {} with invalid path cache, ignoring.", rv.index);
|
||||
return;
|
||||
}
|
||||
size_t n = std::min(rv_path_td.size(), rv_path_tile.size());
|
||||
if (n == 0) return;
|
||||
|
||||
rv.path.reserve(n);
|
||||
for (size_t c = 0; c < n; ++c) {
|
||||
rv.path.emplace_back(rv_path_td[c], rv_path_tile[c]);
|
||||
}
|
||||
|
||||
/* Path cache is now taken from back instead of front, so needs reversing. */
|
||||
std::reverse(std::begin(rv.path), std::end(rv.path));
|
||||
}
|
||||
|
||||
void Save(Vehicle *v) const override
|
||||
{
|
||||
if (v->type != VEH_ROAD) return;
|
||||
|
@ -848,6 +884,9 @@ public:
|
|||
{
|
||||
if (v->type != VEH_ROAD) return;
|
||||
SlObject(v, this->GetLoadDescription());
|
||||
if (!IsSavegameVersionBefore(SLV_ROADVEH_PATH_CACHE) && IsSavegameVersionBefore(SLV_PATH_CACHE_FORMAT)) {
|
||||
ConvertPathCache(*static_cast<RoadVehicle *>(v));
|
||||
}
|
||||
}
|
||||
|
||||
void FixPointers(Vehicle *v) const override
|
||||
|
@ -857,12 +896,25 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class SlVehicleShipPath : public VectorSaveLoadHandler<SlVehicleShipPath, Ship, ShipPathElement> {
|
||||
public:
|
||||
inline static const SaveLoad description[] = {
|
||||
SLE_VAR(ShipPathElement, trackdir, SLE_UINT8),
|
||||
};
|
||||
inline const static SaveLoadCompatTable compat_description = {};
|
||||
|
||||
std::vector<ShipPathElement> &GetVector(Ship *s) const override { return s->path; }
|
||||
};
|
||||
|
||||
class SlVehicleShip : public DefaultSaveLoadHandler<SlVehicleShip, Vehicle> {
|
||||
public:
|
||||
static inline std::vector<Trackdir> ship_path_td;
|
||||
|
||||
inline static const SaveLoad description[] = {
|
||||
SLEG_STRUCT("common", SlVehicleCommon),
|
||||
SLE_VAR(Ship, state, SLE_UINT8),
|
||||
SLE_CONDDEQUE(Ship, path, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION),
|
||||
SLEG_CONDVECTOR("path", ship_path_td, SLE_UINT8, SLV_SHIP_PATH_CACHE, SLV_PATH_CACHE_FORMAT),
|
||||
SLEG_CONDSTRUCTLIST("path", SlVehicleShipPath, SLV_PATH_CACHE_FORMAT, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Ship, rotation, SLE_UINT8, SLV_SHIP_ROTATION, SL_MAX_VERSION),
|
||||
};
|
||||
inline const static SaveLoadCompatTable compat_description = _vehicle_ship_sl_compat;
|
||||
|
@ -877,6 +929,12 @@ public:
|
|||
{
|
||||
if (v->type != VEH_SHIP) return;
|
||||
SlObject(v, this->GetLoadDescription());
|
||||
|
||||
if (IsSavegameVersionBefore(SLV_PATH_CACHE_FORMAT)) {
|
||||
/* Path cache is now taken from back instead of front, so needs reversing. */
|
||||
Ship *s = static_cast<Ship *>(v);
|
||||
std::transform(std::rbegin(ship_path_td), std::rend(ship_path_td), std::back_inserter(s->path), [](Trackdir trackdir) { return trackdir; });
|
||||
}
|
||||
}
|
||||
|
||||
void FixPointers(Vehicle *v) const override
|
||||
|
|
10
src/ship.h
10
src/ship.h
|
@ -16,7 +16,15 @@
|
|||
void GetShipSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type);
|
||||
WaterClass GetEffectiveWaterClass(TileIndex tile);
|
||||
|
||||
typedef std::deque<Trackdir> ShipPathCache;
|
||||
/** Element of the ShipPathCache. */
|
||||
struct ShipPathElement {
|
||||
Trackdir trackdir; ///< Trackdir for this element.
|
||||
|
||||
constexpr ShipPathElement() : trackdir(INVALID_TRACKDIR) {}
|
||||
constexpr ShipPathElement(Trackdir trackdir) : trackdir(trackdir) {}
|
||||
};
|
||||
|
||||
using ShipPathCache = std::vector<ShipPathElement>;
|
||||
|
||||
/**
|
||||
* All ships have this type.
|
||||
|
|
|
@ -506,10 +506,10 @@ static Track ChooseShipTrack(Ship *v, TileIndex tile, TrackBits tracks)
|
|||
} else {
|
||||
/* Attempt to follow cached path. */
|
||||
if (!v->path.empty()) {
|
||||
track = TrackdirToTrack(v->path.front());
|
||||
track = TrackdirToTrack(v->path.back().trackdir);
|
||||
|
||||
if (HasBit(tracks, track)) {
|
||||
v->path.pop_front();
|
||||
v->path.pop_back();
|
||||
/* HandlePathfindResult() is not called here because this is not a new pathfinder result. */
|
||||
return track;
|
||||
}
|
||||
|
@ -854,7 +854,7 @@ static void ShipController(Ship *v)
|
|||
|
||||
/* Ship is back on the bridge head, we need to consume its path
|
||||
* cache entry here as we didn't have to choose a ship track. */
|
||||
if (!v->path.empty()) v->path.pop_front();
|
||||
if (!v->path.empty()) v->path.pop_back();
|
||||
}
|
||||
|
||||
/* update image of ship, as well as delta XY */
|
||||
|
|
Loading…
Reference in New Issue