Improve performance of departures window

See: #119
pull/128/head
Jonathan G Rennison 5 years ago
parent abfb141afd
commit ec0b5e0ed0

@ -195,14 +195,14 @@ static void ScheduledDispatchDepartureLocalFix(DepartureList *departure_list)
/**
* Compute an up-to-date list of departures for a station.
* @param station the station to compute the departures of
* @param show_vehicle_types the types of vehicles to include in the departure list
* @param vehicles set of all the vehicles stopping at this station, of all vehicles types that we are interested in
* @param type the type of departures to get (departures or arrivals)
* @param show_vehicles_via whether to include vehicles that have this station in their orders but do not stop at it
* @param show_pax whether to include passenger vehicles
* @param show_freight whether to include freight vehicles
* @return a list of departures, which is empty if an error occurred
*/
DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], DepartureType type, bool show_vehicles_via, bool show_pax, bool show_freight)
DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehicle *> &vehicles, DepartureType type, bool show_vehicles_via, bool show_pax, bool show_freight)
{
/* This function is the meat of the departure boards functionality. */
/* As an overview, it works by repeatedly considering the best possible next departure to show. */
@ -231,24 +231,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
/* Cache for scheduled departure time */
schdispatch_cache_t schdispatch_last_planned_dispatch;
/* Get all the vehicles stopping at this station. */
/* We do this to get the order which is the first time they will stop at this station. */
/* This order is stored along with some more information. */
/* We keep a pointer to the `least' order (the one with the soonest expected completion time). */
for (uint i = 0; i < 4; ++i) {
VehicleList vehicles;
if (!show_vehicle_types[i]) {
/* Don't show vehicles whose type we're not interested in. */
continue;
}
/* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */
if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station))) {
/* Something went wrong: panic! */
return result;
}
{
/* Get the first order for each vehicle for the station we're interested in that doesn't have No Loading set. */
/* We find the least order while we're at it. */
for (const Vehicle *v : vehicles) {

@ -16,7 +16,9 @@
#include "core/smallvec_type.hpp"
#include "departures_type.h"
DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[4], DepartureType type = D_DEPARTURE,
#include <vector>
DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehicle *> &vehicles, DepartureType type = D_DEPARTURE,
bool show_vehicles_via = false, bool show_pax = true, bool show_freight = true);
#endif /* DEPARTURES_FUNC_H */

@ -74,9 +74,16 @@ static WindowDesc _departures_desc(
static uint cached_date_width = 0; ///< The cached maximum width required to display a date.
static uint cached_status_width = 0; ///< The cached maximum width required to show the status field.
static uint cached_date_arrow_width = 0; ///< The cached width of the red/green arrows that may be displayed alongside times.
static uint cached_veh_type_width = 0; ///< The cached width of the vehicle type icon.
static bool cached_date_display_method; ///< Whether the above cached values refers to original (d,m,y) dates or the 24h clock.
static bool cached_arr_dep_display_method; ///< Whether to show departures and arrivals on a single line.
void FlushDeparturesWindowTextCaches()
{
cached_date_width = cached_status_width = cached_date_arrow_width = cached_veh_type_width = 0;
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
}
template<bool Twaypoint = false>
struct DeparturesWindow : public Window {
protected:
@ -93,6 +100,10 @@ protected:
bool cargo_buttons_disabled;///< Show pax/freight buttons disabled
uint min_width; ///< The minimum width of this window.
Scrollbar *vscroll;
std::vector<const Vehicle *> vehicles; /// current set of vehicles
int veh_width; /// current width of vehicle field
int group_width; /// current width of group field
int toc_width; /// current width of company field
virtual uint GetMinWidth() const;
static void RecomputeDateWidth();
@ -122,6 +133,81 @@ protected:
}
}
void FillVehicleList()
{
this->vehicles.clear();
this->veh_width = 0;
this->group_width = 0;
this->toc_width = 0;
btree::btree_set<GroupID> groups;
CompanyMask companies = 0;
int unitnumber_max[4] = { -1, -1, -1, -1 };
const Vehicle *v;
FOR_ALL_VEHICLES(v) {
if (v->type < 4 && this->show_types[v->type] && v->IsPrimaryVehicle()) {
const Order *order;
FOR_VEHICLE_ORDERS(v, order) {
if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT) || order->IsType(OT_IMPLICIT))
&& order->GetDestination() == this->station) {
this->vehicles.push_back(v);
if (v->name == nullptr) {
if (v->unitnumber > unitnumber_max[v->type]) unitnumber_max[v->type] = v->unitnumber;
} else {
SetDParam(0, (uint64)(v->index));
int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width;
if (width > this->veh_width) this->veh_width = width;
}
if (v->group_id != INVALID_GROUP && v->group_id != DEFAULT_GROUP) {
groups.insert(v->group_id);
}
SetBit(companies, v->owner);
break;
}
}
}
}
for (uint i = 0; i < 4; i++) {
if (unitnumber_max[i] >= 0) {
uint unitnumber_digits = 2;
if (unitnumber_max[i] >= 10000) {
unitnumber_digits = 5;
} else if (unitnumber_max[i] >= 1000) {
unitnumber_digits = 4;
} else if (unitnumber_max[i] >= 100) {
unitnumber_digits = 3;
}
SetDParamMaxDigits(0, unitnumber_digits);
int width = (GetStringBoundingBox(STR_SV_TRAIN_NAME + i)).width;
if (width > this->veh_width) this->veh_width = width;
}
}
for (GroupID gid : groups) {
SetDParam(0, (uint64)gid);
int width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width;
if (width > this->group_width) this->group_width = width;
}
uint owner;
FOR_EACH_SET_BIT(owner, companies) {
SetDParam(0, owner);
int width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width;
if (width > this->toc_width) this->toc_width = width;
}
}
void RefreshVehicleList() {
this->FillVehicleList();
this->calc_tick_countdown = 0;
}
public:
DeparturesWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc),
@ -171,6 +257,12 @@ public:
this->LowerWidget(WID_DB_SHOW_VIA);
}
if (cached_veh_type_width == 0) {
cached_veh_type_width = GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE).width;
}
this->RefreshVehicleList();
if (_pause_mode != PM_UNPAUSED) this->OnGameTick();
}
@ -227,7 +319,7 @@ public:
this->SetWidgetDirty(widget);
}
/* We need to recompute the departures list. */
this->calc_tick_countdown = 0;
this->RefreshVehicleList();
/* We need to redraw the button that was pressed. */
if (_pause_mode != PM_UNPAUSED) this->OnGameTick();
break;
@ -349,8 +441,8 @@ public:
this->DeleteDeparturesList(this->arrivals);
bool show_pax = _settings_client.gui.departure_only_passengers ? true : this->show_pax;
bool show_freight = _settings_client.gui.departure_only_passengers ? false : this->show_freight;
this->departures = (this->departure_types[0] ? MakeDepartureList(this->station, this->show_types, D_DEPARTURE, Twaypoint || this->departure_types[2], show_pax, show_freight) : new DepartureList());
this->arrivals = (this->departure_types[1] && !_settings_client.gui.departure_show_both ? MakeDepartureList(this->station, this->show_types, D_ARRIVAL, false, show_pax, show_freight) : new DepartureList());
this->departures = (this->departure_types[0] ? MakeDepartureList(this->station, this->vehicles, D_DEPARTURE, Twaypoint || this->departure_types[2], show_pax, show_freight) : new DepartureList());
this->arrivals = (this->departure_types[1] && !_settings_client.gui.departure_show_both ? MakeDepartureList(this->station, this->vehicles, D_ARRIVAL, false, show_pax, show_freight) : new DepartureList());
this->SetWidgetDirty(WID_DB_LIST);
}
@ -400,6 +492,16 @@ public:
this->vscroll->SetCapacityFromWidget(this, WID_DB_LIST);
this->GetWidget<NWidgetCore>(WID_DB_LIST)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
}
/**
* Some data on this window has become invalid.
* @param data Information about the changed data.
* @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
*/
void OnInvalidateData(int data = 0, bool gui_scope = true) override
{
this->RefreshVehicleList();
}
};
/**
@ -459,49 +561,12 @@ uint DeparturesWindow<Twaypoint>::GetMinWidth() const
result = cached_date_width;
/* Vehicle type icon */
result += _settings_client.gui.departure_show_vehicle_type ? (GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE)).width : 0;
result += _settings_client.gui.departure_show_vehicle_type ? cached_veh_type_width : 0;
/* Status */
result += cached_status_width;
/* Find the maximum company name width. */
int toc_width = 0;
/* Find the maximum company name width. */
int group_width = 0;
/* Find the maximum vehicle name width. */
int veh_width = 0;
if (_settings_client.gui.departure_show_vehicle || _settings_client.gui.departure_show_company || _settings_client.gui.departure_show_group) {
for (uint i = 0; i < 4; ++i) {
VehicleList vehicles;
/* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */
if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station))) {
/* Something went wrong: panic! */
continue;
}
for (const Vehicle *v : vehicles) {
SetDParam(0, (uint64)(v->index));
int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width;
if (_settings_client.gui.departure_show_vehicle && width > veh_width) veh_width = width;
if (v->group_id != INVALID_GROUP && v->group_id != DEFAULT_GROUP) {
SetDParam(0, (uint64)(v->group_id));
width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width;
if (_settings_client.gui.departure_show_group && width > group_width) group_width = width;
}
SetDParam(0, (uint64)(v->owner));
width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width;
if (_settings_client.gui.departure_show_company && width > toc_width) toc_width = width;
}
}
}
result += toc_width + veh_width + group_width;
result += this->toc_width + this->veh_width + this->group_width;
return result + 140;
}
@ -588,7 +653,7 @@ void DeparturesWindow<Twaypoint>::DrawDeparturesListItems(const Rect &r) const
}
/* Vehicle type icon */
int type_width = _settings_client.gui.departure_show_vehicle_type ? (GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE)).width : 0;
int type_width = _settings_client.gui.departure_show_vehicle_type ? cached_veh_type_width : 0;
/* Find the maximum width of the status field */
int status_width = cached_status_width;
@ -597,41 +662,13 @@ void DeparturesWindow<Twaypoint>::DrawDeparturesListItems(const Rect &r) const
int calling_at_width = (GetStringBoundingBox(_settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LARGE : STR_DEPARTURES_CALLING_AT)).width;
/* Find the maximum company name width. */
int toc_width = 0;
int toc_width = _settings_client.gui.departure_show_company ? this->toc_width : 0;
/* Find the maximum group name width. */
int group_width = 0;
int group_width = _settings_client.gui.departure_show_group ? this->group_width : 0;
/* Find the maximum vehicle name width. */
int veh_width = 0;
if (_settings_client.gui.departure_show_vehicle || _settings_client.gui.departure_show_company || _settings_client.gui.departure_show_group) {
for (uint i = 0; i < 4; ++i) {
VehicleList vehicles;
/* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */
if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station))) {
/* Something went wrong: panic! */
continue;
}
for (const Vehicle *v : vehicles) {
SetDParam(0, (uint64)(v->index));
int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width;
if (_settings_client.gui.departure_show_vehicle && width > veh_width) veh_width = width;
if (v->group_id != INVALID_GROUP && v->group_id != DEFAULT_GROUP) {
SetDParam(0, (uint64)(v->group_id));
width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width;
if (_settings_client.gui.departure_show_group && width > group_width) group_width = width;
}
SetDParam(0, (uint64)(v->owner));
width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width;
if (_settings_client.gui.departure_show_company && width > toc_width) toc_width = width;
}
}
}
int veh_width = _settings_client.gui.departure_show_vehicle ? this->veh_width : 0;
uint departure = 0;
uint arrival = 0;

