mirror of https://github.com/OpenTTD/OpenTTD
Change: Use bitmap for free unit ID generation. (#12165)
This improves performance of finding the next free unit number for a vehicle. Based loosely on pool's used slot bitmap.pull/12277/head
parent
9d0c548f55
commit
b64ad26068
|
@ -49,6 +49,19 @@ struct CompanyInfrastructure {
|
||||||
uint32_t GetTramTotal() const;
|
uint32_t GetTramTotal() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FreeUnitIDGenerator {
|
||||||
|
public:
|
||||||
|
UnitID NextID() const;
|
||||||
|
UnitID UseID(UnitID index);
|
||||||
|
void ReleaseID(UnitID index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
using BitmapStorage = size_t;
|
||||||
|
static constexpr size_t BITMAP_SIZE = std::numeric_limits<BitmapStorage>::digits;
|
||||||
|
|
||||||
|
std::vector<BitmapStorage> used_bitmap;
|
||||||
|
};
|
||||||
|
|
||||||
typedef Pool<Company, CompanyID, 1, MAX_COMPANIES> CompanyPool;
|
typedef Pool<Company, CompanyID, 1, MAX_COMPANIES> CompanyPool;
|
||||||
extern CompanyPool _company_pool;
|
extern CompanyPool _company_pool;
|
||||||
|
|
||||||
|
@ -129,6 +142,8 @@ struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> {
|
||||||
|
|
||||||
CompanyInfrastructure infrastructure; ///< NOSAVE: Counts of company owned infrastructure.
|
CompanyInfrastructure infrastructure; ///< NOSAVE: Counts of company owned infrastructure.
|
||||||
|
|
||||||
|
FreeUnitIDGenerator freeunits[VEH_COMPANY_END];
|
||||||
|
|
||||||
Money GetMaxLoan() const;
|
Money GetMaxLoan() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -430,17 +430,13 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
FreeUnitIDGenerator unitidgen[] = {
|
Company *new_company = new_owner == INVALID_OWNER ? nullptr : Company::Get(new_owner);
|
||||||
FreeUnitIDGenerator(VEH_TRAIN, new_owner), FreeUnitIDGenerator(VEH_ROAD, new_owner),
|
|
||||||
FreeUnitIDGenerator(VEH_SHIP, new_owner), FreeUnitIDGenerator(VEH_AIRCRAFT, new_owner)
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Override company settings to new company defaults in case we need to convert them.
|
/* Override company settings to new company defaults in case we need to convert them.
|
||||||
* This is required as the CmdChangeServiceInt doesn't copy the supplied value when it is non-custom
|
* This is required as the CmdChangeServiceInt doesn't copy the supplied value when it is non-custom
|
||||||
*/
|
*/
|
||||||
if (new_owner != INVALID_OWNER) {
|
if (new_owner != INVALID_OWNER) {
|
||||||
Company *old_company = Company::Get(old_owner);
|
Company *old_company = Company::Get(old_owner);
|
||||||
Company *new_company = Company::Get(new_owner);
|
|
||||||
|
|
||||||
old_company->settings.vehicle.servint_aircraft = new_company->settings.vehicle.servint_aircraft;
|
old_company->settings.vehicle.servint_aircraft = new_company->settings.vehicle.servint_aircraft;
|
||||||
old_company->settings.vehicle.servint_trains = new_company->settings.vehicle.servint_trains;
|
old_company->settings.vehicle.servint_trains = new_company->settings.vehicle.servint_trains;
|
||||||
|
@ -457,8 +453,6 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
|
||||||
* This prevents invalid values on mismatching company defaults being accepted.
|
* This prevents invalid values on mismatching company defaults being accepted.
|
||||||
*/
|
*/
|
||||||
if (!v->ServiceIntervalIsCustom()) {
|
if (!v->ServiceIntervalIsCustom()) {
|
||||||
Company *new_company = Company::Get(new_owner);
|
|
||||||
|
|
||||||
/* Technically, passing the interval is not needed as the command will query the default value itself.
|
/* Technically, passing the interval is not needed as the command will query the default value itself.
|
||||||
* However, do not rely on that behaviour.
|
* However, do not rely on that behaviour.
|
||||||
*/
|
*/
|
||||||
|
@ -477,7 +471,8 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
|
||||||
}
|
}
|
||||||
if (v->IsPrimaryVehicle()) {
|
if (v->IsPrimaryVehicle()) {
|
||||||
GroupStatistics::CountVehicle(v, 1);
|
GroupStatistics::CountVehicle(v, 1);
|
||||||
v->unitnumber = unitidgen[v->type].NextID();
|
auto &unitidgen = new_company->freeunits[v->type];
|
||||||
|
v->unitnumber = unitidgen.UseID(unitidgen.NextID());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Invalidate the vehicle's cargo payment "owner cache". */
|
/* Invalidate the vehicle's cargo payment "owner cache". */
|
||||||
|
|
|
@ -517,6 +517,10 @@ void AfterLoadVehicles(bool part_of_load)
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (part_of_load && v->unitnumber != 0) {
|
||||||
|
Company::Get(v->owner)->freeunits[v->type].UseID(v->unitnumber);
|
||||||
|
}
|
||||||
|
|
||||||
v->UpdateDeltaXY();
|
v->UpdateDeltaXY();
|
||||||
v->coord.left = INVALID_COORD;
|
v->coord.left = INVALID_COORD;
|
||||||
v->sprite_cache.old_coord.left = INVALID_COORD;
|
v->sprite_cache.old_coord.left = INVALID_COORD;
|
||||||
|
|
|
@ -1178,7 +1178,7 @@ static void NormaliseTrainHead(Train *head)
|
||||||
|
|
||||||
/* If we don't have a unit number yet, set one. */
|
/* If we don't have a unit number yet, set one. */
|
||||||
if (head->unitnumber != 0) return;
|
if (head->unitnumber != 0) return;
|
||||||
head->unitnumber = GetFreeUnitNumber(VEH_TRAIN);
|
head->unitnumber = Company::Get(head->owner)->freeunits[head->type].UseID(GetFreeUnitNumber(VEH_TRAIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1336,6 +1336,7 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
|
||||||
}
|
}
|
||||||
/* Remove stuff not valid anymore for non-front engines. */
|
/* Remove stuff not valid anymore for non-front engines. */
|
||||||
DeleteVehicleOrders(src);
|
DeleteVehicleOrders(src);
|
||||||
|
Company::Get(src->owner)->freeunits[src->type].ReleaseID(src->unitnumber);
|
||||||
src->unitnumber = 0;
|
src->unitnumber = 0;
|
||||||
src->name.clear();
|
src->name.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -846,6 +846,8 @@ void Vehicle::PreDestructor()
|
||||||
DeleteGroupHighlightOfVehicle(this);
|
DeleteGroupHighlightOfVehicle(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Company::Get(this->owner)->freeunits[this->type].ReleaseID(this->unitnumber);
|
||||||
|
|
||||||
if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
|
if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
|
||||||
Aircraft *a = Aircraft::From(this);
|
Aircraft *a = Aircraft::From(this);
|
||||||
Station *st = GetTargetAirportIfValid(a);
|
Station *st = GetTargetAirportIfValid(a);
|
||||||
|
@ -1816,44 +1818,50 @@ VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the structure. Vehicle unit numbers are supposed not to change after
|
* Find first unused unit number.
|
||||||
* struct initialization, except after each call to this->NextID() the returned value
|
* This does not mark the unit number as used.
|
||||||
* is assigned to a vehicle.
|
* @returns First unused unit number.
|
||||||
* @param type type of vehicle
|
|
||||||
* @param owner owner of vehicles
|
|
||||||
*/
|
*/
|
||||||
FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(nullptr), maxid(0), curid(0)
|
UnitID FreeUnitIDGenerator::NextID() const
|
||||||
{
|
{
|
||||||
/* Find maximum */
|
for (auto it = std::begin(this->used_bitmap); it != std::end(this->used_bitmap); ++it) {
|
||||||
for (const Vehicle *v : Vehicle::Iterate()) {
|
BitmapStorage available = ~(*it);
|
||||||
if (v->type == type && v->owner == owner) {
|
if (available == 0) continue;
|
||||||
this->maxid = std::max<UnitID>(this->maxid, v->unitnumber);
|
return static_cast<UnitID>(std::distance(std::begin(this->used_bitmap), it) * BITMAP_SIZE + FindFirstBit(available) + 1);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->maxid == 0) return;
|
|
||||||
|
|
||||||
/* Reserving 'maxid + 2' because we need:
|
|
||||||
* - space for the last item (with v->unitnumber == maxid)
|
|
||||||
* - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
|
|
||||||
this->cache = CallocT<bool>(this->maxid + 2);
|
|
||||||
|
|
||||||
/* Fill the cache */
|
|
||||||
for (const Vehicle *v : Vehicle::Iterate()) {
|
|
||||||
if (v->type == type && v->owner == owner) {
|
|
||||||
this->cache[v->unitnumber] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return static_cast<UnitID>(this->used_bitmap.size() * BITMAP_SIZE + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
|
/**
|
||||||
UnitID FreeUnitIDGenerator::NextID()
|
* Use a unit number. If the unit number is not valid it is ignored.
|
||||||
|
* @param index Unit number to use.
|
||||||
|
* @returns Unit number used.
|
||||||
|
*/
|
||||||
|
UnitID FreeUnitIDGenerator::UseID(UnitID index)
|
||||||
{
|
{
|
||||||
if (this->maxid <= this->curid) return ++this->curid;
|
if (index == 0 || index == UINT16_MAX) return index;
|
||||||
|
|
||||||
while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
|
index--;
|
||||||
|
|
||||||
return this->curid;
|
size_t slot = index / BITMAP_SIZE;
|
||||||
|
if (slot >= this->used_bitmap.size()) this->used_bitmap.resize(slot + 1);
|
||||||
|
SetBit(this->used_bitmap[index / BITMAP_SIZE], index % BITMAP_SIZE);
|
||||||
|
|
||||||
|
return index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release a unit number. If the unit number is not valid it is ignored.
|
||||||
|
* @param index Unit number to release.
|
||||||
|
*/
|
||||||
|
void FreeUnitIDGenerator::ReleaseID(UnitID index)
|
||||||
|
{
|
||||||
|
if (index == 0 || index == UINT16_MAX) return;
|
||||||
|
|
||||||
|
index--;
|
||||||
|
|
||||||
|
assert(index / BITMAP_SIZE < this->used_bitmap.size());
|
||||||
|
ClrBit(this->used_bitmap[index / BITMAP_SIZE], index % BITMAP_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1876,9 +1884,7 @@ UnitID GetFreeUnitNumber(VehicleType type)
|
||||||
const Company *c = Company::Get(_current_company);
|
const Company *c = Company::Get(_current_company);
|
||||||
if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
|
if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
|
||||||
|
|
||||||
FreeUnitIDGenerator gen(type, _current_company);
|
return c->freeunits[type].NextID();
|
||||||
|
|
||||||
return gen.NextID();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -755,7 +755,7 @@ public:
|
||||||
* and that shall not be resetted for the new vehicle.
|
* and that shall not be resetted for the new vehicle.
|
||||||
* @param src The old vehicle
|
* @param src The old vehicle
|
||||||
*/
|
*/
|
||||||
inline void CopyVehicleConfigAndStatistics(const Vehicle *src)
|
inline void CopyVehicleConfigAndStatistics(Vehicle *src)
|
||||||
{
|
{
|
||||||
this->CopyConsistPropertiesFrom(src);
|
this->CopyConsistPropertiesFrom(src);
|
||||||
|
|
||||||
|
@ -766,6 +766,8 @@ public:
|
||||||
|
|
||||||
this->profit_this_year = src->profit_this_year;
|
this->profit_this_year = src->profit_this_year;
|
||||||
this->profit_last_year = src->profit_last_year;
|
this->profit_last_year = src->profit_last_year;
|
||||||
|
|
||||||
|
src->unitnumber = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1270,19 +1272,6 @@ struct SpecializedVehicle : public Vehicle {
|
||||||
static Pool::IterateWrapper<T> Iterate(size_t from = 0) { return Pool::IterateWrapper<T>(from); }
|
static Pool::IterateWrapper<T> Iterate(size_t from = 0) { return Pool::IterateWrapper<T>(from); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Generates sequence of free UnitID numbers */
|
|
||||||
struct FreeUnitIDGenerator {
|
|
||||||
bool *cache; ///< array of occupied unit id numbers
|
|
||||||
UnitID maxid; ///< maximum ID at the moment of constructor call
|
|
||||||
UnitID curid; ///< last ID returned; 0 if none
|
|
||||||
|
|
||||||
FreeUnitIDGenerator(VehicleType type, CompanyID owner);
|
|
||||||
UnitID NextID();
|
|
||||||
|
|
||||||
/** Releases allocated memory */
|
|
||||||
~FreeUnitIDGenerator() { free(this->cache); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Sentinel for an invalid coordinate. */
|
/** Sentinel for an invalid coordinate. */
|
||||||
static const int32_t INVALID_COORD = 0x7fffffff;
|
static const int32_t INVALID_COORD = 0x7fffffff;
|
||||||
|
|
||||||
|
|
|
@ -194,6 +194,8 @@ std::tuple<CommandCost, VehicleID, uint, uint16_t, CargoArray> CmdBuildVehicle(D
|
||||||
GroupStatistics::CountVehicle(v, 1);
|
GroupStatistics::CountVehicle(v, 1);
|
||||||
if (!(subflags & DC_AUTOREPLACE)) OrderBackup::Restore(v, client_id);
|
if (!(subflags & DC_AUTOREPLACE)) OrderBackup::Restore(v, client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Company::Get(v->owner)->freeunits[v->type].UseID(v->unitnumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue