mirror of https://github.com/OpenTTD/OpenTTD.git
Feature: Framerate display window (#6822)
Frame rate and various game loop/graphics timing measurements and graphs. Accessible via the Help menu, and can print some stats in the console via the fps command.
This commit is contained in:
parent
a3d1950b65
commit
2a868b9f3b
65
README.md
65
README.md
|
@ -14,6 +14,7 @@
|
|||
- 4.4) [Files in tar (archives)](#44-files-in-tar-archives)
|
||||
- 5.0) [OpenTTD features](#50-openttd-features)
|
||||
- 5.1) [Logging of potentially dangerous actions](#51-logging-of-potentially-dangerous-actions)
|
||||
- 5.2) [Frame rate and performance metrics](#52-frame-rate-and-performance-metrics)
|
||||
- 6.0) [Configuration file](#60-configuration-file)
|
||||
- 7.0) [Compiling](#70-compiling)
|
||||
- 7.1) [Required/optional libraries](#71-requiredoptional-libraries)
|
||||
|
@ -433,6 +434,70 @@ No personal information is stored.
|
|||
You can show the game log by typing 'gamelog' in the console or by running
|
||||
OpenTTD in debug mode.
|
||||
|
||||
### 5.2) Frame rate and performance metrics
|
||||
|
||||
The Help menu in-game has a function to open the Frame rate window. This
|
||||
window shows various real-time performance statistics, measuring what parts
|
||||
of the game require the most processing power currently.
|
||||
|
||||
A summary of the statistics can also be retrieved from the console with the
|
||||
`fps` command. This is especially useful on dedicated servers, where the
|
||||
administrator might want to determine what's limiting performance in a slow
|
||||
game.
|
||||
|
||||
The frame rate is given as two figures, the simulation rate and the graphics
|
||||
frame rate. Usually these are identical, as the screen is rendered exactly
|
||||
once per simulated tick, but in the future there might be support for graphics
|
||||
and simulation running at different rates. When the game is paused, the
|
||||
simulation rate drops to zero.
|
||||
|
||||
In addition to the simulation rate, a game speed factor is also calculated.
|
||||
This is based on the target simulation speed, which is 30 milliseconds per
|
||||
game tick. At that speed, the expected frame rate is 33.33 frames/second, and
|
||||
the game speed factor is how close to that target the actual rate is. When
|
||||
the game is in fast forward mode, the game speed factor shows how much
|
||||
speed up is achieved.
|
||||
|
||||
The lower part of the window shows timing statistics for individual parts of
|
||||
the game. The times shown are short-term and long-term averages of how long
|
||||
it takes to process one tick of game time, all figures are in milliseconds.
|
||||
|
||||
Clicking a line in the lower part of the window opens a graph window, giving
|
||||
detailed readings on each tick simulated by the game.
|
||||
|
||||
The following is an explanation of the different statistics:
|
||||
|
||||
- *Game loop* - Total processing time used per simulated "tick" in the game.
|
||||
This includes all pathfinding, world updates, and economy handling.
|
||||
- *Cargo handling* - Time spent loading/unloading cargo at stations, and
|
||||
industries and towns sending/retrieving cargo from stations.
|
||||
- *Train ticks*, *Road vehicle ticks*, *Ship ticks*, *Aircraft ticks* -
|
||||
Time spent on pathfinding and other processing for each player vehicle type.
|
||||
- *World ticks* - Time spent on other world/landscape processing. This
|
||||
includes towns growing, building animations, updates of farmland and trees,
|
||||
and station rating updates.
|
||||
- *Link graph delay* - Time overruns of the cargo distribution link graph
|
||||
update thread. Usually the link graph is updated in a background thread,
|
||||
but these updates need to synchronise with the main game loop occasionally,
|
||||
if the time spent on link graph updates is longer than the time taken to
|
||||
otherwise simulate the game while it was updating, these delays are counted
|
||||
in this figure.
|
||||
- *Graphics rendering* - Total time spent rendering all graphics, including
|
||||
both GUI and world viewports. This typically spikes when panning the view
|
||||
around, and when more things are happening on screen at once.
|
||||
- *World viewport rendering* - Isolated time spent rendering just world
|
||||
viewports. If this figure is significantly lower than the total graphics
|
||||
rendering time, most time is spent rendering GUI than rendering world.
|
||||
- *Video output* - Speed of copying the rendered graphics to the display
|
||||
adapter. Usually this should be very fast (in the range of 0-3 ms), large
|
||||
values for this can indicate a graphics driver problem.
|
||||
- *Sound mixing* - Speed of mixing active audio samples together. Usually
|
||||
this should be very fast (in the range of 0-3 ms), if it is slow, consider
|
||||
switching to the NoSound set.
|
||||
|
||||
If the frame rate window is shaded, the title bar will instead show just the
|
||||
current simulation rate and the game speed factor.
|
||||
|
||||
## 6.0) Configuration file
|
||||
|
||||
The configuration file for OpenTTD (openttd.cfg) is in a simple Windows-like
|
||||
|
|
|
@ -481,6 +481,7 @@
|
|||
<ClInclude Include="..\src\fios.h" />
|
||||
<ClInclude Include="..\src\fontcache.h" />
|
||||
<ClInclude Include="..\src\fontdetection.h" />
|
||||
<ClInclude Include="..\src\framerate_type.h" />
|
||||
<ClInclude Include="..\src\base_consist.h" />
|
||||
<ClInclude Include="..\src\gamelog.h" />
|
||||
<ClInclude Include="..\src\gamelog_internal.h" />
|
||||
|
@ -731,6 +732,7 @@
|
|||
<ClCompile Include="..\src\engine_gui.cpp" />
|
||||
<ClCompile Include="..\src\error_gui.cpp" />
|
||||
<ClCompile Include="..\src\fios_gui.cpp" />
|
||||
<ClCompile Include="..\src\framerate_gui.cpp" />
|
||||
<ClCompile Include="..\src\genworld_gui.cpp" />
|
||||
<ClCompile Include="..\src\goal_gui.cpp" />
|
||||
<ClCompile Include="..\src\graph_gui.cpp" />
|
||||
|
|
|
@ -600,6 +600,9 @@
|
|||
<ClInclude Include="..\src\fontdetection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\framerate_type.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\base_consist.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1350,6 +1353,9 @@
|
|||
<ClCompile Include="..\src\fios_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\framerate_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\genworld_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -502,6 +502,7 @@
|
|||
<ClInclude Include="..\src\fios.h" />
|
||||
<ClInclude Include="..\src\fontcache.h" />
|
||||
<ClInclude Include="..\src\fontdetection.h" />
|
||||
<ClInclude Include="..\src\framerate_type.h" />
|
||||
<ClInclude Include="..\src\base_consist.h" />
|
||||
<ClInclude Include="..\src\gamelog.h" />
|
||||
<ClInclude Include="..\src\gamelog_internal.h" />
|
||||
|
@ -752,6 +753,7 @@
|
|||
<ClCompile Include="..\src\engine_gui.cpp" />
|
||||
<ClCompile Include="..\src\error_gui.cpp" />
|
||||
<ClCompile Include="..\src\fios_gui.cpp" />
|
||||
<ClCompile Include="..\src\framerate_gui.cpp" />
|
||||
<ClCompile Include="..\src\genworld_gui.cpp" />
|
||||
<ClCompile Include="..\src\goal_gui.cpp" />
|
||||
<ClCompile Include="..\src\graph_gui.cpp" />
|
||||
|
|
|
@ -600,6 +600,9 @@
|
|||
<ClInclude Include="..\src\fontdetection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\framerate_type.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\base_consist.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1350,6 +1353,9 @@
|
|||
<ClCompile Include="..\src\fios_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\framerate_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\genworld_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -502,6 +502,7 @@
|
|||
<ClInclude Include="..\src\fios.h" />
|
||||
<ClInclude Include="..\src\fontcache.h" />
|
||||
<ClInclude Include="..\src\fontdetection.h" />
|
||||
<ClInclude Include="..\src\framerate_type.h" />
|
||||
<ClInclude Include="..\src\base_consist.h" />
|
||||
<ClInclude Include="..\src\gamelog.h" />
|
||||
<ClInclude Include="..\src\gamelog_internal.h" />
|
||||
|
@ -752,6 +753,7 @@
|
|||
<ClCompile Include="..\src\engine_gui.cpp" />
|
||||
<ClCompile Include="..\src\error_gui.cpp" />
|
||||
<ClCompile Include="..\src\fios_gui.cpp" />
|
||||
<ClCompile Include="..\src\framerate_gui.cpp" />
|
||||
<ClCompile Include="..\src\genworld_gui.cpp" />
|
||||
<ClCompile Include="..\src\goal_gui.cpp" />
|
||||
<ClCompile Include="..\src\graph_gui.cpp" />
|
||||
|
|
|
@ -600,6 +600,9 @@
|
|||
<ClInclude Include="..\src\fontdetection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\framerate_type.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\base_consist.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1350,6 +1353,9 @@
|
|||
<ClCompile Include="..\src\fios_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\framerate_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\genworld_gui.cpp">
|
||||
<Filter>GUI Source Code</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -1110,6 +1110,10 @@
|
|||
RelativePath=".\..\src\fontdetection.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\framerate_type.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\base_consist.h"
|
||||
>
|
||||
|
@ -2118,6 +2122,10 @@
|
|||
RelativePath=".\..\src\fios_gui.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\framerate_gui.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\genworld_gui.cpp"
|
||||
>
|
||||
|
|
|
@ -1107,6 +1107,10 @@
|
|||
RelativePath=".\..\src\fontdetection.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\framerate_type.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\base_consist.h"
|
||||
>
|
||||
|
@ -2115,6 +2119,10 @@
|
|||
RelativePath=".\..\src\fios_gui.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\framerate_gui.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\genworld_gui.cpp"
|
||||
>
|
||||
|
|
|
@ -193,6 +193,7 @@ fileio_type.h
|
|||
fios.h
|
||||
fontcache.h
|
||||
fontdetection.h
|
||||
framerate_type.h
|
||||
base_consist.h
|
||||
gamelog.h
|
||||
gamelog_internal.h
|
||||
|
@ -462,6 +463,7 @@ dock_gui.cpp
|
|||
engine_gui.cpp
|
||||
error_gui.cpp
|
||||
fios_gui.cpp
|
||||
framerate_gui.cpp
|
||||
genworld_gui.cpp
|
||||
goal_gui.cpp
|
||||
graph_gui.cpp
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "core/backup_type.hpp"
|
||||
#include "zoom_func.h"
|
||||
#include "disaster_vehicle.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -2038,6 +2039,8 @@ bool Aircraft::Tick()
|
|||
{
|
||||
if (!this->IsNormalAircraft()) return true;
|
||||
|
||||
PerformanceAccumulator framerate(PFE_GL_AIRCRAFT);
|
||||
|
||||
this->tick_counter++;
|
||||
|
||||
if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "core/smallvec_type.hpp"
|
||||
#include "tile_cmd.h"
|
||||
#include "viewport_func.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
@ -50,6 +51,8 @@ void AddAnimatedTile(TileIndex tile)
|
|||
*/
|
||||
void AnimateAnimatedTiles()
|
||||
{
|
||||
PerformanceAccumulator framerate(PFE_GL_LANDSCAPE);
|
||||
|
||||
const TileIndex *ti = _animated_tiles.Begin();
|
||||
while (ti < _animated_tiles.End()) {
|
||||
const TileIndex curr = *ti;
|
||||
|
|
|
@ -1895,6 +1895,37 @@ static void IConsoleDebugLibRegister()
|
|||
}
|
||||
#endif
|
||||
|
||||
DEF_CONSOLE_CMD(ConFramerate)
|
||||
{
|
||||
extern void ConPrintFramerate(); // framerate_gui.cpp
|
||||
|
||||
if (argc == 0) {
|
||||
IConsoleHelp("Show frame rate and game speed information");
|
||||
return true;
|
||||
}
|
||||
|
||||
ConPrintFramerate();
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_CONSOLE_CMD(ConFramerateWindow)
|
||||
{
|
||||
extern void ShowFramerateWindow();
|
||||
|
||||
if (argc == 0) {
|
||||
IConsoleHelp("Open the frame rate window");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_network_dedicated) {
|
||||
IConsoleError("Can not open frame rate window on a dedicated server");
|
||||
return false;
|
||||
}
|
||||
|
||||
ShowFramerateWindow();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
* console command registration
|
||||
*******************************/
|
||||
|
@ -2025,6 +2056,8 @@ void IConsoleStdLibRegister()
|
|||
#ifdef _DEBUG
|
||||
IConsoleDebugLibRegister();
|
||||
#endif
|
||||
IConsoleCmdRegister("fps", ConFramerate);
|
||||
IConsoleCmdRegister("fps_wnd", ConFramerateWindow);
|
||||
|
||||
/* NewGRF development stuff */
|
||||
IConsoleCmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool);
|
||||
|
|
|
@ -0,0 +1,837 @@
|
|||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file framerate_gui.cpp GUI for displaying framerate/game speed information. */
|
||||
|
||||
#include "framerate_type.h"
|
||||
#include <chrono>
|
||||
#include "gfx_func.h"
|
||||
#include "window_gui.h"
|
||||
#include "table/sprites.h"
|
||||
#include "strings_func.h"
|
||||
#include "debug.h"
|
||||
#include "console_func.h"
|
||||
#include "console_type.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
/** Number of data points to keep in buffer for each performance measurement */
|
||||
const int NUM_FRAMERATE_POINTS = 512;
|
||||
/** Units a second is divided into in performance measurements */
|
||||
const TimingMeasurement TIMESTAMP_PRECISION = 1000000;
|
||||
|
||||
struct PerformanceData {
|
||||
/** Duration value indicating the value is not valid should be considered a gap in measurements */
|
||||
static const TimingMeasurement INVALID_DURATION = UINT64_MAX;
|
||||
|
||||
/** Time spent processing each cycle of the performance element, circular buffer */
|
||||
TimingMeasurement durations[NUM_FRAMERATE_POINTS];
|
||||
/** Start time of each cycle of the performance element, circular buffer */
|
||||
TimingMeasurement timestamps[NUM_FRAMERATE_POINTS];
|
||||
/** Expected number of cycles per second when the system is running without slowdowns */
|
||||
double expected_rate;
|
||||
/** Next index to write to in \c durations and \c timestamps */
|
||||
int next_index;
|
||||
/** Last index written to in \c durations and \c timestamps */
|
||||
int prev_index;
|
||||
/** Number of data points recorded, clamped to \c NUM_FRAMERATE_POINTS */
|
||||
int num_valid;
|
||||
|
||||
/** Current accumulated duration */
|
||||
TimingMeasurement acc_duration;
|
||||
/** Start time for current accumulation cycle */
|
||||
TimingMeasurement acc_timestamp;
|
||||
|
||||
explicit PerformanceData(double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
|
||||
|
||||
void Add(TimingMeasurement start_time, TimingMeasurement end_time)
|
||||
{
|
||||
this->durations[this->next_index] = end_time - start_time;
|
||||
this->timestamps[this->next_index] = start_time;
|
||||
this->prev_index = this->next_index;
|
||||
this->next_index += 1;
|
||||
if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
|
||||
this->num_valid = min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
|
||||
}
|
||||
|
||||
void BeginAccumulate(TimingMeasurement start_time)
|
||||
{
|
||||
this->timestamps[this->next_index] = this->acc_timestamp;
|
||||
this->durations[this->next_index] = this->acc_duration;
|
||||
this->prev_index = this->next_index;
|
||||
this->next_index += 1;
|
||||
if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
|
||||
this->num_valid = min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
|
||||
|
||||
this->acc_duration = 0;
|
||||
this->acc_timestamp = start_time;
|
||||
}
|
||||
|
||||
void AddAccumulate(TimingMeasurement duration)
|
||||
{
|
||||
this->acc_duration += duration;
|
||||
}
|
||||
|
||||
void AddPause(TimingMeasurement start_time)
|
||||
{
|
||||
if (this->durations[this->prev_index] != INVALID_DURATION) {
|
||||
this->timestamps[this->next_index] = start_time;
|
||||
this->durations[this->next_index] = INVALID_DURATION;
|
||||
this->prev_index = this->next_index;
|
||||
this->next_index += 1;
|
||||
if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
|
||||
this->num_valid += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get average cycle processing time over a number of data points */
|
||||
double GetAverageDurationMilliseconds(int count)
|
||||
{
|
||||
count = min(count, this->num_valid);
|
||||
|
||||
int first_point = this->prev_index - count;
|
||||
if (first_point < 0) first_point += NUM_FRAMERATE_POINTS;
|
||||
|
||||
/* Sum durations, skipping invalid points */
|
||||
double sumtime = 0;
|
||||
for (int i = first_point; i < first_point + count; i++) {
|
||||
auto d = this->durations[i % NUM_FRAMERATE_POINTS];
|
||||
if (d != INVALID_DURATION) {
|
||||
sumtime += d;
|
||||
} else {
|
||||
/* Don't count the invalid durations */
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) return 0; // avoid div by zero
|
||||
return sumtime * 1000 / count / TIMESTAMP_PRECISION;
|
||||
}
|
||||
|
||||
/** Get current rate of a performance element, based on approximately the past one second of data */
|
||||
double GetRate()
|
||||
{
|
||||
/* Start at last recorded point, end at latest when reaching the earliest recorded point */
|
||||
int point = this->prev_index;
|
||||
int last_point = this->next_index - this->num_valid;
|
||||
if (last_point < 0) last_point += NUM_FRAMERATE_POINTS;
|
||||
|
||||
/** Number of data points collected */
|
||||
int count = 0;
|
||||
/** Time of previous data point */
|
||||
TimingMeasurement last = this->timestamps[point];
|
||||
/** Total duration covered by collected points */
|
||||
TimingMeasurement total = 0;
|
||||
|
||||
while (point != last_point) {
|
||||
/* Only record valid data points, but pretend the gaps in measurements aren't there */
|
||||
if (this->durations[point] != INVALID_DURATION) {
|
||||
total += last - this->timestamps[point];
|
||||
count++;
|
||||
}
|
||||
last = this->timestamps[point];
|
||||
if (total >= TIMESTAMP_PRECISION) break; // end after 1 second has been collected
|
||||
point--;
|
||||
if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
|
||||
}
|
||||
|
||||
if (total == 0 || count == 0) return 0;
|
||||
return (double)count * TIMESTAMP_PRECISION / total;
|
||||
}
|
||||
};
|
||||
|
||||
/** Game loop rate, cycles per second */
|
||||
static const double GL_RATE = 1000.0 / MILLISECONDS_PER_TICK;
|
||||
|
||||
PerformanceData _pf_data[PFE_MAX] = {
|
||||
PerformanceData(GL_RATE), // PFE_GAMELOOP
|
||||
PerformanceData(1), // PFE_ACC_GL_ECONOMY
|
||||
PerformanceData(1), // PFE_ACC_GL_TRAINS
|
||||
PerformanceData(1), // PFE_ACC_GL_ROADVEHS
|
||||
PerformanceData(1), // PFE_ACC_GL_SHIPS
|
||||
PerformanceData(1), // PFE_ACC_GL_AIRCRAFT
|
||||
PerformanceData(1), // PFE_GL_LANDSCAPE
|
||||
PerformanceData(1), // PFE_GL_LINKGRAPH
|
||||
PerformanceData(GL_RATE), // PFE_DRAWING
|
||||
PerformanceData(1), // PFE_ACC_DRAWWORLD
|
||||
PerformanceData(60.0), // PFE_VIDEO
|
||||
PerformanceData(1000.0 * 8192 / 44100), // PFE_SOUND
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a timestamp with \c TIMESTAMP_PRECISION ticks per second precision.
|
||||
* The basis of the timestamp is implementation defined, but the value should be steady,
|
||||
* so differences can be taken to reliably measure intervals.
|
||||
*/
|
||||
static TimingMeasurement GetPerformanceTimer()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return (TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
|
||||
}
|
||||
|
||||
|
||||
/** Begin a cycle of a measured element. */
|
||||
PerformanceMeasurer::PerformanceMeasurer(PerformanceElement elem)
|
||||
{
|
||||
assert(elem < PFE_MAX);
|
||||
|
||||
this->elem = elem;
|
||||
this->start_time = GetPerformanceTimer();
|
||||
}
|
||||
|
||||
/** Finish a cycle of a measured element and store the measurement taken. */
|
||||
PerformanceMeasurer::~PerformanceMeasurer()
|
||||
{
|
||||
_pf_data[this->elem].Add(this->start_time, GetPerformanceTimer());
|
||||
}
|
||||
|
||||
/** Set the rate of expected cycles per second of a performance element. */
|
||||
void PerformanceMeasurer::SetExpectedRate(double rate)
|
||||
{
|
||||
_pf_data[this->elem].expected_rate = rate;
|
||||
}
|
||||
|
||||
/** Indicate that a cycle of "pause" where no processing occurs. */
|
||||
void PerformanceMeasurer::Paused(PerformanceElement elem)
|
||||
{
|
||||
_pf_data[elem].AddPause(GetPerformanceTimer());
|
||||
}
|
||||
|
||||
|
||||
/** Begin measuring one block of the accumulating value. */
|
||||
PerformanceAccumulator::PerformanceAccumulator(PerformanceElement elem)
|
||||
{
|
||||
assert(elem < PFE_MAX);
|
||||
|
||||
this->elem = elem;
|
||||
this->start_time = GetPerformanceTimer();
|
||||
}
|
||||
|
||||
/** Finish and add one block of the accumulating value. */
|
||||
PerformanceAccumulator::~PerformanceAccumulator()
|
||||
{
|
||||
_pf_data[this->elem].AddAccumulate(GetPerformanceTimer() - this->start_time);
|
||||
}
|
||||
|
||||
/** Store the previous accumulator value and reset for a new cycle of accumulating measurements. */
|
||||
void PerformanceAccumulator::Reset(PerformanceElement elem)
|
||||
{
|
||||
_pf_data[elem].BeginAccumulate(GetPerformanceTimer());
|
||||
}
|
||||
|
||||
|
||||
void ShowFrametimeGraphWindow(PerformanceElement elem);
|
||||
|
||||
|
||||
enum FramerateWindowWidgets {
|
||||
WID_FRW_CAPTION,
|
||||
WID_FRW_RATE_GAMELOOP,
|
||||
WID_FRW_RATE_DRAWING,
|
||||
WID_FRW_RATE_FACTOR,
|
||||
WID_FRW_INFO_DATA_POINTS,
|
||||
WID_FRW_TIMES_NAMES,
|
||||
WID_FRW_TIMES_CURRENT,
|
||||
WID_FRW_TIMES_AVERAGE,
|
||||
};
|
||||
|
||||
static const NWidgetPart _framerate_window_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_FRW_CAPTION), SetDataTip(STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
NWidget(WWT_SHADEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_STICKYBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(NWID_VERTICAL), SetPadding(6), SetPIP(0, 3, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_GAMELOOP), SetDataTip(STR_FRAMERATE_RATE_GAMELOOP, STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_DRAWING), SetDataTip(STR_FRAMERATE_RATE_BLITTER, STR_FRAMERATE_RATE_BLITTER_TOOLTIP),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_FACTOR), SetDataTip(STR_FRAMERATE_SPEED_FACTOR, STR_FRAMERATE_SPEED_FACTOR_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(NWID_VERTICAL), SetPadding(6), SetPIP(0, 3, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, 6, 0),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_NAMES),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_CURRENT),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_AVERAGE),
|
||||
EndContainer(),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_INFO_DATA_POINTS), SetDataTip(STR_FRAMERATE_DATA_POINTS, 0x0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
struct FramerateWindow : Window {
|
||||
bool small;
|
||||
uint32 next_update;
|
||||
|
||||
struct CachedDecimal {
|
||||
StringID strid;
|
||||
uint32 value;
|
||||
|
||||
inline void SetRate(double value, double target)
|
||||
{
|
||||
const double threshold_good = target * 0.95;
|
||||
const double threshold_bad = target * 2 / 3;
|
||||
value = min(9999.99, value);
|
||||
this->value = (uint32)(value * 100);
|
||||
this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
|
||||
}
|
||||
|
||||
inline void SetTime(double value, double target)
|
||||
{
|
||||
const double threshold_good = target / 3;
|
||||
const double threshold_bad = target;
|
||||
value = min(9999.99, value);
|
||||
this->value = (uint32)(value * 100);
|
||||
this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
|
||||
}
|
||||
|
||||
inline void InsertDParams(uint n) const
|
||||
{
|
||||
SetDParam(n, this->value);
|
||||
SetDParam(n + 1, 2);
|
||||
}
|
||||
};
|
||||
|
||||
CachedDecimal rate_gameloop; ///< cached game loop tick rate
|
||||
CachedDecimal rate_drawing; ///< cached drawing frame rate
|
||||
CachedDecimal speed_gameloop; ///< cached game loop speed factor
|
||||
CachedDecimal times_shortterm[PFE_MAX]; ///< cached short term average times
|
||||
CachedDecimal times_longterm[PFE_MAX]; ///< cached long term average times
|
||||
|
||||
static const int VSPACING = 3; ///< space between column heading and values
|
||||
|
||||
FramerateWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
|
||||
{
|
||||
this->InitNested(number);
|
||||
this->small = this->IsShaded();
|
||||
this->UpdateData();
|
||||
}
|
||||
|
||||
virtual void OnTick()
|
||||
{
|
||||
/* Check if the shaded state has changed, switch caption text if it has */
|
||||
if (this->small != this->IsShaded()) {
|
||||
this->small = this->IsShaded();
|
||||
this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
|
||||
this->next_update = 0;
|
||||
}
|
||||
|
||||
if (_realtime_tick >= this->next_update) {
|
||||
this->UpdateData();
|
||||
this->SetDirty();
|
||||
this->next_update = _realtime_tick + 100;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateData()
|
||||
{
|
||||
double gl_rate = _pf_data[PFE_GAMELOOP].GetRate();
|
||||
this->rate_gameloop.SetRate(gl_rate, _pf_data[PFE_GAMELOOP].expected_rate);
|
||||
this->speed_gameloop.SetRate(gl_rate / _pf_data[PFE_GAMELOOP].expected_rate, 1.0);
|
||||
if (this->small) return; // in small mode, this is everything needed
|
||||
|
||||
this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _pf_data[PFE_DRAWING].expected_rate);
|
||||
|
||||
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
|
||||
this->times_shortterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(8), MILLISECONDS_PER_TICK);
|
||||
this->times_longterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(NUM_FRAMERATE_POINTS), MILLISECONDS_PER_TICK);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetStringParameters(int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FRW_CAPTION:
|
||||
/* When the window is shaded, the caption shows game loop rate and speed factor */
|
||||
if (!this->small) break;
|
||||
SetDParam(0, this->rate_gameloop.strid);
|
||||
this->rate_gameloop.InsertDParams(1);
|
||||
this->speed_gameloop.InsertDParams(3);
|
||||
break;
|
||||
|
||||
case WID_FRW_RATE_GAMELOOP:
|
||||
SetDParam(0, this->rate_gameloop.strid);
|
||||
this->rate_gameloop.InsertDParams(1);
|
||||
break;
|
||||
case WID_FRW_RATE_DRAWING:
|
||||
SetDParam(0, this->rate_drawing.strid);
|
||||
this->rate_drawing.InsertDParams(1);
|
||||
break;
|
||||
case WID_FRW_RATE_FACTOR:
|
||||
this->speed_gameloop.InsertDParams(0);
|
||||
break;
|
||||
case WID_FRW_INFO_DATA_POINTS:
|
||||
SetDParam(0, NUM_FRAMERATE_POINTS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FRW_RATE_GAMELOOP:
|
||||
SetDParam(0, STR_FRAMERATE_FPS_GOOD);
|
||||
SetDParam(1, 999999);
|
||||
SetDParam(2, 2);
|
||||
*size = GetStringBoundingBox(STR_FRAMERATE_RATE_GAMELOOP);
|
||||
break;
|
||||
case WID_FRW_RATE_DRAWING:
|
||||
SetDParam(0, STR_FRAMERATE_FPS_GOOD);
|
||||
SetDParam(1, 999999);
|
||||
SetDParam(2, 2);
|
||||
*size = GetStringBoundingBox(STR_FRAMERATE_RATE_BLITTER);
|
||||
break;
|
||||
case WID_FRW_RATE_FACTOR:
|
||||
SetDParam(0, 999999);
|
||||
SetDParam(1, 2);
|
||||
*size = GetStringBoundingBox(STR_FRAMERATE_SPEED_FACTOR);
|
||||
break;
|
||||
|
||||
case WID_FRW_TIMES_NAMES: {
|
||||
int linecount = PFE_MAX - PFE_FIRST;
|
||||
size->width = 0;
|
||||
size->height = FONT_HEIGHT_NORMAL * (linecount + 1) + VSPACING;
|
||||
for (int line = 0; line < linecount; line++) {
|
||||
Dimension line_size = GetStringBoundingBox(STR_FRAMERATE_GAMELOOP + line);
|
||||
size->width = max(size->width, line_size.width);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_FRW_TIMES_CURRENT:
|
||||
case WID_FRW_TIMES_AVERAGE: {
|
||||
int linecount = PFE_MAX - PFE_FIRST;
|
||||
*size = GetStringBoundingBox(STR_FRAMERATE_CURRENT + (widget - WID_FRW_TIMES_CURRENT));
|
||||
SetDParam(0, 999999);
|
||||
SetDParam(1, 2);
|
||||
Dimension item_size = GetStringBoundingBox(STR_FRAMERATE_MS_GOOD);
|
||||
size->width = max(size->width, item_size.width);
|
||||
size->height += FONT_HEIGHT_NORMAL * linecount + VSPACING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Render a column of formatted average durations */
|
||||
void DrawElementTimesColumn(const Rect &r, StringID heading_str, const CachedDecimal *values) const
|
||||
{
|
||||
int y = r.top;
|
||||
DrawString(r.left, r.right, y, heading_str, TC_FROMSTRING, SA_CENTER);
|
||||
y += FONT_HEIGHT_NORMAL + VSPACING;
|
||||
|
||||
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
|
||||
values[e].InsertDParams(0);
|
||||
DrawString(r.left, r.right, y, values[e].strid, TC_FROMSTRING, SA_RIGHT);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FRW_TIMES_NAMES: {
|
||||
/* Render a column of titles for performance element names */
|
||||
int linecount = PFE_MAX - PFE_FIRST;
|
||||
int y = r.top + FONT_HEIGHT_NORMAL + VSPACING; // first line contains headings in the value columns
|
||||
for (int i = 0; i < linecount; i++) {
|
||||
DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + i, TC_FROMSTRING, SA_LEFT);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WID_FRW_TIMES_CURRENT:
|
||||
/* Render short-term average values */
|
||||
DrawElementTimesColumn(r, STR_FRAMERATE_CURRENT, this->times_shortterm);
|
||||
break;
|
||||
case WID_FRW_TIMES_AVERAGE:
|
||||
/* Render averages of all recorded values */
|
||||
DrawElementTimesColumn(r, STR_FRAMERATE_AVERAGE, this->times_longterm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnClick(Point pt, int widget, int click_count)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FRW_TIMES_NAMES:
|
||||
case WID_FRW_TIMES_CURRENT:
|
||||
case WID_FRW_TIMES_AVERAGE: {
|
||||
/* Open time graph windows when clicking detail measurement lines */
|
||||
int line = this->GetRowFromWidget(pt.y, widget, VSPACING, FONT_HEIGHT_NORMAL);
|
||||
if (line > 0) {
|
||||
line -= 1;
|
||||
ShowFrametimeGraphWindow((PerformanceElement)line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static WindowDesc _framerate_display_desc(
|
||||
WDP_AUTO, "framerate_display", 60, 40,
|
||||
WC_FRAMERATE_DISPLAY, WC_NONE,
|
||||
0,
|
||||
_framerate_window_widgets, lengthof(_framerate_window_widgets)
|
||||
);
|
||||
|
||||
|
||||
enum FrametimeGraphWindowWidgets {
|
||||
WID_FGW_CAPTION,
|
||||
WID_FGW_GRAPH,
|
||||
};
|
||||
|
||||
static const NWidgetPart _frametime_graph_window_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_FGW_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
NWidget(WWT_STICKYBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(NWID_VERTICAL), SetPadding(6),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_FGW_GRAPH),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
struct FrametimeGraphWindow : Window {
|
||||
int vertical_scale; ///< number of TIMESTAMP_PRECISION units vertically
|
||||
int horizontal_scale; ///< number of half-second units horizontally
|
||||
uint32 next_scale_update; ///< realtime tick for next scale update
|
||||
|
||||
PerformanceElement element; ///< what element this window renders graph for
|
||||
Dimension graph_size; ///< size of the main graph area (excluding axis labels)
|
||||
|
||||
FrametimeGraphWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
|
||||
{
|
||||
this->element = (PerformanceElement)number;
|
||||
this->horizontal_scale = 4;
|
||||
this->vertical_scale = TIMESTAMP_PRECISION / 10;
|
||||
this->next_scale_update = 0;
|
||||
|
||||
this->InitNested(number);
|
||||
}
|
||||
|
||||
virtual void SetStringParameters(int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FGW_CAPTION:
|
||||
SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
|
||||
{
|
||||
if (widget == WID_FGW_GRAPH) {
|
||||
SetDParam(0, 100);
|
||||
Dimension size_ms_label = GetStringBoundingBox(STR_FRAMERATE_GRAPH_MILLISECONDS);
|
||||
SetDParam(0, 100);
|
||||
Dimension size_s_label = GetStringBoundingBox(STR_FRAMERATE_GRAPH_SECONDS);
|
||||
|
||||
/* Size graph in height to fit at least 10 vertical labels with space between, or at least 100 pixels */
|
||||
graph_size.height = max<uint>(100, 10 * (size_ms_label.height + 1));
|
||||
/* Always 2:1 graph area */
|
||||
graph_size.width = 2 * graph_size.height;
|
||||
*size = graph_size;
|
||||
|
||||
size->width += size_ms_label.width + 2;
|
||||
size->height += size_s_label.height + 2;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectHorizontalScale(TimingMeasurement range)
|
||||
{
|
||||
/* Determine horizontal scale based on period covered by 60 points
|
||||
* (slightly less than 2 seconds at full game speed) */
|
||||
struct ScaleDef { TimingMeasurement range; int scale; };
|
||||
static const ScaleDef hscales[] = {
|
||||
{ 120, 60 },
|
||||
{ 10, 20 },
|
||||
{ 5, 10 },
|
||||
{ 3, 4 },
|
||||
{ 1, 2 },
|
||||
};
|
||||
for (const ScaleDef *sc = hscales; sc < hscales + lengthof(hscales); sc++) {
|
||||
if (range < sc->range) this->horizontal_scale = sc->scale;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectVerticalScale(TimingMeasurement range)
|
||||
{
|
||||
/* Determine vertical scale based on peak value (within the horizontal scale + a bit) */
|
||||
static const TimingMeasurement vscales[] = {
|
||||
TIMESTAMP_PRECISION * 100,
|
||||
TIMESTAMP_PRECISION * 10,
|
||||
TIMESTAMP_PRECISION * 5,
|
||||
TIMESTAMP_PRECISION,
|
||||
TIMESTAMP_PRECISION / 2,
|
||||
TIMESTAMP_PRECISION / 5,
|
||||
TIMESTAMP_PRECISION / 10,
|
||||
TIMESTAMP_PRECISION / 50,
|
||||
TIMESTAMP_PRECISION / 200,
|
||||
};
|
||||
for (const TimingMeasurement *sc = vscales; sc < vscales + lengthof(vscales); sc++) {
|
||||
if (range < *sc) this->vertical_scale = (int)*sc;
|
||||
}
|
||||
}
|
||||
|
||||
/** Recalculate the graph scaling factors based on current recorded data */
|
||||
void UpdateScale()
|
||||
{
|
||||
const TimingMeasurement *durations = _pf_data[this->element].durations;
|
||||
const TimingMeasurement *timestamps = _pf_data[this->element].timestamps;
|
||||
int num_valid = _pf_data[this->element].num_valid;
|
||||
int point = _pf_data[this->element].prev_index;
|
||||
|
||||
TimingMeasurement lastts = timestamps[point];
|
||||
TimingMeasurement time_sum = 0;
|
||||
TimingMeasurement peak_value = 0;
|
||||
int count = 0;
|
||||
|
||||
/* Sensible default for when too few measurements are available */
|
||||
this->horizontal_scale = 4;
|
||||
|
||||
for (int i = 1; i < num_valid; i++) {
|
||||
point--;
|
||||
if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
|
||||
|
||||
TimingMeasurement value = durations[point];
|
||||
if (value == PerformanceData::INVALID_DURATION) {
|
||||
/* Skip gaps in data by pretending time is continuous across them */
|
||||
lastts = timestamps[point];
|
||||
continue;
|
||||
}
|
||||
if (value > peak_value) peak_value = value;
|
||||
count++;
|
||||
|
||||
/* Accumulate period of time covered by data */
|
||||
time_sum += lastts - timestamps[point];
|
||||
lastts = timestamps[point];
|
||||
|
||||
/* Enough data to select a range and get decent data density */
|
||||
if (count == 60) this->SelectHorizontalScale(time_sum / TIMESTAMP_PRECISION);
|
||||
|
||||
/* End when enough points have been collected and the horizontal scale has been exceeded */
|
||||
if (count >= 60 && time_sum >= (this->horizontal_scale + 2) * TIMESTAMP_PRECISION / 2) break;
|
||||
}
|
||||
|
||||
this->SelectVerticalScale(peak_value);
|
||||
}
|
||||
|
||||
virtual void OnTick()
|
||||
{
|
||||
this->SetDirty();
|
||||
|
||||
if (this->next_scale_update < _realtime_tick) {
|
||||
this->next_scale_update = _realtime_tick + 500;
|
||||
this->UpdateScale();
|
||||
}
|
||||
}
|
||||
|
||||
/** Scale and interpolate a value from a source range into a destination range */
|
||||
template<typename T>
|
||||
static inline T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
|
||||
{
|
||||
T dst_diff = dst_max - dst_min;
|
||||
T src_diff = src_max - src_min;
|
||||
return (value - src_min) * dst_diff / src_diff + dst_min;
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
{
|
||||
if (widget == WID_FGW_GRAPH) {
|
||||
const TimingMeasurement *durations = _pf_data[this->element].durations;
|
||||
const TimingMeasurement *timestamps = _pf_data[this->element].timestamps;
|
||||
int point = _pf_data[this->element].prev_index;
|
||||
|
||||
const int x_zero = r.right - (int)this->graph_size.width;
|
||||
const int x_max = r.right;
|
||||
const int y_zero = r.top + (int)this->graph_size.height;
|
||||
const int y_max = r.top;
|
||||
const int c_grid = PC_DARK_GREY;
|
||||
const int c_lines = PC_BLACK;
|
||||
const int c_peak = PC_DARK_RED;
|
||||
|
||||
const TimingMeasurement draw_horz_scale = (TimingMeasurement)this->horizontal_scale * TIMESTAMP_PRECISION / 2;
|
||||
const TimingMeasurement draw_vert_scale = (TimingMeasurement)this->vertical_scale;
|
||||
|
||||
/* Number of \c horizontal_scale units in each horizontal division */
|
||||
const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
|
||||
/* Number of divisions of the horizontal axis */
|
||||
const uint horz_divisions = this->horizontal_scale / horz_div_scl;
|
||||
/* Number of divisions of the vertical axis */
|
||||
const uint vert_divisions = 10;
|
||||
|
||||
/* Draw division lines and labels for the vertical axis */
|
||||
for (uint division = 0; division < vert_divisions; division++) {
|
||||
int y = Scinterlate(y_zero, y_max, 0, (int)vert_divisions, (int)division);
|
||||
GfxDrawLine(x_zero, y, x_max, y, c_grid);
|
||||
if (division % 2 == 0) {
|
||||
if ((TimingMeasurement)this->vertical_scale > TIMESTAMP_PRECISION) {
|
||||
SetDParam(0, this->vertical_scale * division / 10 / TIMESTAMP_PRECISION);
|
||||
DrawString(r.left, x_zero - 2, y - FONT_HEIGHT_SMALL, STR_FRAMERATE_GRAPH_SECONDS, TC_GREY, SA_RIGHT | SA_FORCE, false, FS_SMALL);
|
||||
} else {
|
||||
SetDParam(0, this->vertical_scale * division / 10 * 1000 / TIMESTAMP_PRECISION);
|
||||
DrawString(r.left, x_zero - 2, y - FONT_HEIGHT_SMALL, STR_FRAMERATE_GRAPH_MILLISECONDS, TC_GREY, SA_RIGHT | SA_FORCE, false, FS_SMALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Draw divison lines and labels for the horizontal axis */
|
||||
for (uint division = horz_divisions; division > 0; division--) {
|
||||
int x = Scinterlate(x_zero, x_max, 0, (int)horz_divisions, (int)horz_divisions - (int)division);
|
||||
GfxDrawLine(x, y_max, x, y_zero, c_grid);
|
||||
if (division % 2 == 0) {
|
||||
SetDParam(0, division * horz_div_scl / 2);
|
||||
DrawString(x, x_max, y_zero + 2, STR_FRAMERATE_GRAPH_SECONDS, TC_GREY, SA_LEFT | SA_FORCE, false, FS_SMALL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Position of last rendered data point */
|
||||
Point lastpoint = {
|
||||
x_max,
|
||||
(int)Scinterlate<int64>(y_zero, y_max, 0, this->vertical_scale, durations[point])
|
||||
};
|
||||
/* Timestamp of last rendered data point */
|
||||
TimingMeasurement lastts = timestamps[point];
|
||||
|
||||
TimingMeasurement peak_value = 0;
|
||||
Point peak_point = { 0, 0 };
|
||||
TimingMeasurement value_sum = 0;
|
||||
TimingMeasurement time_sum = 0;
|
||||
int points_drawn = 0;
|
||||
|
||||
for (int i = 1; i < NUM_FRAMERATE_POINTS; i++) {
|
||||
point--;
|
||||
if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
|
||||
|
||||
TimingMeasurement value = durations[point];
|
||||
if (value == PerformanceData::INVALID_DURATION) {
|
||||
/* Skip gaps in measurements, pretend the data points on each side are continuous */
|
||||
lastts = timestamps[point];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Use total time period covered for value along horizontal axis */
|
||||
time_sum += lastts - timestamps[point];
|
||||
lastts = timestamps[point];
|
||||
/* Stop if past the width of the graph */
|
||||
if (time_sum > draw_horz_scale) break;
|
||||
|
||||
/* Draw line from previous point to new point */
|
||||
Point newpoint = {
|
||||
(int)Scinterlate<int64>(x_zero, x_max, 0, (int64)draw_horz_scale, (int64)draw_horz_scale - (int64)time_sum),
|
||||
(int)Scinterlate<int64>(y_zero, y_max, 0, (int64)draw_vert_scale, (int64)value)
|
||||
};
|
||||
assert(newpoint.x <= lastpoint.x);
|
||||
GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
|
||||
lastpoint = newpoint;
|
||||
|
||||
/* Record peak and average value across graphed data */
|
||||
value_sum += value;
|
||||
points_drawn++;
|
||||
if (value > peak_value) {
|
||||
peak_value = value;
|
||||
peak_point = newpoint;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the peak value is significantly larger than the average, mark and label it */
|
||||
if (points_drawn > 0 && peak_value > TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
|
||||
TextColour tc_peak = (TextColour)(TC_IS_PALETTE_COLOUR | c_peak);
|
||||
GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
|
||||
SetDParam(0, peak_value * 1000 / TIMESTAMP_PRECISION);
|
||||
int label_y = max(y_max, peak_point.y - FONT_HEIGHT_SMALL);
|
||||
if (peak_point.x - x_zero > (int)this->graph_size.width / 2) {
|
||||
DrawString(x_zero, peak_point.x - 2, label_y, STR_FRAMERATE_GRAPH_MILLISECONDS, tc_peak, SA_RIGHT | SA_FORCE, false, FS_SMALL);
|
||||
} else {
|
||||
DrawString(peak_point.x + 2, x_max, label_y, STR_FRAMERATE_GRAPH_MILLISECONDS, tc_peak, SA_LEFT | SA_FORCE, false, FS_SMALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static WindowDesc _frametime_graph_window_desc(
|
||||
WDP_AUTO, "frametime_graph", 140, 90,
|
||||
WC_FRAMETIME_GRAPH, WC_NONE,
|
||||
0,
|
||||
_frametime_graph_window_widgets, lengthof(_frametime_graph_window_widgets)
|
||||
);
|
||||
|
||||
|
||||
|
||||
void ShowFramerateWindow()
|
||||
{
|
||||
AllocateWindowDescFront<FramerateWindow>(&_framerate_display_desc, 0);
|
||||
}
|
||||
|
||||
void ShowFrametimeGraphWindow(PerformanceElement elem)
|
||||
{
|
||||
if (elem < PFE_FIRST || elem >= PFE_MAX) return; // maybe warn?
|
||||
AllocateWindowDescFront<FrametimeGraphWindow>(&_frametime_graph_window_desc, elem, true);
|
||||
}
|
||||
|
||||
void ConPrintFramerate()
|
||||
{
|
||||
const int count1 = NUM_FRAMERATE_POINTS / 8;
|
||||
const int count2 = NUM_FRAMERATE_POINTS / 4;
|
||||
const int count3 = NUM_FRAMERATE_POINTS / 1;
|
||||
|
||||
IConsolePrintF(TC_SILVER, "Based on num. data points: %d %d %d", count1, count2, count3);
|
||||
|
||||
static const char *MEASUREMENT_NAMES[PFE_MAX] = {
|
||||
"Game loop",
|
||||
" GL station ticks",
|
||||
" GL train ticks",
|
||||
" GL road vehicle ticks",
|
||||
" GL ship ticks",
|
||||
" GL aircraft ticks",
|
||||
" GL landscape ticks",
|
||||
" GL link graph delays",
|
||||
"Drawing",
|
||||
" Viewport drawing",
|
||||
"Video output",
|
||||
"Sound mixing",
|
||||
};
|
||||
|
||||
static const PerformanceElement rate_elements[] = { PFE_GAMELOOP, PFE_DRAWING, PFE_VIDEO };
|
||||
|
||||
bool printed_anything = false;
|
||||
|
||||
for (const PerformanceElement *e = rate_elements; e < rate_elements + lengthof(rate_elements); e++) {
|
||||
auto &pf = _pf_data[*e];
|
||||
if (pf.num_valid == 0) continue;
|
||||
IConsolePrintF(TC_GREEN, "%s rate: %.2ffps (expected: %.2ffps)",
|
||||
MEASUREMENT_NAMES[*e],
|
||||
pf.GetRate(),
|
||||
pf.expected_rate);
|
||||
printed_anything = true;
|
||||
}
|
||||
|
||||
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
|
||||
auto &pf = _pf_data[e];
|
||||
if (pf.num_valid == 0) continue;
|
||||
IConsolePrintF(TC_LIGHT_BLUE, "%s times: %.2fms %.2fms %.2fms",
|
||||
MEASUREMENT_NAMES[e],
|
||||
pf.GetAverageDurationMilliseconds(count1),
|
||||
pf.GetAverageDurationMilliseconds(count2),
|
||||
pf.GetAverageDurationMilliseconds(count3));
|
||||
printed_anything = true;
|
||||
}
|
||||
|
||||
if (!printed_anything) {
|
||||
IConsoleWarning("No performance measurements have been taken yet");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FRAMERATE_GUI_H
|
||||
#define FRAMERATE_GUI_H
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "core/enum_type.hpp"
|
||||
|
||||
enum PerformanceElement {
|
||||
PFE_FIRST = 0,
|
||||
PFE_GAMELOOP = 0, ///< Speed of gameloop processing.
|
||||
PFE_GL_ECONOMY, ///< Time spent processing cargo movement
|
||||
PFE_GL_TRAINS, ///< Time spent processing trains
|
||||
PFE_GL_ROADVEHS, ///< Time spend processing road vehicles
|
||||
PFE_GL_SHIPS, ///< Time spent processing ships
|
||||
PFE_GL_AIRCRAFT, ///< Time spent processing aircraft
|
||||
PFE_GL_LANDSCAPE, ///< Time spent processing other world features
|
||||
PFE_GL_LINKGRAPH, ///< Time spent waiting for link graph background jobs
|
||||
PFE_DRAWING, ///< Speed of drawing world and GUI.
|
||||
PFE_DRAWWORLD, ///< Time spent drawing world viewports in GUI
|
||||
PFE_VIDEO, ///< Speed of painting drawn video buffer.
|
||||
PFE_SOUND, ///< Speed of mixing audio samples
|
||||
PFE_MAX, ///< End of enum, must be last.
|
||||
};
|
||||
DECLARE_POSTFIX_INCREMENT(PerformanceElement)
|
||||
|
||||
typedef uint64 TimingMeasurement;
|
||||
|
||||
/**
|
||||
* RAII class for measuring simple elements of performance.
|
||||
* Construct an object with the appropriate element parameter when processing begins,
|
||||
* time is automatically taken when the object goes out of scope again.
|
||||
*/
|
||||
class PerformanceMeasurer {
|
||||
PerformanceElement elem;
|
||||
TimingMeasurement start_time;
|
||||
public:
|
||||
PerformanceMeasurer(PerformanceElement elem);
|
||||
~PerformanceMeasurer();
|
||||
void SetExpectedRate(double rate);
|
||||
static void Paused(PerformanceElement elem);
|
||||
};
|
||||
|
||||
/**
|
||||
* RAII class for measuring multi-step elements of performance.
|
||||
* At the beginning of a frame, call Reset on the element, then construct an object in the scope where
|
||||
* each processing cycle happens. The measurements are summed between resets.
|
||||
*/
|
||||
class PerformanceAccumulator {
|
||||
PerformanceElement elem;
|
||||
TimingMeasurement start_time;
|
||||
public:
|
||||
PerformanceAccumulator(PerformanceElement elem);
|
||||
~PerformanceAccumulator();
|
||||
static void Reset(PerformanceElement elem);
|
||||
};
|
||||
|
||||
void ShowFramerateWindow();
|
||||
|
||||
#endif /* FRAMERATE_GUI_H */
|
|
@ -32,6 +32,7 @@
|
|||
#include "company_func.h"
|
||||
#include "pathfinder/npf/aystar.h"
|
||||
#include "saveload/saveload.h"
|
||||
#include "framerate_type.h"
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
|
@ -720,6 +721,8 @@ TileIndex _cur_tileloop_tile;
|
|||
*/
|
||||
void RunTileLoop()
|
||||
{
|
||||
PerformanceAccumulator framerate(PFE_GL_LANDSCAPE);
|
||||
|
||||
/* The pseudorandom sequence of tiles is generated using a Galois linear feedback
|
||||
* shift register (LFSR). This allows a deterministic pseudorandom ordering, but
|
||||
* still with minimal state and fast iteration. */
|
||||
|
@ -1303,10 +1306,14 @@ void OnTick_LinkGraph();
|
|||
|
||||
void CallLandscapeTick()
|
||||
{
|
||||
{
|
||||
PerformanceAccumulator framerate(PFE_GL_LANDSCAPE);
|
||||
|
||||
OnTick_Town();
|
||||
OnTick_Trees();
|
||||
OnTick_Station();
|
||||
OnTick_Industry();
|
||||
}
|
||||
|
||||
OnTick_Companies();
|
||||
OnTick_LinkGraph();
|
||||
|
|
|
@ -475,6 +475,7 @@ STR_ABOUT_MENU_SCREENSHOT :Screenshot
|
|||
STR_ABOUT_MENU_ZOOMIN_SCREENSHOT :Fully zoomed in screenshot
|
||||
STR_ABOUT_MENU_DEFAULTZOOM_SCREENSHOT :Default zoom screenshot
|
||||
STR_ABOUT_MENU_GIANT_SCREENSHOT :Whole map screenshot
|
||||
STR_ABOUT_MENU_SHOW_FRAMERATE :Show frame rate
|
||||
STR_ABOUT_MENU_ABOUT_OPENTTD :About 'OpenTTD'
|
||||
STR_ABOUT_MENU_SPRITE_ALIGNER :Sprite aligner
|
||||
STR_ABOUT_MENU_TOGGLE_BOUNDING_BOXES :Toggle bounding boxes
|
||||
|
@ -2697,6 +2698,56 @@ STR_ABOUT_ORIGINAL_COPYRIGHT :{BLACK}Original
|
|||
STR_ABOUT_VERSION :{BLACK}OpenTTD version {REV}
|
||||
STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD {COPYRIGHT} 2002-2018 The OpenTTD team
|
||||
|
||||
# Framerate display window
|
||||
STR_FRAMERATE_CAPTION :{WHITE}Frame rate
|
||||
STR_FRAMERATE_CAPTION_SMALL :{STRING2}{WHITE} ({DECIMAL}x)
|
||||
STR_FRAMERATE_RATE_GAMELOOP :{WHITE}Simulation rate: {STRING2}
|
||||
STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP :{BLACK}Number of game ticks simulated per second.
|
||||
STR_FRAMERATE_RATE_BLITTER :{WHITE}Graphics frame rate: {STRING2}
|
||||
STR_FRAMERATE_RATE_BLITTER_TOOLTIP :{BLACK}Number of video frames rendered per second.
|
||||
STR_FRAMERATE_SPEED_FACTOR :{WHITE}Current game speed factor: {DECIMAL}x
|
||||
STR_FRAMERATE_SPEED_FACTOR_TOOLTIP :{BLACK}How fast the game is currently running, compared to the expected speed at normal simulation rate.
|
||||
STR_FRAMERATE_CURRENT :{WHITE}Current
|
||||
STR_FRAMERATE_AVERAGE :{WHITE}Average
|
||||
STR_FRAMERATE_DATA_POINTS :{WHITE}Data based on {COMMA} measurements
|
||||
STR_FRAMERATE_MS_GOOD :{LTBLUE}{DECIMAL}{WHITE} ms
|
||||
STR_FRAMERATE_MS_WARN :{YELLOW}{DECIMAL}{WHITE} ms
|
||||
STR_FRAMERATE_MS_BAD :{RED}{DECIMAL}{WHITE} ms
|
||||
STR_FRAMERATE_FPS_GOOD :{LTBLUE}{DECIMAL}{WHITE} frames/s
|
||||
STR_FRAMERATE_FPS_WARN :{YELLOW}{DECIMAL}{WHITE} frames/s
|
||||
STR_FRAMERATE_FPS_BAD :{RED}{DECIMAL}{WHITE} frames/s
|
||||
STR_FRAMERATE_GRAPH_MILLISECONDS :{TINY_FONT}{COMMA} ms
|
||||
STR_FRAMERATE_GRAPH_SECONDS :{TINY_FONT}{COMMA} s
|
||||
############ Leave those lines in this order!!
|
||||
STR_FRAMERATE_GAMELOOP :{WHITE}Game loop total:
|
||||
STR_FRAMERATE_GL_ECONOMY :{WHITE} Cargo handling:
|
||||
STR_FRAMERATE_GL_TRAINS :{WHITE} Train ticks:
|
||||
STR_FRAMERATE_GL_ROADVEHS :{WHITE} Road vehicle ticks:
|
||||
STR_FRAMERATE_GL_SHIPS :{WHITE} Ship ticks:
|
||||
STR_FRAMERATE_GL_AIRCRAFT :{WHITE} Aircraft ticks:
|
||||
STR_FRAMERATE_GL_LANDSCAPE :{WHITE} World ticks:
|
||||
STR_FRAMERATE_GL_LINKGRAPH :{WHITE} Link graph delay:
|
||||
STR_FRAMERATE_DRAWING :{WHITE}Graphics rendering:
|
||||
STR_FRAMERATE_DRAWING_VIEWPORTS :{WHITE} World viewports:
|
||||
STR_FRAMERATE_VIDEO :{WHITE}Video output:
|
||||
STR_FRAMERATE_SOUND :{WHITE}Sound mixing:
|
||||
############ End of leave-in-this-order
|
||||
############ Leave those lines in this order!!
|
||||
STR_FRAMETIME_CAPTION_GAMELOOP :Game loop
|
||||
STR_FRAMETIME_CAPTION_GL_ECONOMY :Cargo handling
|
||||
STR_FRAMETIME_CAPTION_GL_TRAINS :Train ticks
|
||||
STR_FRAMETIME_CAPTION_GL_ROADVEHS :Road vehicle ticks
|
||||
STR_FRAMETIME_CAPTION_GL_SHIPS :Ship ticks
|
||||
STR_FRAMETIME_CAPTION_GL_AIRCRAFT :Aircraft ticks
|
||||
STR_FRAMETIME_CAPTION_GL_LANDSCAPE :World ticks
|
||||
STR_FRAMETIME_CAPTION_GL_LINKGRAPH :Link graph delay
|
||||
STR_FRAMETIME_CAPTION_DRAWING :Graphics rendering
|
||||
STR_FRAMETIME_CAPTION_DRAWING_VIEWPORTS :World viewport rendering
|
||||
STR_FRAMETIME_CAPTION_VIDEO :Video output
|
||||
STR_FRAMETIME_CAPTION_SOUND :Sound mixing
|
||||
############ End of leave-in-this-order
|
||||
|
||||
|
||||
# Save/load game/scenario
|
||||
STR_SAVELOAD_SAVE_CAPTION :{WHITE}Save Game
|
||||
STR_SAVELOAD_LOAD_CAPTION :{WHITE}Load Game
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "demands.h"
|
||||
#include "mcf.h"
|
||||
#include "flowmapper.h"
|
||||
#include "../framerate_type.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
|
@ -151,6 +152,7 @@ void OnTick_LinkGraph()
|
|||
if (offset == 0) {
|
||||
LinkGraphSchedule::instance.SpawnNext();
|
||||
} else if (offset == _settings_game.linkgraph.recalc_interval / 2) {
|
||||
PerformanceMeasurer framerate(PFE_GL_LINKGRAPH);
|
||||
LinkGraphSchedule::instance.JoinNext();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "stdafx.h"
|
||||
#include <math.h>
|
||||
#include "core/math_func.hpp"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
@ -138,6 +139,13 @@ static void MxCloseChannel(MixerChannel *mc)
|
|||
|
||||
void MxMixSamples(void *buffer, uint samples)
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_SOUND);
|
||||
static uint last_samples = 0;
|
||||
if (samples != last_samples) {
|
||||
framerate.SetExpectedRate((double)_play_rate / samples);
|
||||
last_samples = samples;
|
||||
}
|
||||
|
||||
MixerChannel *mc;
|
||||
|
||||
/* Clear the buffer */
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include "subsidy_func.h"
|
||||
#include "gfx_layout.h"
|
||||
#include "viewport_sprite_sorter.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "linkgraph/linkgraphschedule.h"
|
||||
|
||||
|
@ -1336,6 +1337,14 @@ void StateGameLoop()
|
|||
{
|
||||
/* don't execute the state loop during pause */
|
||||
if (_pause_mode != PM_UNPAUSED) {
|
||||
PerformanceMeasurer::Paused(PFE_GAMELOOP);
|
||||
PerformanceMeasurer::Paused(PFE_GL_ECONOMY);
|
||||
PerformanceMeasurer::Paused(PFE_GL_TRAINS);
|
||||
PerformanceMeasurer::Paused(PFE_GL_ROADVEHS);
|
||||
PerformanceMeasurer::Paused(PFE_GL_SHIPS);
|
||||
PerformanceMeasurer::Paused(PFE_GL_AIRCRAFT);
|
||||
PerformanceMeasurer::Paused(PFE_GL_LANDSCAPE);
|
||||
|
||||
UpdateLandscapingLimits();
|
||||
#ifndef DEBUG_DUMP_COMMANDS
|
||||
Game::GameLoop();
|
||||
|
@ -1343,6 +1352,9 @@ void StateGameLoop()
|
|||
CallWindowTickEvent();
|
||||
return;
|
||||
}
|
||||
|
||||
PerformanceMeasurer framerate(PFE_GAMELOOP);
|
||||
PerformanceAccumulator::Reset(PFE_GL_LANDSCAPE);
|
||||
if (HasModalProgress()) return;
|
||||
|
||||
Layouter::ReduceLineCache();
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "core/backup_type.hpp"
|
||||
#include "newgrf.h"
|
||||
#include "zoom_func.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -1587,6 +1588,8 @@ Money RoadVehicle::GetRunningCost() const
|
|||
|
||||
bool RoadVehicle::Tick()
|
||||
{
|
||||
PerformanceAccumulator framerate(PFE_GL_ROADVEHS);
|
||||
|
||||
this->tick_counter++;
|
||||
|
||||
if (this->IsFrontEngine()) {
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "company_base.h"
|
||||
#include "tunnelbridge_map.h"
|
||||
#include "zoom_func.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -638,6 +639,8 @@ reverse_direction:
|
|||
|
||||
bool Ship::Tick()
|
||||
{
|
||||
PerformanceAccumulator framerate(PFE_GL_SHIPS);
|
||||
|
||||
if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
|
||||
|
||||
ShipController(this);
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "goal_base.h"
|
||||
#include "story_base.h"
|
||||
#include "toolbar_gui.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "widgets/toolbar_widget.h"
|
||||
|
||||
|
@ -1045,7 +1046,7 @@ static CallBackFunction PlaceLandBlockInfo()
|
|||
|
||||
static CallBackFunction ToolbarHelpClick(Window *w)
|
||||
{
|
||||
PopupMainToolbMenu(w, WID_TN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 12 : 9);
|
||||
PopupMainToolbMenu(w, WID_TN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 13 : 10);
|
||||
return CBF_NONE;
|
||||
}
|
||||
|
||||
|
@ -1147,10 +1148,11 @@ static CallBackFunction MenuClickHelp(int index)
|
|||
case 5: MenuClickLargeWorldScreenshot(SC_ZOOMEDIN); break;
|
||||
case 6: MenuClickLargeWorldScreenshot(SC_DEFAULTZOOM); break;
|
||||
case 7: MenuClickLargeWorldScreenshot(SC_WORLD); break;
|
||||
case 8: ShowAboutWindow(); break;
|
||||
case 9: ShowSpriteAlignerWindow(); break;
|
||||
case 10: ToggleBoundingBoxes(); break;
|
||||
case 11: ToggleDirtyBlocks(); break;
|
||||
case 8: ShowFramerateWindow(); break;
|
||||
case 9: ShowAboutWindow(); break;
|
||||
case 10: ShowSpriteAlignerWindow(); break;
|
||||
case 11: ToggleBoundingBoxes(); break;
|
||||
case 12: ToggleDirtyBlocks(); break;
|
||||
}
|
||||
return CBF_NONE;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "order_backup.h"
|
||||
#include "zoom_func.h"
|
||||
#include "newgrf_debug.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "table/train_cmd.h"
|
||||
|
@ -3902,6 +3903,8 @@ Money Train::GetRunningCost() const
|
|||
*/
|
||||
bool Train::Tick()
|
||||
{
|
||||
PerformanceAccumulator framerate(PFE_GL_TRAINS);
|
||||
|
||||
this->tick_counter++;
|
||||
|
||||
if (this->IsFrontEngine()) {
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "gamelog.h"
|
||||
#include "linkgraph/linkgraph.h"
|
||||
#include "linkgraph/refresh.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -945,8 +946,15 @@ void CallVehicleTicks()
|
|||
|
||||
RunVehicleDayProc();
|
||||
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_GL_ECONOMY);
|
||||
Station *st;
|
||||
FOR_ALL_STATIONS(st) LoadUnloadStation(st);
|
||||
}
|
||||
PerformanceAccumulator::Reset(PFE_GL_TRAINS);
|
||||
PerformanceAccumulator::Reset(PFE_GL_ROADVEHS);
|
||||
PerformanceAccumulator::Reset(PFE_GL_SHIPS);
|
||||
PerformanceAccumulator::Reset(PFE_GL_AIRCRAFT);
|
||||
|
||||
Vehicle *v;
|
||||
FOR_ALL_VEHICLES(v) {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "../network/network.h"
|
||||
#include "../core/random_func.hpp"
|
||||
#include "../core/math_func.hpp"
|
||||
#include "../framerate_type.h"
|
||||
#include "allegro_v.h"
|
||||
#include <allegro.h>
|
||||
|
||||
|
@ -56,6 +57,8 @@ void VideoDriver_Allegro::MakeDirty(int left, int top, int width, int height)
|
|||
|
||||
static void DrawSurfaceToScreen()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
int n = _num_dirty_rects;
|
||||
if (n == 0) return;
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "cocoa_v.h"
|
||||
#include "../../core/math_func.hpp"
|
||||
#include "../../gfx_func.h"
|
||||
#include "../../framerate_type.h"
|
||||
|
||||
/* On some old versions of MAC OS this may not be defined.
|
||||
* Those versions generally only produce code for PPC. So it should be safe to
|
||||
|
@ -431,6 +432,8 @@ WindowQuartzSubdriver::~WindowQuartzSubdriver()
|
|||
|
||||
void WindowQuartzSubdriver::Draw(bool force_update)
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
/* Check if we need to do anything */
|
||||
if (this->num_dirty_rects == 0 || [ this->window isMiniaturized ]) return;
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "cocoa_v.h"
|
||||
#include "../../core/math_func.hpp"
|
||||
#include "../../gfx_func.h"
|
||||
#include "../../framerate_type.h"
|
||||
|
||||
/**
|
||||
* Important notice regarding all modifications!!!!!!!
|
||||
|
@ -361,6 +362,8 @@ WindowQuickdrawSubdriver::~WindowQuickdrawSubdriver()
|
|||
|
||||
void WindowQuickdrawSubdriver::Draw(bool force_update)
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
/* Check if we need to do anything */
|
||||
if (this->num_dirty_rects == 0 || [ this->window isMiniaturized ]) return;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "../core/random_func.hpp"
|
||||
#include "../core/math_func.hpp"
|
||||
#include "../fileio_func.h"
|
||||
#include "../framerate_type.h"
|
||||
#include "sdl_v.h"
|
||||
#include <SDL.h>
|
||||
|
||||
|
@ -148,6 +149,8 @@ static void CheckPaletteAnim()
|
|||
|
||||
static void DrawSurfaceToScreen()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
int n = _num_dirty_rects;
|
||||
if (n == 0) return;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "../progress.h"
|
||||
#include "../window_gui.h"
|
||||
#include "../window_func.h"
|
||||
#include "../framerate_type.h"
|
||||
#include "win32_v.h"
|
||||
#include <windows.h>
|
||||
#include <imm.h>
|
||||
|
@ -359,6 +360,8 @@ bool VideoDriver_Win32::MakeWindow(bool full_screen)
|
|||
/** Do palette animation and blit to the window. */
|
||||
static void PaintWindow(HDC dc)
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
HDC dc2 = CreateCompatibleDC(dc);
|
||||
HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
|
||||
HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
#include "company_base.h"
|
||||
#include "command_func.h"
|
||||
#include "network/network_func.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
@ -1654,6 +1655,8 @@ static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right
|
|||
*/
|
||||
void Window::DrawViewport() const
|
||||
{
|
||||
PerformanceAccumulator framerate(PFE_DRAWWORLD);
|
||||
|
||||
DrawPixelInfo *dpi = _cur_dpi;
|
||||
|
||||
dpi->left += this->left;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "error.h"
|
||||
#include "game/game.hpp"
|
||||
#include "video/video_driver.hpp"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
@ -3077,6 +3078,9 @@ void InputLoop()
|
|||
*/
|
||||
void UpdateWindows()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_DRAWING);
|
||||
PerformanceAccumulator::Reset(PFE_DRAWWORLD);
|
||||
|
||||
Window *w;
|
||||
|
||||
static int highlight_timer = 1;
|
||||
|
|
|
@ -681,6 +681,18 @@ enum WindowClass {
|
|||
*/
|
||||
WC_SAVE_PRESET,
|
||||
|
||||
/**
|
||||
* Framerate display; %Window numbers:
|
||||
* - 0 = #FramerateDisplayWidgets
|
||||
*/
|
||||
WC_FRAMERATE_DISPLAY,
|
||||
|
||||
/**
|
||||
* Frame time graph; %Window numbers:
|
||||
* - 0 = #FramerateDisplayWidgets
|
||||
*/
|
||||
WC_FRAMETIME_GRAPH,
|
||||
|
||||
WC_INVALID = 0xFFFF, ///< Invalid window.
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue