36#include "table/strings.h"
40static std::mutex _sound_perf_lock;
41static std::atomic<bool> _sound_perf_pending;
42static std::vector<TimingMeasurement> _sound_perf_measurements;
59 std::array<TimingMeasurement, NUM_FRAMERATE_POINTS>
durations{};
61 std::array<TimingMeasurement, NUM_FRAMERATE_POINTS>
timestamps{};
91 this->durations[this->next_index] = end_time - start_time;
92 this->timestamps[this->next_index] = start_time;
93 this->prev_index = this->next_index;
94 this->next_index += 1;
105 this->timestamps[this->next_index] = this->acc_timestamp;
106 this->durations[this->next_index] = this->acc_duration;
107 this->prev_index = this->next_index;
108 this->next_index += 1;
112 this->acc_duration = 0;
113 this->acc_timestamp = start_time;
122 this->acc_duration += duration;
132 this->timestamps[this->next_index] = start_time;
134 this->prev_index = this->next_index;
135 this->next_index += 1;
137 this->num_valid += 1;
148 count = std::min(count, this->num_valid);
150 int first_point = this->prev_index - count;
155 for (
int i = first_point; i < first_point + count; i++) {
165 if (count == 0)
return 0;
176 int point = this->prev_index;
177 int last_point = this->next_index - this->num_valid;
191 while (point != last_point) {
194 total += last - this->timestamps[point];
197 last = this->timestamps[point];
203 if (total == 0 || count == 0)
return 0;
259 using namespace std::chrono;
260 return (
TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
282 for (uint e =
PFE_AI0; e <
PFE_MAX; e++) any_active |= _pf_data[e].num_valid > 0;
295 std::lock_guard lk(_sound_perf_lock);
296 if (_sound_perf_measurements.size() >= NUM_FRAMERATE_POINTS * 2)
return;
297 _sound_perf_measurements.push_back(this->start_time);
298 _sound_perf_measurements.push_back(end);
299 _sound_perf_pending.store(
true, std::memory_order_release);
400static std::string_view GetAIName(
int ai_index)
407static constexpr std::initializer_list<NWidgetPart> _framerate_window_widgets = {
442 int num_displayed = 0;
448 inline void SetRate(
double value,
double target)
450 const double threshold_good = target * 0.95;
451 const double threshold_bad = target * 2 / 3;
452 this->value = (uint32_t)(value * 100);
453 this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
456 inline void SetTime(
double value,
double target)
458 const double threshold_good = target / 3;
459 const double threshold_bad = target;
460 this->value = (uint32_t)(value * 100);
461 this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
464 inline uint32_t GetValue()
const {
return this->value; }
465 inline uint32_t GetDecimals()
const {
return 2; }
480 this->num_displayed = this->num_active;
510 if (new_active != this->num_active) {
511 this->num_active = new_active;
514 sb->
SetCapacity(std::min(this->num_displayed, this->num_active));
521 case WID_FRW_CAPTION:
525 return GetString(STR_FRAMERATE_CAPTION_SMALL, this->rate_gameloop.strid, this->rate_gameloop.GetValue(), this->rate_gameloop.GetDecimals(), this->speed_gameloop.GetValue(), this->speed_gameloop.GetDecimals());
527 case WID_FRW_RATE_GAMELOOP:
528 return GetString(STR_FRAMERATE_RATE_GAMELOOP, this->rate_gameloop.strid, this->rate_gameloop.GetValue(), this->rate_gameloop.GetDecimals());
530 case WID_FRW_RATE_DRAWING:
531 return GetString(STR_FRAMERATE_RATE_BLITTER, this->rate_drawing.strid, this->rate_drawing.GetValue(), this->rate_drawing.GetDecimals());
533 case WID_FRW_RATE_FACTOR:
534 return GetString(STR_FRAMERATE_SPEED_FACTOR, this->speed_gameloop.GetValue(), this->speed_gameloop.GetDecimals());
536 case WID_FRW_INFO_DATA_POINTS:
547 case WID_FRW_RATE_GAMELOOP:
550 case WID_FRW_RATE_DRAWING:
553 case WID_FRW_RATE_FACTOR:
557 case WID_FRW_TIMES_NAMES: {
563 if (
_pf_data[e].num_valid == 0)
continue;
570 size.width = std::max(size.width, line_size.width);
575 case WID_FRW_TIMES_CURRENT:
576 case WID_FRW_TIMES_AVERAGE:
577 case WID_FRW_ALLOCSIZE: {
580 size.width = std::max(size.width, item_size.width);
599 int drawable = this->num_displayed;
604 if (
_pf_data[e].num_valid == 0)
continue;
611 if (drawable == 0)
break;
616 void DrawElementAllocationsColumn(
const Rect &r)
const
620 int drawable = this->num_displayed;
625 if (
_pf_data[e].num_valid == 0)
continue;
633 if (drawable == 0)
break;
638 if (drawable == 0)
break;
643 if (drawable == 0)
break;
651 case WID_FRW_TIMES_NAMES: {
655 int drawable = this->num_displayed;
658 if (
_pf_data[e].num_valid == 0)
continue;
663 DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING,
SA_LEFT);
669 if (drawable == 0)
break;
674 case WID_FRW_TIMES_CURRENT:
678 case WID_FRW_TIMES_AVERAGE:
682 case WID_FRW_ALLOCSIZE:
683 DrawElementAllocationsColumn(r);
691 case WID_FRW_TIMES_NAMES:
692 case WID_FRW_TIMES_CURRENT:
693 case WID_FRW_TIMES_AVERAGE: {
697 if (line != INT32_MAX) {
701 if (
_pf_data[e].num_valid > 0) line--;
722 WDP_AUTO,
"framerate_display", 0, 0,
725 _framerate_window_widgets
730static constexpr std::initializer_list<NWidgetPart> _frametime_graph_window_widgets = {
759 case WID_FGW_CAPTION:
761 return GetString(STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
772 if (widget == WID_FGW_GRAPH) {
798 static const std::initializer_list<ScaleDef> hscales = {
805 for (
const auto &sc : hscales) {
813 static const std::initializer_list<TimingMeasurement> vscales = {
824 for (
const auto &sc : vscales) {
832 const auto &durations =
_pf_data[this->element].durations;
833 const auto ×tamps =
_pf_data[this->element].timestamps;
834 int num_valid =
_pf_data[this->element].num_valid;
835 int point =
_pf_data[this->element].prev_index;
843 this->horizontal_scale = 4;
845 for (
int i = 1; i < num_valid; i++) {
852 lastts = timestamps[point];
855 if (value > peak_value) peak_value = value;
859 time_sum += lastts - timestamps[point];
860 lastts = timestamps[point];
863 if (count == 60) this->SelectHorizontalScale(time_sum);
866 if (count >= 60 && time_sum >= (this->horizontal_scale + 2) *
TIMESTAMP_PRECISION / 2)
break;
869 this->SelectVerticalScale(peak_value);
891 template <
typename T>
892 static inline T
Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
894 T dst_diff = dst_max - dst_min;
895 T src_diff = src_max - src_min;
896 return (value - src_min) * dst_diff / src_diff + dst_min;
901 if (widget == WID_FGW_GRAPH) {
902 const auto &durations =
_pf_data[this->element].durations;
903 const auto ×tamps =
_pf_data[this->element].timestamps;
904 int point =
_pf_data[this->element].prev_index;
906 const int x_zero = r.right - (int)this->graph_size.width;
907 const int x_max = r.right;
908 const int y_zero = r.top + (int)this->graph_size.height;
909 const int y_max = r.top;
918 const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
920 const uint horz_divisions = this->horizontal_scale / horz_div_scl;
922 const uint vert_divisions = 10;
925 for (uint division = 0; division < vert_divisions; division++) {
926 int y =
Scinterlate(y_zero, y_max, 0, (
int)vert_divisions, (
int)division);
927 GfxDrawLine(x_zero, y, x_max, y, c_grid);
928 if (division % 2 == 0) {
941 for (uint division = horz_divisions; division > 0; division--) {
942 int x =
Scinterlate(x_zero, x_max, 0, (
int)horz_divisions, (
int)horz_divisions - (
int)division);
943 GfxDrawLine(x, y_max, x, y_zero, c_grid);
944 if (division % 2 == 0) {
946 GetString(STR_FRAMERATE_GRAPH_SECONDS, division * horz_div_scl / 2),
960 Point peak_point = { 0, 0 };
963 int points_drawn = 0;
972 lastts = timestamps[point];
977 time_sum += lastts - timestamps[point];
978 lastts = timestamps[point];
980 if (time_sum > draw_horz_scale)
break;
984 (int)
Scinterlate<int64_t>(x_zero, x_max, 0, (int64_t)draw_horz_scale, (int64_t)draw_horz_scale - (int64_t)time_sum),
987 if (newpoint.
x > lastpoint.
x)
continue;
988 GfxDrawLine(lastpoint.
x, lastpoint.
y, newpoint.
x, newpoint.
y, c_lines);
989 lastpoint = newpoint;
994 if (value > peak_value) {
996 peak_point = newpoint;
1001 if (points_drawn > 0 && peak_value >
TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
1003 GfxFillRect(peak_point.
x - 1, peak_point.
y - 1, peak_point.
x + 1, peak_point.
y + 1, c_peak);
1006 if (peak_point.
x - x_zero > (
int)this->graph_size.width / 2) {
1016static WindowDesc _frametime_graph_window_desc(
1017 WDP_AUTO,
"frametime_graph", 140, 90,
1020 _frametime_graph_window_widgets
1037 if (elem < PFE_FIRST || elem >=
PFE_MAX)
return;
1048 IConsolePrint(TC_SILVER,
"Based on num. data points: {} {} {}", count1, count2, count3);
1050 static const std::array<std::string_view, PFE_MAX> MEASUREMENT_NAMES = {
1052 " GL station ticks",
1054 " GL road vehicle ticks",
1056 " GL aircraft ticks",
1057 " GL landscape ticks",
1058 " GL link graph delays",
1060 " Viewport drawing",
1063 "AI/GS scripts total",
1066 std::string ai_name_buf;
1068 bool printed_anything =
false;
1072 if (pf.num_valid == 0)
continue;
1073 IConsolePrint(TC_GREEN,
"{} rate: {:.2f}fps (expected: {:.2f}fps)",
1074 MEASUREMENT_NAMES[e],
1077 printed_anything =
true;
1082 if (pf.num_valid == 0)
continue;
1083 std::string_view name;
1085 name = MEASUREMENT_NAMES[e];
1087 ai_name_buf = fmt::format(
"AI {} {}", e -
PFE_AI0 + 1, GetAIName(e -
PFE_AI0));
1090 IConsolePrint(TC_LIGHT_BLUE,
"{} times: {:.2f}ms {:.2f}ms {:.2f}ms",
1092 pf.GetAverageDurationMilliseconds(count1),
1093 pf.GetAverageDurationMilliseconds(count2),
1094 pf.GetAverageDurationMilliseconds(count3));
1095 printed_anything =
true;
1098 if (!printed_anything) {
1112 if (_sound_perf_pending.load(std::memory_order_acquire)) {
1113 std::lock_guard lk(_sound_perf_lock);
1114 for (
size_t i = 0; i < _sound_perf_measurements.size(); i += 2) {
1117 _sound_perf_measurements.clear();
1118 _sound_perf_pending.store(
false, std::memory_order_relaxed);
AIInfo keeps track of all information of an AI, like Author, Description, ...
The AIInstance tracks an AI.
static class GameInstance * GetInstance()
Get the current active instance.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition of stuff that is very close to a company, like the company struct itself.
void IConsolePrint(TextColour colour_code, const std::string &string)
Handle the printing of text entered into the console or redirected there by any other means.
Console functions used outside of the console code.
Globally used console related types.
static const TextColour CC_ERROR
Colour for error lines.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
void ShowFramerateWindow()
Open the general framerate window.
static TimingMeasurement GetPerformanceTimer()
Return a timestamp with TIMESTAMP_PRECISION ticks per second precision.
void ProcessPendingPerformanceMeasurements()
This drains the PFE_SOUND measurement data queue into _pf_data.
void ShowFrametimeGraphWindow(PerformanceElement elem)
Open a graph window for a performance element.
void ConPrintFramerate()
Print performance statistics to game console.
Types for recording game performance data.
PerformanceElement
Elements of game performance that can be measured.
@ PFE_AI6
AI execution for player slot 7.
@ PFE_AI1
AI execution for player slot 2.
@ PFE_GAMELOOP
Speed of gameloop processing.
@ PFE_AI9
AI execution for player slot 10.
@ PFE_GL_SHIPS
Time spent processing ships.
@ PFE_AI3
AI execution for player slot 4.
@ PFE_AI4
AI execution for player slot 5.
@ PFE_AI11
AI execution for player slot 12.
@ PFE_AI8
AI execution for player slot 9.
@ PFE_GAMESCRIPT
Game script execution.
@ PFE_VIDEO
Speed of painting drawn video buffer.
@ PFE_GL_LINKGRAPH
Time spent waiting for link graph background jobs.
@ PFE_AI13
AI execution for player slot 14.
@ PFE_AI7
AI execution for player slot 8.
@ PFE_GL_AIRCRAFT
Time spent processing aircraft.
@ PFE_GL_ECONOMY
Time spent processing cargo movement.
@ PFE_AI0
AI execution for player slot 1.
@ PFE_DRAWING
Speed of drawing world and GUI.
@ PFE_AI12
AI execution for player slot 13.
@ PFE_AI2
AI execution for player slot 3.
@ PFE_AI10
AI execution for player slot 11.
@ PFE_GL_LANDSCAPE
Time spent processing other world features.
@ PFE_GL_ROADVEHS
Time spend processing road vehicles.
@ PFE_GL_TRAINS
Time spent processing trains.
@ PFE_MAX
End of enum, must be last.
@ PFE_ALLSCRIPTS
Sum of all GS/AI scripts.
@ PFE_SOUND
Speed of mixing audio samples.
@ PFE_DRAWWORLD
Time spent drawing world viewports in GUI.
@ PFE_AI14
AI execution for player slot 15.
@ PFE_AI5
AI execution for player slot 6.
uint64_t TimingMeasurement
Type used to hold a performance timing measurement.
Base functions for all Games.
The GameInstance tracks games.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Functions related to the gfx engine.
@ FS_SMALL
Index of the small font in the font tables.
@ FS_NORMAL
Index of the normal font in the font tables.
@ SA_LEFT
Left align the text.
@ SA_RIGHT
Right align the text (must be a single bit).
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
@ SA_CENTER
Center both horizontally and vertically.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
const TimingMeasurement TIMESTAMP_PRECISION
Units a second is divided into in performance measurements
static const double GL_RATE
Game loop rate, cycles per second
const int NUM_FRAMERATE_POINTS
Number of data points to keep in buffer for each performance measurement.
PerformanceData _pf_data[PFE_MAX]
Storage for all performance element measurements.
size_t GetSoundPoolAllocatedMemory()
Get size of memory allocated to sound effects.
Functions related to NewGRF provided sounds.
static constexpr PixelColour PC_DARK_RED
Dark red palette colour.
static constexpr PixelColour PC_DARK_GREY
Dark grey palette colour.
static constexpr PixelColour PC_BLACK
Black palette colour.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static bool IsValidAiID(auto index)
Is this company a valid company, controlled by the computer (a NoAI program)?
Dimensions (a width and height) of a rectangle in 2D.
void OnResize() override
Called after the window got resized.
void DrawElementTimesColumn(const Rect &r, StringID heading_str, std::span< const CachedDecimal > values) const
Render a column of formatted average durations.
CachedDecimal rate_drawing
cached drawing frame rate
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
std::array< CachedDecimal, PFE_MAX > times_longterm
cached long term average times
std::array< CachedDecimal, PFE_MAX > times_shortterm
cached short term average times
CachedDecimal speed_gameloop
cached game loop speed factor
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
const IntervalTimer< TimerWindow > update_interval
Update the window on a regular interval.
CachedDecimal rate_gameloop
cached game loop tick rate
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
static constexpr int MIN_ELEMENTS
smallest number of elements to display
int horizontal_scale
number of half-second units horizontally
void OnRealtimeTick(uint delta_ms) override
Called periodically.
void UpdateScale()
Recalculate the graph scaling factors based on current recorded data.
const IntervalTimer< TimerWindow > update_interval
Update the scaling on a regular interval.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Dimension graph_size
size of the main graph area (excluding axis labels)
PerformanceElement element
what element this window renders graph for
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
static T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
Scale and interpolate a value from a source range into a destination range.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
int vertical_scale
number of TIMESTAMP_PRECISION units vertically
Colour for pixel/line drawing.
static Company * Get(auto index)
Specification of a rectangle with absolute coordinates of all edges.
High level window description.
Number to differentiate different windows of the same class.
Data structure for an opened window.
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
ResizeInfo resize
Resize information.
bool IsShaded() const
Is window shaded currently?
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition of Interval and OneShot timers.
Definition of the Window system.
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
Twindow * AllocateWindowDescFront(WindowDesc &desc, WindowNumber window_number, Targs... extra_arguments)
Open a new window.
@ WDP_AUTO
Find a place automatically.
@ WC_FRAMETIME_GRAPH
Frame time graph; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
@ WC_FRAMERATE_DISPLAY
Framerate display; Window numbers:
Functions related to zooming.