Changeset - r22959:8c769e762c2f
[Not reviewed]
README.md
Show inline comments
 
@@ -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
projects/openttd_vs100.vcxproj
Show inline comments
 
@@ -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" />
projects/openttd_vs100.vcxproj.filters
Show inline comments
 
@@ -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>
projects/openttd_vs140.vcxproj
Show inline comments
 
@@ -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" />
projects/openttd_vs140.vcxproj.filters
Show inline comments
 
@@ -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>
projects/openttd_vs141.vcxproj
Show inline comments
 
@@ -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" />
projects/openttd_vs141.vcxproj.filters
Show inline comments
 
@@ -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>
projects/openttd_vs80.vcproj
Show inline comments
 
@@ -1111,6 +1111,10 @@
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\framerate_type.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\base_consist.h"
 
				>
 
			</File>
 
@@ -2119,6 +2123,10 @@
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\framerate_gui.cpp"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\genworld_gui.cpp"
 
				>
 
			</File>
projects/openttd_vs90.vcproj
Show inline comments
 
@@ -1108,6 +1108,10 @@
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\framerate_type.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\base_consist.h"
 
				>
 
			</File>
 
@@ -2116,6 +2120,10 @@
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\framerate_gui.cpp"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\genworld_gui.cpp"
 
				>
 
			</File>
source.list
Show inline comments
 
@@ -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
src/aircraft_cmd.cpp
Show inline comments
 
@@ -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++;
src/animated_tile.cpp
Show inline comments
 
@@ -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;
src/console_cmds.cpp
Show inline comments
 
@@ -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);
src/framerate_gui.cpp
Show inline comments
 
new file 100644
 
/* $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");
 
	}
 
}
src/framerate_type.h
Show inline comments
 
new file 100644
 
/* $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 */
src/landscape.cpp
Show inline comments
 
@@ -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()
 
{
 
	OnTick_Town();
 
	OnTick_Trees();
 
	OnTick_Station();
 
	OnTick_Industry();
 
	{
 
		PerformanceAccumulator framerate(PFE_GL_LANDSCAPE);
 

	
 
		OnTick_Town();
 
		OnTick_Trees();
 
		OnTick_Station();
 
		OnTick_Industry();
 
	}
 

	
 
	OnTick_Companies();
 
	OnTick_LinkGraph();
src/lang/english.txt
Show inline comments
 
@@ -475,6 +475,7 @@ STR_ABOUT_MENU_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            
 
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
src/linkgraph/linkgraphschedule.cpp
Show inline comments
 
@@ -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();
 
	}
 
}
src/mixer.cpp
Show inline comments
 
@@ -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 
 

	
 
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 */
src/openttd.cpp
Show inline comments
 
@@ -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();
src/roadveh_cmd.cpp
Show inline comments
 
@@ -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() cons
 

	
 
bool RoadVehicle::Tick()
 
{
 
	PerformanceAccumulator framerate(PFE_GL_ROADVEHS);
 

	
 
	this->tick_counter++;
 

	
 
	if (this->IsFrontEngine()) {
src/ship_cmd.cpp
Show inline comments
 
@@ -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);
src/toolbar_gui.cpp
Show inline comments
 
@@ -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 PlaceLandBlockIn
 

	
 
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(in
 
		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;
 
}
src/train_cmd.cpp
Show inline comments
 
@@ -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()) {
src/vehicle.cpp
Show inline comments
 
@@ -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();
 

	
 
	Station *st;
 
	FOR_ALL_STATIONS(st) LoadUnloadStation(st);
 
	{
 
		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) {
src/video/allegro_v.cpp
Show inline comments
 
@@ -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 
 

	
 
static void DrawSurfaceToScreen()
 
{
 
	PerformanceMeasurer framerate(PFE_VIDEO);
 

	
 
	int n = _num_dirty_rects;
 
	if (n == 0) return;
 

	
src/video/cocoa/wnd_quartz.mm
Show inline comments
 
@@ -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::~WindowQuartzSubd
 

	
 
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;
 

	
src/video/cocoa/wnd_quickdraw.mm
Show inline comments
 
@@ -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::~WindowQuickdr
 

	
 
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;
 

	
src/video/sdl_v.cpp
Show inline comments
 
@@ -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;
 

	
src/video/win32_v.cpp
Show inline comments
 
@@ -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 
 
/** 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);
src/viewport.cpp
Show inline comments
 
@@ -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 Vi
 
 */
 
void Window::DrawViewport() const
 
{
 
	PerformanceAccumulator framerate(PFE_DRAWWORLD);
 

	
 
	DrawPixelInfo *dpi = _cur_dpi;
 

	
 
	dpi->left += this->left;
src/window.cpp
Show inline comments
 
@@ -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;
src/window_type.h
Show inline comments
 
@@ -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.
 
};
 

	
0 comments (0 inline, 0 general)