From 3ac94e97c8eb05e2a767ce5c0267a268e9e12145 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 8 Jul 2016 19:07:25 +0100 Subject: [PATCH] Cache the result of GetImage() in Vehicle::UpdateViewport() where possible. Sprite number is not cached if callback is made, or a variable access outside a whitelist occurs. Invalidate cached sprite number when direction or cargo changes, or vehicle is marked dirty. --- src/aircraft_cmd.cpp | 1 + src/disaster_vehicle.cpp | 2 ++ src/ground_vehicle.cpp | 1 + src/newgrf_spritegroup.cpp | 44 ++++++++++++++++++++++++++++++++++++++ src/roadveh_cmd.cpp | 1 + src/ship_cmd.cpp | 1 + src/train_cmd.cpp | 3 +++ src/vehicle.cpp | 1 + src/vehicle_base.h | 12 ++++++++++- 9 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index bc1ca00711..4a1bde888c 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -1293,6 +1293,7 @@ TileIndex Aircraft::GetOrderStationLocation(StationID station) void Aircraft::MarkDirty() { this->colourmap = PAL_NONE; + this->cur_image_valid_dir = INVALID_DIR; this->UpdateViewport(true, false); if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP); } diff --git a/src/disaster_vehicle.cpp b/src/disaster_vehicle.cpp index 08958d5bb8..5fa20baed7 100644 --- a/src/disaster_vehicle.cpp +++ b/src/disaster_vehicle.cpp @@ -186,6 +186,7 @@ void DisasterVehicle::UpdatePosition(int x, int y, int z) this->z_pos = z; this->tile = TileVirtXY(x, y); + this->cur_image_valid_dir = INVALID_DIR; this->UpdateImage(); this->UpdatePositionAndViewport(); @@ -199,6 +200,7 @@ void DisasterVehicle::UpdatePosition(int x, int y, int z) safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE); u->z_pos = GetSlopePixelZ(safe_x, safe_y); u->direction = this->direction; + u->cur_image_valid_dir = INVALID_DIR; u->UpdateImage(); u->UpdatePositionAndViewport(); diff --git a/src/ground_vehicle.cpp b/src/ground_vehicle.cpp index 91d1900558..e6bc103c46 100644 --- a/src/ground_vehicle.cpp +++ b/src/ground_vehicle.cpp @@ -108,6 +108,7 @@ void GroundVehicle::CargoChanged() weight += current_weight; /* Slope steepness is in percent, result in N. */ u->gcache.cached_slope_resistance = current_weight * u->GetSlopeSteepness() * 100; + u->cur_image_valid_dir = INVALID_DIR; } /* Store consist weight in cache. */ diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 624b4d5474..997cbb6465 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -237,6 +237,8 @@ static U EvalAdjustT(const DeterministicSpriteGroupAdjust *adjust, ScopeResolver } } +bool _sprite_group_resolve_check_veh_enable = false; +bool _sprite_group_resolve_check_veh_result = false; const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) const { @@ -252,6 +254,7 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con /* Try to get the variable. We shall assume it is available, unless told otherwise. */ bool available = true; if (adjust->variable == 0x7E) { + _sprite_group_resolve_check_veh_result = false; const SpriteGroup *subgroup = SpriteGroup::Resolve(adjust->subroutine, object, false); if (subgroup == NULL) { value = CALLBACK_FAILED; @@ -261,8 +264,49 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con /* Note: 'last_value' and 'reseed' are shared between the main chain and the procedure */ } else if (adjust->variable == 0x7B) { + _sprite_group_resolve_check_veh_result = false; value = GetVariable(object, scope, adjust->parameter, last_value, &available); } else { + if (_sprite_group_resolve_check_veh_enable) { + switch (adjust->variable) { + // whitelist of variables which can be checked without requiring an immediate re-check on the next tick + case 0xC: + case 0x1A: + case 0x1C: + case 0x25: + case 0x40: + case 0x41: + case 0x42: + case 0x47: + case 0x49: + case 0x4B: + case 0x4D: + case 0x60: + case 0x7D: + case 0x7F: + case 0x80 + 0x0: + case 0x80 + 0x1: + case 0x80 + 0x4: + case 0x80 + 0x5: + case 0x80 + 0x39: + case 0x80 + 0x3A: + case 0x80 + 0x3B: + case 0x80 + 0x3C: + case 0x80 + 0x3D: + case 0x80 + 0x44: + case 0x80 + 0x45: + case 0x80 + 0x46: + case 0x80 + 0x47: + case 0x80 + 0x5A: + case 0x80 + 0x72: + case 0x80 + 0x7A: + break; + + default: + _sprite_group_resolve_check_veh_result = false; + break; + } + } value = GetVariable(object, scope, adjust->variable, adjust->parameter, &available); } diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index a15bcfbbe7..e16956c63f 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -408,6 +408,7 @@ void RoadVehicle::MarkDirty() { for (RoadVehicle *v = this; v != NULL; v = v->Next()) { v->colourmap = PAL_NONE; + v->cur_image_valid_dir = INVALID_DIR; v->UpdateViewport(true, false); } this->CargoChanged(); diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index daf19687dd..b77769b01a 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -259,6 +259,7 @@ Trackdir Ship::GetVehicleTrackdir() const void Ship::MarkDirty() { this->colourmap = PAL_NONE; + this->cur_image_valid_dir = INVALID_DIR; this->UpdateViewport(true, false); this->UpdateCache(); } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 5653484332..14493f187a 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1638,6 +1638,8 @@ static void SwapTrainFlags(uint16 *swap_flag1, uint16 *swap_flag2) */ static void UpdateStatusAfterSwap(Train *v) { + v->cur_image_valid_dir = INVALID_DIR; + /* Reverse the direction. */ if (v->track != TRACK_BIT_DEPOT) v->direction = ReverseDir(v->direction); @@ -3052,6 +3054,7 @@ void Train::MarkDirty() Train *v = this; do { v->colourmap = PAL_NONE; + v->cur_image_valid_dir = INVALID_DIR; v->UpdateViewport(true, false); } while ((v = v->Next()) != NULL); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index ca03a22ab4..f5ba0d297b 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -367,6 +367,7 @@ Vehicle::Vehicle(VehicleType type) this->cargo_age_counter = 1; this->last_station_visited = INVALID_STATION; this->last_loading_station = INVALID_STATION; + this->cur_image_valid_dir = INVALID_DIR; } /** diff --git a/src/vehicle_base.h b/src/vehicle_base.h index c136eee6ef..6c5264a054 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -291,6 +291,7 @@ public: uint16 load_unload_ticks; ///< Ticks to wait before starting next cycle. GroupID group_id; ///< Index of group Pool array byte subtype; ///< subtype (Filled with values from #EffectVehicles/#TrainSubTypes/#AircraftSubTypes) + DirectionByte cur_image_valid_dir; ///< NOSAVE: direction for which cur_image does not need to be regenerated on the next tick NewGRFCache grf_cache; ///< Cache of often used calculated NewGRF values VehicleCache vcache; ///< Cache of often used vehicle values. @@ -1158,11 +1159,20 @@ struct SpecializedVehicle : public Vehicle { /* Skip updating sprites on dedicated servers without screen */ if (_network_dedicated) return; + extern bool _sprite_group_resolve_check_veh_enable; + extern bool _sprite_group_resolve_check_veh_result; + /* Explicitly choose method to call to prevent vtable dereference - * it gives ~3% runtime improvements in games with many vehicles */ if (update_delta) ((T *)this)->T::UpdateDeltaXY(this->direction); SpriteID old_image = this->cur_image; - this->cur_image = ((T *)this)->T::GetImage(this->direction, EIT_ON_MAP); + if (this->cur_image_valid_dir != this->direction) { + _sprite_group_resolve_check_veh_enable = true; + _sprite_group_resolve_check_veh_result = true; + this->cur_image = ((T *)this)->T::GetImage(this->direction, EIT_ON_MAP); + this->cur_image_valid_dir = _sprite_group_resolve_check_veh_result ? this->direction : INVALID_DIR; + _sprite_group_resolve_check_veh_enable = false; + } if (force_update || this->cur_image != old_image) this->Vehicle::UpdateViewport(true); } };