diff --git a/src/pathfinder/pathfinder_type.h b/src/pathfinder/pathfinder_type.h index 6151dea3ee..563b81d7a9 100644 --- a/src/pathfinder/pathfinder_type.h +++ b/src/pathfinder/pathfinder_type.h @@ -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; diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp index 24eed596d2..51a4484fa4 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -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; diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index eba8e58b0f..db8e0f7422 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -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 diff --git a/src/settings.cpp b/src/settings.cpp index 344a3d7f83..982835ca20 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -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(); } } diff --git a/src/ship.h b/src/ship.h index 97ad98aadd..048932d105 100644 --- a/src/ship.h +++ b/src/ship.h @@ -10,7 +10,7 @@ #ifndef SHIP_H #define SHIP_H -#include +#include #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 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 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 { TrackBits state; ///< The "track" the ship is following. - ShipPathCache path; ///< Cached path. + std::unique_ptr 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 { 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); diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index eb8409be0f..b57a6ec5b8 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -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; } diff --git a/src/sl/vehicle_sl.cpp b/src/sl/vehicle_sl.cpp index 2df5d8bafa..666bb5089e 100644 --- a/src/sl/vehicle_sl.cpp +++ b/src/sl/vehicle_sl.cpp @@ -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]; + } } } }