@ -1228,6 +1228,7 @@ void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord)
/* Make sure to rebuild the whole list */
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
}
/**
@ -1246,6 +1247,7 @@ static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags)
DeleteVehicleOrders(dst);
InvalidateVehicleOrder(dst, VIWD_REMOVE_ALL_ORDERS);
InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
CheckMarkDirtyFocusedRoutePaths(dst);
}
return CommandCost();
@ -1383,6 +1385,7 @@ void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord)
}
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
}
/**
@ -2056,6 +2059,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
CheckMarkDirtyFocusedRoutePaths(dst);
CheckAdvanceVehicleOrdersAfterClone(dst, flags);
@ -2159,6 +2163,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
InvalidateVehicleOrder(dst, VIWD_REMOVE_ALL_ORDERS);
InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
CheckMarkDirtyFocusedRoutePaths(dst);
CheckAdvanceVehicleOrdersAfterClone(dst, flags);

@ -41,6 +41,7 @@
#include "safeguards.h"
extern void FlushDeparturesWindowTextCaches();
static const StringID _driveside_dropdown[] = {
STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT,
@ -529,6 +530,7 @@ struct GameOptionsWindow : Window {
ClearAllCachedNames();
UpdateAllVirtCoords();
ReInitAllWindows();
FlushDeparturesWindowTextCaches();
break;
case WID_GO_RESOLUTION_DROPDOWN: // Change resolution
@ -544,6 +546,7 @@ struct GameOptionsWindow : Window {
UpdateAllVirtCoords();
FixTitleGameZoom();
ReInitAllWindows();
FlushDeparturesWindowTextCaches();
break;
case WID_GO_FONT_ZOOM_DROPDOWN:
@ -554,6 +557,7 @@ struct GameOptionsWindow : Window {
UpdateFontHeightCache();
LoadStringWidthTable();
UpdateAllVirtCoords();
FlushDeparturesWindowTextCaches();
break;
case WID_GO_BASE_GRF_DROPDOWN:

@ -1009,6 +1009,7 @@ void Vehicle::PreDestructor()
OrderBackup::ClearVehicle(this);
}
InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
this->cargo.Truncate();
DeleteVehicleOrders(this);

@ -168,6 +168,7 @@ CommandCost CmdBuildVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
if (flags & DC_EXEC) {
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
SetWindowDirty(WC_COMPANY, _current_company);
if (IsLocalCompany()) {
InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the auto replace window (must be called before incrementing num_engines)
@ -560,6 +561,7 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
if (!free_wagon) {
InvalidateWindowData(WC_VEHICLE_DETAILS, front->index);
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
}
/* virtual vehicles get their cargo changed by the TemplateCreateWindow, so set this dirty instead of a depot window */
if (HasBit(v->subtype, GVSF_VIRTUAL)) {
@ -1616,6 +1618,7 @@ CommandCost CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
free(v->name);
v->name = reset ? nullptr : stredup(text);
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 1);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
MarkWholeScreenDirty();
}

Loading…
Cancel
Save