Change ship path cache to be optional and use a ring buffer

This commit is contained in:
Jonathan G Rennison 2023-08-16 15:20:24 +01:00
parent 2ae4e5bdc1
commit 92e632454e
7 changed files with 92 additions and 20 deletions

View File

@ -38,9 +38,6 @@ static const int YAPF_TILE_CORNER_LENGTH = 71;
*/ */
static const int YAPF_INFINITE_PENALTY = 1000 * YAPF_TILE_LENGTH; static const int YAPF_INFINITE_PENALTY = 1000 * YAPF_TILE_LENGTH;
/** Maximum length of ship path cache */
static const int YAPF_SHIP_PATH_CACHE_LENGTH = 32;
/** Distance from destination road stops to not cache any further */ /** Distance from destination road stops to not cache any further */
static const int YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT = 8; static const int YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT = 8;

View File

@ -173,7 +173,7 @@ public:
uint steps = 0; uint steps = 0;
for (Node *n = pNode; n->m_parent != nullptr; n = n->m_parent) steps++; for (Node *n = pNode; n->m_parent != nullptr; n = n->m_parent) steps++;
uint skip = 0; uint skip = 0;
if (path_found) skip = YAPF_SHIP_PATH_CACHE_LENGTH / 2; if (path_found) skip = SHIP_PATH_CACHE_LENGTH / 2;
/* walk through the path back to the origin */ /* walk through the path back to the origin */
Node *pPrevNode = nullptr; Node *pPrevNode = nullptr;
@ -181,7 +181,7 @@ public:
steps--; steps--;
/* Skip tiles at end of path near destination. */ /* Skip tiles at end of path near destination. */
if (skip > 0) skip--; if (skip > 0) skip--;
if (skip == 0 && steps > 0 && steps < YAPF_SHIP_PATH_CACHE_LENGTH) { if (skip == 0 && steps > 0 && steps < SHIP_PATH_CACHE_LENGTH) {
path_cache.push_front(pNode->GetTrackdir()); path_cache.push_front(pNode->GetTrackdir());
} }
pPrevNode = pNode; pPrevNode = pNode;

View File

