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

pull/590/head
Jonathan G Rennison 10 months ago
parent 2ae4e5bdc1
commit 92e632454e

@ -38,9 +38,6 @@ static const int YAPF_TILE_CORNER_LENGTH = 71;
*/
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 */
static const int YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT = 8;

@ -173,7 +173,7 @@ public:
uint steps = 0;
for (Node *n = pNode; n->m_parent != nullptr; n = n->m_parent) steps++;
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 */
Node *pPrevNode = nullptr;
@ -181,7 +181,7 @@ public:
steps--;
/* Skip tiles at end of path near destination. */
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());
}
pPrevNode = pNode;

@ -293,7 +293,7 @@ public:
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", _path_td, SLE_UINT8, SLV_SHIP_PATH_CACHE, 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;
@ -308,6 +308,16 @@ public:
{
if (v->type != VEH_SHIP) return;
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

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

@ -10,7 +10,7 @@
#ifndef SHIP_H
#define SHIP_H
#include <deque>
#include <array>
#include "vehicle_base.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);
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.
*/
struct Ship FINAL : public SpecializedVehicle<Ship, VEH_SHIP> {
TrackBits state; ///< The "track" the ship is following.
ShipPathCache path; ///< Cached path.
std::unique_ptr<ShipPathCache> cached_path; ///< Cached path.
Direction rotation; ///< Visible direction.
int16 rotation_x_pos; ///< NOSAVE: X 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;
void UpdateCache();
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);

@ -567,22 +567,22 @@ static Track ChooseShipTrack(Ship *v, TileIndex tile, DiagDirection enterdir, Tr
path_found = false;
} else {
/* Attempt to follow cached path. */
if (!v->path.empty()) {
track = TrackdirToTrack(v->path.front());
if (v->cached_path != nullptr && !v->cached_path->empty()) {
track = TrackdirToTrack(v->cached_path->front());
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. */
return track;
}
/* Cached path is invalid so continue with pathfinder. */
v->path.clear();
v->cached_path->clear();
}
switch (_settings_game.pf.pathfinder_for_ships) {
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();
}
}
@ -882,7 +882,7 @@ static void ReverseShipIntoTrackdir(Ship *v, Trackdir trackdir)
v->rotation_x_pos = v->x_pos;
v->rotation_y_pos = v->y_pos;
UpdateShipSpeed(v, 0);
v->path.clear();
if (v->cached_path != nullptr) v->cached_path->clear();
v->UpdatePosition();
v->UpdateViewport(true, true);
@ -896,7 +896,7 @@ static void ReverseShip(Ship *v)
v->rotation_x_pos = v->x_pos;
v->rotation_y_pos = v->y_pos;
UpdateShipSpeed(v, 0);
v->path.clear();
if (v->cached_path != nullptr) v->cached_path->clear();
v->UpdatePosition();
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
* 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 */
@ -1107,7 +1107,7 @@ bool Ship::Tick()
void Ship::SetDestTile(TileIndex tile)
{
if (tile == this->dest_tile) return;
this->path.clear();
if (this->cached_path != nullptr) this->cached_path->clear();
this->dest_tile = tile;
}

@ -877,7 +877,7 @@ SaveLoadTable GetVehicleDescription(VehicleType vt)
SLE_WRITEBYTE(Vehicle, type),
SLE_VEH_INCLUDE(),
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_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)),
@ -1043,6 +1043,17 @@ static void Save_VEHS()
}
_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);
SlObjectSaveFiltered(v, GetVehicleDescriptionFiltered(v->type));
@ -1123,6 +1134,13 @@ void Load_VEHS()
rv->cached_path->tile[i] = _path_tile[i];
}
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];
}
}
}
}

Loading…
Cancel
Save