Change road vehicle path cache to be optional and use ring buffers
Show path cache in debug window
This commit is contained in:
@@ -41,9 +41,6 @@ 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;
|
||||
|
||||
/** Maximum segments of road vehicle path cache */
|
||||
static const int YAPF_ROADVEH_PATH_CACHE_SEGMENTS = 16;
|
||||
|
||||
/** Distance from destination road stops to not cache any further */
|
||||
static const int YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT = 8;
|
||||
|
||||
|
@@ -495,16 +495,11 @@ public:
|
||||
Trackdir next_trackdir = INVALID_TRACKDIR;
|
||||
Node *pNode = Yapf().GetBestNode();
|
||||
if (pNode != nullptr) {
|
||||
uint steps = 0;
|
||||
for (Node *n = pNode; n->m_parent != nullptr; n = n->m_parent) steps++;
|
||||
|
||||
/* path was found or at least suggested
|
||||
* walk through the path back to its origin */
|
||||
while (pNode->m_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());
|
||||
if (pNode->GetIsChoice()) {
|
||||
path_cache.push_front(pNode->GetTile(), pNode->GetTrackdir());
|
||||
}
|
||||
pNode = pNode->m_parent;
|
||||
}
|
||||
@@ -514,8 +509,7 @@ public:
|
||||
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();
|
||||
path_cache.pop_back();
|
||||
}
|
||||
path_cache.layout_ctr = _road_layout_change_counter;
|
||||
|
||||
@@ -523,9 +517,8 @@ public:
|
||||
if (multiple_targets) {
|
||||
/* Destination station has at least 2 usable road stops, or first is a drive-through stop,
|
||||
* trim end of path cache within a number of tiles of road stop tile area */
|
||||
while (!path_cache.empty() && non_cached_area.Contains(path_cache.tile.back())) {
|
||||
path_cache.td.pop_back();
|
||||
path_cache.tile.pop_back();
|
||||
while (!path_cache.empty() && non_cached_area.Contains(path_cache.back_tile())) {
|
||||
path_cache.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@
|
||||
#include "road.h"
|
||||
#include "road_map.h"
|
||||
#include "newgrf_engine.h"
|
||||
#include <deque>
|
||||
#include <array>
|
||||
|
||||
struct RoadVehicle;
|
||||
|
||||
@@ -79,26 +79,56 @@ static const uint RVC_DEPOT_STOP_FRAME = 11;
|
||||
/** The number of ticks a vehicle has for overtaking. */
|
||||
static const byte RV_OVERTAKE_TIMEOUT = 35;
|
||||
|
||||
/** Maximum segments of road vehicle path cache */
|
||||
static const uint8 RV_PATH_CACHE_SEGMENTS = 16;
|
||||
static const uint8 RV_PATH_CACHE_SEGMENT_MASK = (RV_PATH_CACHE_SEGMENTS - 1);
|
||||
static_assert((RV_PATH_CACHE_SEGMENTS & RV_PATH_CACHE_SEGMENT_MASK) == 0, ""); // Must be a power of 2
|
||||
|
||||
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;
|
||||
uint32 layout_ctr;
|
||||
std::array<TileIndex, RV_PATH_CACHE_SEGMENTS> tile;
|
||||
std::array<Trackdir, RV_PATH_CACHE_SEGMENTS> td;
|
||||
uint32 layout_ctr = 0;
|
||||
uint8 start = 0;
|
||||
uint8 count = 0;
|
||||
|
||||
inline bool empty() const { return this->td.empty(); }
|
||||
|
||||
inline size_t size() const
|
||||
{
|
||||
dbg_assert(this->td.size() == this->tile.size());
|
||||
return this->td.size();
|
||||
}
|
||||
inline bool empty() const { return this->count == 0; }
|
||||
inline uint8 size() const { return this->count; }
|
||||
inline bool full() const { return this->count >= RV_PATH_CACHE_SEGMENTS; }
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
this->td.clear();
|
||||
this->tile.clear();
|
||||
this->start = 0;
|
||||
this->count = 0;
|
||||
}
|
||||
|
||||
inline TileIndex front_tile() const { return this->tile[this->start]; }
|
||||
inline Trackdir front_td() const { return this->td[this->start]; }
|
||||
|
||||
inline uint8 back_index() const { return (this->start + this->count - 1) & RV_PATH_CACHE_SEGMENT_MASK; }
|
||||
inline TileIndex back_tile() const { return this->tile[this->back_index()]; }
|
||||
inline Trackdir back_td() const { return this->td[this->back_index()]; }
|
||||
|
||||
/* push an item to the front of the ring, if the ring is already full, the back item is overwritten */
|
||||
inline void push_front(TileIndex tile, Trackdir td)
|
||||
{
|
||||
this->start = (this->start - 1) & RV_PATH_CACHE_SEGMENT_MASK;
|
||||
if (!this->full()) this->count++;
|
||||
this->tile[this->start] = tile;
|
||||
this->td[this->start] = td;
|
||||
}
|
||||
|
||||
inline void pop_front()
|
||||
{
|
||||
this->start = (this->start + 1) & RV_PATH_CACHE_SEGMENT_MASK;
|
||||
this->count--;
|
||||
}
|
||||
|
||||
inline void pop_back()
|
||||
{
|
||||
this->count--;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,7 +136,7 @@ struct RoadVehPathCache {
|
||||
* Buses, trucks and trams belong to this class.
|
||||
*/
|
||||
struct RoadVehicle FINAL : public GroundVehicle<RoadVehicle, VEH_ROAD> {
|
||||
RoadVehPathCache path; ///< Cached path.
|
||||
std::unique_ptr<RoadVehPathCache> cached_path; ///< Cached path.
|
||||
byte state; ///< @see RoadVehicleStates
|
||||
byte frame;
|
||||
uint16 blocked_ctr;
|
||||
@@ -175,6 +205,12 @@ struct RoadVehicle FINAL : public GroundVehicle<RoadVehicle, VEH_ROAD> {
|
||||
|
||||
void SetRoadVehicleOvertaking(byte overtaking);
|
||||
|
||||
inline RoadVehPathCache &GetOrCreatePathCache()
|
||||
{
|
||||
if (!this->cached_path) this->cached_path.reset(new RoadVehPathCache());
|
||||
return *this->cached_path;
|
||||
}
|
||||
|
||||
protected: // These functions should not be called outside acceleration code.
|
||||
|
||||
/**
|
||||
|
@@ -924,8 +924,8 @@ static bool CheckRoadInfraUnsuitableForOvertaking(OvertakeData *od)
|
||||
if (trackbits & ~TRACK_BIT_CROSS) {
|
||||
RoadCachedOneWayState rcows = GetRoadCachedOneWayState(od->tile);
|
||||
if (rcows == RCOWS_SIDE_JUNCTION) {
|
||||
const RoadVehPathCache &pc = od->v->path;
|
||||
if (!pc.empty() && pc.tile.front() == od->tile && !IsStraightRoadTrackdir(pc.td.front())) {
|
||||
const RoadVehPathCache *pc = od->v->cached_path.get();
|
||||
if (pc != nullptr && !pc->empty() && pc->front_tile() == od->tile && !IsStraightRoadTrackdir(pc->front_td())) {
|
||||
/* cached path indicates that we are turning here, do not overtake */
|
||||
return true;
|
||||
}
|
||||
@@ -1210,7 +1210,7 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
|
||||
trackdirs &= DiagdirReachesTrackdirs(enterdir);
|
||||
if (trackdirs == TRACKDIR_BIT_NONE) {
|
||||
/* If vehicle expected a path, it no longer exists, so invalidate it. */
|
||||
if (!v->path.empty()) v->path.clear();
|
||||
if (v->cached_path != nullptr) v->cached_path->clear();
|
||||
/* No reachable tracks, so we'll reverse */
|
||||
return_track(_road_reverse_table[enterdir]);
|
||||
}
|
||||
@@ -1241,40 +1241,39 @@ 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->cached_path != nullptr && !v->cached_path->empty() && v->cached_path->front_tile() == tile) {
|
||||
/* Vehicle expected a choice here, invalidate its path. */
|
||||
v->path.clear();
|
||||
v->cached_path->clear();
|
||||
}
|
||||
return_track(FindFirstBit2x64(trackdirs));
|
||||
}
|
||||
|
||||
/* Path cache is out of date, clear it */
|
||||
if (!v->path.empty() && v->path.layout_ctr != _road_layout_change_counter) {
|
||||
v->path.clear();
|
||||
if (v->cached_path != nullptr && !v->cached_path->empty() && v->cached_path->layout_ctr != _road_layout_change_counter) {
|
||||
v->cached_path->clear();
|
||||
}
|
||||
|
||||
/* Attempt to follow cached path. */
|
||||
if (!v->path.empty()) {
|
||||
if (v->path.tile.front() != tile) {
|
||||
if (v->cached_path != nullptr && !v->cached_path->empty()) {
|
||||
if (v->cached_path->front_tile() != tile) {
|
||||
/* Vehicle didn't expect a choice here, invalidate its path. */
|
||||
v->path.clear();
|
||||
v->cached_path->clear();
|
||||
} else {
|
||||
Trackdir trackdir = v->path.td.front();
|
||||
Trackdir trackdir = v->cached_path->front_td();
|
||||
|
||||
if (HasBit(trackdirs, trackdir)) {
|
||||
v->path.td.pop_front();
|
||||
v->path.tile.pop_front();
|
||||
v->cached_path->pop_front();
|
||||
return_track(trackdir);
|
||||
}
|
||||
|
||||
/* Vehicle expected a choice which is no longer available. */
|
||||
v->path.clear();
|
||||
v->cached_path->clear();
|
||||
}
|
||||
}
|
||||
|
||||
switch (_settings_game.pf.pathfinder_for_roadvehs) {
|
||||
case VPF_NPF: best_track = NPFRoadVehicleChooseTrack(v, tile, enterdir, path_found); break;
|
||||
case VPF_YAPF: best_track = YapfRoadVehicleChooseTrack(v, tile, enterdir, trackdirs, path_found, v->path); break;
|
||||
case VPF_YAPF: best_track = YapfRoadVehicleChooseTrack(v, tile, enterdir, trackdirs, path_found, v->GetOrCreatePathCache()); break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
@@ -1814,8 +1813,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->GetOrCreatePathCache().push_front(tile, dir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1931,8 +1929,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->GetOrCreatePathCache().push_front(v->tile, dir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2226,7 +2223,7 @@ bool RoadVehicle::Tick()
|
||||
void RoadVehicle::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;
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "../disaster_vehicle.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
@@ -32,6 +33,9 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // From tr
|
||||
void ReverseTrainDirection(Train *v);
|
||||
void ReverseTrainSwapVeh(Train *v, int l, int r);
|
||||
|
||||
static std::vector<Trackdir> _path_td;
|
||||
static std::vector<TileIndex> _path_tile;
|
||||
|
||||
namespace upstream_sl {
|
||||
|
||||
static uint8 _cargo_days;
|
||||
@@ -247,8 +251,8 @@ 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", _path_td, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION),
|
||||
SLEG_CONDVECTOR("path.tile", _path_tile, SLE_UINT32, SLV_SHIP_PATH_CACHE, 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;
|
||||
@@ -263,6 +267,18 @@ public:
|
||||
{
|
||||
if (v->type != VEH_ROAD) return;
|
||||
SlObject(v, this->GetLoadDescription());
|
||||
|
||||
if (!_path_td.empty() && _path_td.size() <= RV_PATH_CACHE_SEGMENTS && _path_td.size() == _path_tile.size()) {
|
||||
RoadVehicle *rv = RoadVehicle::From(v);
|
||||
rv->cached_path.reset(new RoadVehPathCache());
|
||||
rv->cached_path->count = _path_td.size();
|
||||
for (size_t i = 0; i < _path_td.size(); i++) {
|
||||
rv->cached_path->td[i] = _path_td[i];
|
||||
rv->cached_path->tile[i] = _path_tile[i];
|
||||
}
|
||||
}
|
||||
_path_td.clear();
|
||||
_path_tile.clear();
|
||||
}
|
||||
|
||||
void FixPointers(Vehicle *v) const override
|
||||
|
@@ -644,6 +644,9 @@ static Money _cargo_feeder_share;
|
||||
static uint32 _cargo_loaded_at_xy;
|
||||
CargoPacketList _cpp_packets;
|
||||
std::map<VehicleID, CargoPacketList> _veh_cpp_packets;
|
||||
static std::vector<Trackdir> _path_td;
|
||||
static std::vector<TileIndex> _path_tile;
|
||||
static uint32 _path_layout_ctr;
|
||||
|
||||
static uint32 _old_ahead_separation;
|
||||
|
||||
@@ -858,9 +861,9 @@ SaveLoadTable GetVehicleDescription(VehicleType vt)
|
||||
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),
|
||||
SLE_CONDVAR_X(RoadVehicle, path.layout_ctr, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)),
|
||||
SLEG_CONDVARVEC(_path_td, SLE_UINT8, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION),
|
||||
SLEG_CONDVARVEC(_path_tile, SLE_UINT32, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION),
|
||||
SLEG_CONDVAR_X(_path_layout_ctr, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)),
|
||||
|
||||
SLE_CONDNULL(2, SLV_6, SLV_69),
|
||||
SLE_CONDVAR(RoadVehicle, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION),
|
||||
@@ -1025,6 +1028,22 @@ static void Save_VEHS()
|
||||
SetupDescs_VEHS();
|
||||
/* Write the vehicles */
|
||||
for (Vehicle *v : Vehicle::Iterate()) {
|
||||
if (v->type == VEH_ROAD) {
|
||||
_path_td.clear();
|
||||
_path_tile.clear();
|
||||
_path_layout_ctr = 0;
|
||||
|
||||
RoadVehicle *rv = RoadVehicle::From(v);
|
||||
if (rv->cached_path != nullptr && !rv->cached_path->empty()) {
|
||||
uint idx = rv->cached_path->start;
|
||||
for (uint i = 0; i < rv->cached_path->size(); i++) {
|
||||
_path_td.push_back(rv->cached_path->td[idx]);
|
||||
_path_tile.push_back(rv->cached_path->tile[idx]);
|
||||
idx = (idx + 1) & RV_PATH_CACHE_SEGMENT_MASK;
|
||||
}
|
||||
_path_layout_ctr = rv->cached_path->layout_ctr;
|
||||
}
|
||||
}
|
||||
SlSetArrayIndex(v->index);
|
||||
SlObjectSaveFiltered(v, GetVehicleDescriptionFiltered(v->type));
|
||||
}
|
||||
@@ -1042,6 +1061,10 @@ void Load_VEHS()
|
||||
_cpp_packets.clear();
|
||||
_veh_cpp_packets.clear();
|
||||
|
||||
_path_td.clear();
|
||||
_path_tile.clear();
|
||||
_path_layout_ctr = 0;
|
||||
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
Vehicle *v;
|
||||
VehicleType vtype = (VehicleType)SlReadByte();
|
||||
@@ -1090,6 +1113,17 @@ void Load_VEHS()
|
||||
if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 4)) {
|
||||
SB(v->vehicle_flags, VF_SEPARATION_ACTIVE, 1, _old_ahead_separation ? 1 : 0);
|
||||
}
|
||||
|
||||
if (vtype == VEH_ROAD && !_path_td.empty() && _path_td.size() <= RV_PATH_CACHE_SEGMENTS && _path_td.size() == _path_tile.size()) {
|
||||
RoadVehicle *rv = RoadVehicle::From(v);
|
||||
rv->cached_path.reset(new RoadVehPathCache());
|
||||
rv->cached_path->count = _path_td.size();
|
||||
for (size_t i = 0; i < _path_td.size(); i++) {
|
||||
rv->cached_path->td[i] = _path_td[i];
|
||||
rv->cached_path->tile[i] = _path_tile[i];
|
||||
}
|
||||
rv->cached_path->layout_ctr = _path_layout_ctr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -413,9 +413,31 @@ class NIHVehicle : public NIHelper {
|
||||
seprintf(buffer, lastof(buffer), " Overtaking: %u, overtaking_ctr: %u, overtaking threshold: %u",
|
||||
rv->overtaking, rv->overtaking_ctr, rv->GetOvertakingCounterThreshold());
|
||||
output.print(buffer);
|
||||
seprintf(buffer, lastof(buffer), " Speed: %u, path cache length: %u",
|
||||
rv->cur_speed, (uint) rv->path.size());
|
||||
seprintf(buffer, lastof(buffer), " Speed: %u", rv->cur_speed);
|
||||
output.print(buffer);
|
||||
|
||||
b = buffer + seprintf(buffer, lastof(buffer), " Path cache: ");
|
||||
if (rv->cached_path != nullptr) {
|
||||
b += seprintf(b, lastof(buffer), "length: %u, layout ctr: %X (current: %X)", (uint)rv->cached_path->size(), rv->cached_path->layout_ctr, _road_layout_change_counter);
|
||||
output.print(buffer);
|
||||
b = buffer;
|
||||
uint idx = rv->cached_path->start;
|
||||
for (uint i = 0; i < rv->cached_path->size(); i++) {
|
||||
if ((i & 3) == 0) {
|
||||
if (b > buffer + 4) output.print(buffer);
|
||||
b = buffer + seprintf(buffer, lastof(buffer), " ");
|
||||
} else {
|
||||
b += seprintf(b, lastof(buffer), ", ");
|
||||
}
|
||||
b += seprintf(b, lastof(buffer), "(%ux%u, %X)", TileX(rv->cached_path->tile[idx]), TileY(rv->cached_path->tile[idx]), rv->cached_path->td[idx]);
|
||||
idx = (idx + 1) & RV_PATH_CACHE_SEGMENT_MASK;
|
||||
}
|
||||
if (b > buffer + 4) output.print(buffer);
|
||||
} else {
|
||||
b += seprintf(b, lastof(buffer), "none");
|
||||
output.print(buffer);
|
||||
}
|
||||
|
||||
output.register_next_line_click_flag_toggle(8 << flag_shift);
|
||||
seprintf(buffer, lastof(buffer), " [%c] Roadtype: %u (%s), Compatible: 0x" OTTD_PRINTFHEX64,
|
||||
(output.flags & (8 << flag_shift)) ? '-' : '+', rv->roadtype, dumper().RoadTypeLabel(rv->roadtype), rv->compatible_roadtypes);
|
||||
|
Reference in New Issue
Block a user