@ -293,7 +293,7 @@ public:
inline static const SaveLoad description[] = { inline static const SaveLoad description[] = {
SLEG_STRUCT("common", SlVehicleCommon), SLEG_STRUCT("common", SlVehicleCommon),
SLE_VAR(Ship, state, SLE_UINT8), SLE_VAR(Ship, state, SLE_UINT8),
SLE_CONDDEQUE(Ship, path, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION), SLEG_CONDVECTOR("path", _path_td, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION),
SLE_CONDVAR(Ship, rotation, SLE_UINT8, SLV_SHIP_ROTATION, 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; inline const static SaveLoadCompatTable compat_description = _vehicle_ship_sl_compat;
@ -308,6 +308,16 @@ public:
{ {
if (v->type != VEH_SHIP) return; if (v->type != VEH_SHIP) return;
SlObject(v, this->GetLoadDescription()); SlObject(v, this->GetLoadDescription());
if (!_path_td.empty() && _path_td.size() <= SHIP_PATH_CACHE_LENGTH) {
Ship *s = Ship::From(v);
s->cached_path.reset(new ShipPathCache());
s->cached_path->count = _path_td.size();
for (size_t i = 0; i < _path_td.size(); i++) {
s->cached_path->td[i] = _path_td[i];
}
}
_path_td.clear();
} }
void FixPointers(Vehicle *v) const override void FixPointers(Vehicle *v) const override

View File

@ -1709,7 +1709,7 @@ static void MaxVehiclesChanged(int32 new_value)
static void InvalidateShipPathCache(int32 new_value) static void InvalidateShipPathCache(int32 new_value)
{ {
for (Ship *s : Ship::Iterate()) { for (Ship *s : Ship::Iterate()) {
s->path.clear(); s->cached_path.reset();
} }
} }

View File

@ -10,7 +10,7 @@
#ifndef SHIP_H #ifndef SHIP_H
#define SHIP_H #define SHIP_H
#include <deque> #include <array>
#include "vehicle_base.h" #include "vehicle_base.h"
#include "water_map.h" #include "water_map.h"
@ -20,14 +20,55 @@ extern const DiagDirection _ship_search_directions[TRACK_END][DIAGDIR_END];
void GetShipSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type); void GetShipSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type);
WaterClass GetEffectiveWaterClass(TileIndex tile); WaterClass GetEffectiveWaterClass(TileIndex tile);
typedef std::deque<Trackdir> ShipPathCache; /** Maximum segments of ship path cache */
static const uint8 SHIP_PATH_CACHE_LENGTH = 32;
static const uint8 SHIP_PATH_CACHE_MASK = (SHIP_PATH_CACHE_LENGTH - 1);
static_assert((SHIP_PATH_CACHE_LENGTH & SHIP_PATH_CACHE_MASK) == 0, ""); // Must be a power of 2
struct ShipPathCache {
std::array<Trackdir, SHIP_PATH_CACHE_LENGTH> td;
uint8 start = 0;
uint8 count = 0;
inline bool empty() const { return this->count == 0; }
inline uint8 size() const { return this->count; }
inline bool full() const { return this->count >= SHIP_PATH_CACHE_LENGTH; }
inline void clear()
{
this->start = 0;
this->count = 0;
}
inline Trackdir front() const { return this->td[this->start]; }
inline Trackdir back() const { return this->td[(this->start + this->count - 1) & SHIP_PATH_CACHE_MASK]; }
/* push an item to the front of the ring, if the ring is already full, the back item is overwritten */
inline void push_front(Trackdir td)
{
this->start = (this->start - 1) & SHIP_PATH_CACHE_MASK;
if (!this->full()) this->count++;
this->td[this->start] = td;
}
inline void pop_front()
{
this->start = (this->start + 1) & SHIP_PATH_CACHE_MASK;
this->count--;
}
inline void pop_back()
{
this->count--;
}
};
/** /**
* All ships have this type. * All ships have this type.
*/ */
struct Ship FINAL : public SpecializedVehicle<Ship, VEH_SHIP> { struct Ship FINAL : public SpecializedVehicle<Ship, VEH_SHIP> {
TrackBits state; ///< The "track" the ship is following. TrackBits state; ///< The "track" the ship is following.
ShipPathCache path; ///< Cached path. std::unique_ptr<ShipPathCache> cached_path; ///< Cached path.
Direction rotation; ///< Visible direction. Direction rotation; ///< Visible direction.
int16 rotation_x_pos; ///< NOSAVE: X Position before rotation. int16 rotation_x_pos; ///< NOSAVE: X Position before rotation.
int16 rotation_y_pos; ///< NOSAVE: Y Position before rotation. int16 rotation_y_pos; ///< NOSAVE: Y Position before rotation.
@ -61,6 +102,12 @@ struct Ship FINAL : public SpecializedVehicle<Ship, VEH_SHIP> {
ClosestDepot FindClosestDepot() override; ClosestDepot FindClosestDepot() override;
void UpdateCache(); void UpdateCache();
void SetDestTile(TileIndex tile) override; void SetDestTile(TileIndex tile) override;
inline ShipPathCache &GetOrCreatePathCache()
{
if (!this->cached_path) this->cached_path.reset(new ShipPathCache());
return *this->cached_path;
}
}; };
bool IsShipDestinationTile(TileIndex tile, StationID station); bool IsShipDestinationTile(TileIndex tile, StationID station);

View File

@ -567,22 +567,22 @@ static Track ChooseShipTrack(Ship *v, TileIndex tile, DiagDirection enterdir, Tr
path_found = false; path_found = false;
} else { } else {
/* Attempt to follow cached path. */ /* Attempt to follow cached path. */
if (!v->path.empty()) { if (v->cached_path != nullptr && !v->cached_path->empty()) {
track = TrackdirToTrack(v->path.front()); track = TrackdirToTrack(v->cached_path->front());
if (HasBit(tracks, track)) { if (HasBit(tracks, track)) {
v->path.pop_front(); v->cached_path->pop_front();
/* HandlePathfindResult() is not called here because this is not a new pathfinder result. */ /* HandlePathfindResult() is not called here because this is not a new pathfinder result. */
return track; return track;
} }
/* Cached path is invalid so continue with pathfinder. */ /* Cached path is invalid so continue with pathfinder. */
v->path.clear(); v->cached_path->clear();
} }
switch (_settings_game.pf.pathfinder_for_ships) { switch (_settings_game.pf.pathfinder_for_ships) {
case VPF_NPF: track = NPFShipChooseTrack(v, path_found); break; case VPF_NPF: track = NPFShipChooseTrack(v, path_found); break;
case VPF_YAPF: track = YapfShipChooseTrack(v, tile, enterdir, tracks, path_found, v->path); break; case VPF_YAPF: track = YapfShipChooseTrack(v, tile, enterdir, tracks, path_found, v->GetOrCreatePathCache()); break;
default: NOT_REACHED(); default: NOT_REACHED();
} }
} }
@ -882,7 +882,7 @@ static void ReverseShipIntoTrackdir(Ship *v, Trackdir trackdir)
v->rotation_x_pos = v->x_pos; v->rotation_x_pos = v->x_pos;
v->rotation_y_pos = v->y_pos; v->rotation_y_pos = v->y_pos;
UpdateShipSpeed(v, 0); UpdateShipSpeed(v, 0);
v->path.clear(); if (v->cached_path != nullptr) v->cached_path->clear();
v->UpdatePosition(); v->UpdatePosition();
v->UpdateViewport(true, true); v->UpdateViewport(true, true);
@ -896,7 +896,7 @@ static void ReverseShip(Ship *v)
v->rotation_x_pos = v->x_pos; v->rotation_x_pos = v->x_pos;
v->rotation_y_pos = v->y_pos; v->rotation_y_pos = v->y_pos;
UpdateShipSpeed(v, 0); UpdateShipSpeed(v, 0);
v->path.clear(); if (v->cached_path != nullptr) v->cached_path->clear();
v->UpdatePosition(); v->UpdatePosition();
v->UpdateViewport(true, true); v->UpdateViewport(true, true);
@ -1081,7 +1081,7 @@ static void ShipController(Ship *v)
/* Ship is back on the bridge head, we need to consume its path /* 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. */ * cache entry here as we didn't have to choose a ship track. */
if (!v->path.empty()) v->path.pop_front(); if (v->cached_path != nullptr && !v->cached_path->empty()) v->cached_path->pop_front();
} }
/* update image of ship, as well as delta XY */ /* update image of ship, as well as delta XY */
@ -1107,7 +1107,7 @@ bool Ship::Tick()
void Ship::SetDestTile(TileIndex tile) void Ship::SetDestTile(TileIndex tile)
{ {
if (tile == this->dest_tile) return; if (tile == this->dest_tile) return;
this->path.clear(); if (this->cached_path != nullptr) this->cached_path->clear();
this->dest_tile = tile; this->dest_tile = tile;
} }

View File

@ -877,7 +877,7 @@ SaveLoadTable GetVehicleDescription(VehicleType vt)
SLE_WRITEBYTE(Vehicle, type), SLE_WRITEBYTE(Vehicle, type),
SLE_VEH_INCLUDE(), SLE_VEH_INCLUDE(),
SLE_VAR(Ship, state, SLE_UINT8), SLE_VAR(Ship, state, SLE_UINT8),
SLE_CONDDEQUE(Ship, path, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION), SLEG_CONDVARVEC(_path_td, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION),
SLE_CONDVAR(Ship, rotation, SLE_UINT8, SLV_SHIP_ROTATION, SL_MAX_VERSION), SLE_CONDVAR(Ship, rotation, SLE_UINT8, SLV_SHIP_ROTATION, SL_MAX_VERSION),
SLE_CONDVAR_X(Ship, lost_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SHIP_LOST_COUNTER)), SLE_CONDVAR_X(Ship, lost_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SHIP_LOST_COUNTER)),
SLE_CONDVAR_X(Ship, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 8)), SLE_CONDVAR_X(Ship, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 8)),
@ -1043,6 +1043,17 @@ static void Save_VEHS()
} }
_path_layout_ctr = rv->cached_path->layout_ctr; _path_layout_ctr = rv->cached_path->layout_ctr;
} }
} else if (v->type == VEH_SHIP) {
_path_td.clear();
Ship *s = Ship::From(v);
if (s->cached_path != nullptr && !s->cached_path->empty()) {
uint idx = s->cached_path->start;
for (uint i = 0; i < s->cached_path->size(); i++) {
_path_td.push_back(s->cached_path->td[idx]);
idx = (idx + 1) & SHIP_PATH_CACHE_MASK;
}
}
} }
SlSetArrayIndex(v->index); SlSetArrayIndex(v->index);
SlObjectSaveFiltered(v, GetVehicleDescriptionFiltered(v->type)); SlObjectSaveFiltered(v, GetVehicleDescriptionFiltered(v->type));
@ -1123,6 +1134,13 @@ void Load_VEHS()
rv->cached_path->tile[i] = _path_tile[i]; rv->cached_path->tile[i] = _path_tile[i];
} }
rv->cached_path->layout_ctr = _path_layout_ctr; rv->cached_path->layout_ctr = _path_layout_ctr;
} else if (vtype == VEH_SHIP && !_path_td.empty() && _path_td.size() <= SHIP_PATH_CACHE_LENGTH) {
Ship *s = Ship::From(v);
s->cached_path.reset(new ShipPathCache());
s->cached_path->count = _path_td.size();
for (size_t i = 0; i < _path_td.size(); i++) {
s->cached_path->td[i] = _path_td[i];
}
} }
} }
} }