35 static std::mutex _sound_perf_lock;
36 static std::atomic<bool> _sound_perf_pending;
37 static std::vector<TimingMeasurement> _sound_perf_measurements;
77 explicit PerformanceData(
double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
82 this->durations[this->next_index] = end_time - start_time;
83 this->timestamps[this->next_index] = start_time;
84 this->prev_index = this->next_index;
85 this->next_index += 1;
93 this->timestamps[this->next_index] = this->acc_timestamp;
94 this->durations[this->next_index] = this->acc_duration;
95 this->prev_index = this->next_index;
96 this->next_index += 1;
100 this->acc_duration = 0;
101 this->acc_timestamp = start_time;
107 this->acc_duration += duration;
113 if (this->durations[this->prev_index] != INVALID_DURATION) {
114 this->timestamps[this->next_index] = start_time;
115 this->durations[this->next_index] = INVALID_DURATION;
116 this->prev_index = this->next_index;
117 this->next_index += 1;
119 this->num_valid += 1;
126 count = std::min(count, this->num_valid);
128 int first_point = this->prev_index - count;
133 for (
int i = first_point; i < first_point + count; i++) {
135 if (d != INVALID_DURATION) {
143 if (count == 0)
return 0;
151 int point = this->prev_index;
152 int last_point = this->next_index - this->num_valid;
166 while (point != last_point) {
168 if (this->durations[point] != INVALID_DURATION) {
169 total += last - this->timestamps[point];
172 last = this->timestamps[point];
178 if (total == 0 || count == 0)
return 0;
233 using namespace std::chrono;
234 return (
TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
269 std::lock_guard lk(_sound_perf_lock);
271 _sound_perf_measurements.push_back(this->start_time);
272 _sound_perf_measurements.push_back(end);
273 _sound_perf_pending.store(
true, std::memory_order_release);
368 static const char * GetAIName(
int ai_index)
371 return Company::Get(ai_index)->ai_info->GetName().c_str();
375 static constexpr
NWidgetPart _framerate_window_widgets[] = {
420 inline void SetRate(
double value,
double target)
422 const double threshold_good = target * 0.95;
423 const double threshold_bad = target * 2 / 3;
424 this->value = (uint32_t)(value * 100);
425 this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
428 inline void SetTime(
double value,
double target)
430 const double threshold_good = target / 3;
431 const double threshold_bad = target;
432 this->value = (uint32_t)(value * 100);
433 this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
436 inline void InsertDParams(uint n)
const
455 this->showing_memory =
true;
457 this->num_displayed = this->num_active;
466 if (this->small != this->
IsShaded()) {
468 this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
483 bool have_script =
false;
486 if (this->small)
return;
500 if (this->showing_memory != have_script) {
501 NWidgetStacked *plane = this->GetWidget<NWidgetStacked>(WID_FRW_SEL_MEMORY);
503 this->showing_memory = have_script;
506 if (new_active != this->num_active) {
507 this->num_active = new_active;
510 sb->
SetCapacity(std::min(this->num_displayed, this->num_active));
515 void SetStringParameters(
WidgetID widget)
const override
518 case WID_FRW_CAPTION:
520 if (!this->small)
break;
522 this->rate_gameloop.InsertDParams(1);
523 this->speed_gameloop.InsertDParams(3);
526 case WID_FRW_RATE_GAMELOOP:
528 this->rate_gameloop.InsertDParams(1);
530 case WID_FRW_RATE_DRAWING:
532 this->rate_drawing.InsertDParams(1);
534 case WID_FRW_RATE_FACTOR:
535 this->speed_gameloop.InsertDParams(0);
537 case WID_FRW_INFO_DATA_POINTS:
546 case WID_FRW_RATE_GAMELOOP:
552 case WID_FRW_RATE_DRAWING:
558 case WID_FRW_RATE_FACTOR:
564 case WID_FRW_TIMES_NAMES: {
570 if (
_pf_data[e].num_valid == 0)
continue;
579 size.width = std::max(size.width, line_size.width);
584 case WID_FRW_TIMES_CURRENT:
585 case WID_FRW_TIMES_AVERAGE:
586 case WID_FRW_ALLOCSIZE: {
591 size.width = std::max(size.width, item_size.width);
605 int drawable = this->num_displayed;
610 if (
_pf_data[e].num_valid == 0)
continue;
614 values[e].InsertDParams(0);
618 if (drawable == 0)
break;
623 void DrawElementAllocationsColumn(
const Rect &r)
const
627 int drawable = this->num_displayed;
632 if (
_pf_data[e].num_valid == 0)
continue;
644 if (drawable == 0)
break;
649 if (drawable == 0)
break;
654 void DrawWidget(
const Rect &r,
WidgetID widget)
const override
657 case WID_FRW_TIMES_NAMES: {
661 int drawable = this->num_displayed;
664 if (
_pf_data[e].num_valid == 0)
continue;
669 DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING,
SA_LEFT);
677 if (drawable == 0)
break;
682 case WID_FRW_TIMES_CURRENT:
686 case WID_FRW_TIMES_AVERAGE:
690 case WID_FRW_ALLOCSIZE:
691 DrawElementAllocationsColumn(r);
696 void OnClick([[maybe_unused]]
Point pt,
WidgetID widget, [[maybe_unused]]
int click_count)
override
699 case WID_FRW_TIMES_NAMES:
700 case WID_FRW_TIMES_CURRENT:
701 case WID_FRW_TIMES_AVERAGE: {
705 if (line != INT32_MAX) {
709 if (
_pf_data[e].num_valid > 0) line--;
723 auto *wid = this->GetWidget<NWidgetResizeBase>(WID_FRW_TIMES_NAMES);
730 WDP_AUTO,
"framerate_display", 0, 0,
733 _framerate_window_widgets
738 static constexpr
NWidgetPart _frametime_graph_window_widgets[] = {
761 this->horizontal_scale = 4;
768 void SetStringParameters(
WidgetID widget)
const override
771 case WID_FGW_CAPTION:
773 SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
785 if (widget == WID_FGW_GRAPH) {
792 graph_size.height = std::max(100u, 10 * (size_ms_label.height + 1));
797 size.width += size_ms_label.width + 2;
798 size.height += size_s_label.height + 2;
813 static const std::initializer_list<ScaleDef> hscales = {
820 for (
const auto &sc : hscales) {
821 if (range < sc.range) this->horizontal_scale = sc.scale;
828 static const std::initializer_list<TimingMeasurement> vscales = {
839 for (
const auto &sc : vscales) {
840 if (range < sc) this->vertical_scale = (int)sc;
858 this->horizontal_scale = 4;
860 for (
int i = 1; i < num_valid; i++) {
865 if (value == PerformanceData::INVALID_DURATION) {
867 lastts = timestamps[point];
870 if (value > peak_value) peak_value = value;
874 time_sum += lastts - timestamps[point];
875 lastts = timestamps[point];
878 if (count == 60) this->SelectHorizontalScale(time_sum);
881 if (count >= 60 && time_sum >= (this->horizontal_scale + 2) *
TIMESTAMP_PRECISION / 2)
break;
884 this->SelectVerticalScale(peak_value);
899 static inline T
Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
901 T dst_diff = dst_max - dst_min;
902 T src_diff = src_max - src_min;
903 return (value - src_min) * dst_diff / src_diff + dst_min;
906 void DrawWidget(
const Rect &r,
WidgetID widget)
const override
908 if (widget == WID_FGW_GRAPH) {
913 const int x_zero = r.right - (int)this->graph_size.width;
914 const int x_max = r.right;
915 const int y_zero = r.top + (
int)this->graph_size.height;
916 const int y_max = r.top;
925 const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
927 const uint horz_divisions = this->horizontal_scale / horz_div_scl;
929 const uint vert_divisions = 10;
932 for (uint division = 0; division < vert_divisions; division++) {
933 int y =
Scinterlate(y_zero, y_max, 0, (
int)vert_divisions, (
int)division);
934 GfxDrawLine(x_zero, y, x_max, y, c_grid);
935 if (division % 2 == 0) {
946 for (uint division = horz_divisions; division > 0; division--) {
947 int x =
Scinterlate(x_zero, x_max, 0, (
int)horz_divisions, (
int)horz_divisions - (
int)division);
948 GfxDrawLine(x, y_max, x, y_zero, c_grid);
949 if (division % 2 == 0) {
950 SetDParam(0, division * horz_div_scl / 2);
958 (int)Scinterlate<int64_t>(y_zero, y_max, 0, this->vertical_scale, durations[point])
964 Point peak_point = { 0, 0 };
967 int points_drawn = 0;
974 if (value == PerformanceData::INVALID_DURATION) {
976 lastts = timestamps[point];
981 time_sum += lastts - timestamps[point];
982 lastts = timestamps[point];
984 if (time_sum > draw_horz_scale)
break;
988 (int)Scinterlate<int64_t>(x_zero, x_max, 0, (int64_t)draw_horz_scale, (int64_t)draw_horz_scale - (int64_t)time_sum),
989 (
int)Scinterlate<int64_t>(y_zero, y_max, 0, (int64_t)draw_vert_scale, (int64_t)value)
991 if (newpoint.x > lastpoint.x)
continue;
992 GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
993 lastpoint = newpoint;
998 if (value > peak_value) {
1000 peak_point = newpoint;
1005 if (points_drawn > 0 && peak_value >
TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
1007 GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
1010 if (peak_point.x - x_zero > (
int)this->graph_size.width / 2) {
1020 static WindowDesc _frametime_graph_window_desc(
1021 WDP_AUTO,
"frametime_graph", 140, 90,
1024 _frametime_graph_window_widgets
1032 AllocateWindowDescFront<FramerateWindow>(_framerate_display_desc, 0);
1038 if (elem < PFE_FIRST || elem >=
PFE_MAX)
return;
1039 AllocateWindowDescFront<FrametimeGraphWindow>(_frametime_graph_window_desc, elem,
true);
1049 IConsolePrint(TC_SILVER,
"Based on num. data points: {} {} {}", count1, count2, count3);
1051 static const std::array<std::string_view, PFE_MAX> MEASUREMENT_NAMES = {
1053 " GL station ticks",
1055 " GL road vehicle ticks",
1057 " GL aircraft ticks",
1058 " GL landscape ticks",
1059 " GL link graph delays",
1061 " Viewport drawing",
1064 "AI/GS scripts total",
1067 std::string ai_name_buf;
1069 bool printed_anything =
false;
1073 if (pf.num_valid == 0)
continue;
1074 IConsolePrint(TC_GREEN,
"{} rate: {:.2f}fps (expected: {:.2f}fps)",
1075 MEASUREMENT_NAMES[e],
1078 printed_anything =
true;
1083 if (pf.num_valid == 0)
continue;
1084 std::string_view name;
1086 name = MEASUREMENT_NAMES[e];
1088 ai_name_buf = fmt::format(
"AI {} {}", e -
PFE_AI0 + 1, GetAIName(e -
PFE_AI0));
1091 IConsolePrint(TC_LIGHT_BLUE,
"{} times: {:.2f}ms {:.2f}ms {:.2f}ms",
1093 pf.GetAverageDurationMilliseconds(count1),
1094 pf.GetAverageDurationMilliseconds(count2),
1095 pf.GetAverageDurationMilliseconds(count3));
1096 printed_anything =
true;
1099 if (!printed_anything) {
1113 if (_sound_perf_pending.load(std::memory_order_acquire)) {
1114 std::lock_guard lk(_sound_perf_lock);
1115 for (
size_t i = 0; i < _sound_perf_measurements.size(); i += 2) {
1118 _sound_perf_measurements.clear();
1119 _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.
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, int 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.
@ 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.
@ FS_SMALL
Index of the small font in the font tables.
@ FS_NORMAL
Index of the normal font in the font tables.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
@ TC_IS_PALETTE_COLOUR
Colour value is already a real palette colour index, not an index of a StringColour.
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)
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.
static const uint8_t PC_DARK_GREY
Dark grey palette colour.
static const uint8_t PC_BLACK
Black palette colour.
static const uint8_t PC_DARK_RED
Dark red palette colour.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
This file contains all sprite-related enums and defines.
Functions related to low-level strings.
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
void SetDParamMaxDigits(size_t n, uint count, FontSize size)
Set DParam n to 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.
GUISettings gui
settings related to the GUI
static bool IsValidAiID(size_t 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.
CachedDecimal rate_drawing
cached drawing frame rate
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
Called periodically.
IntervalTimer< TimerWindow > update_interval
Update the window on a regular interval.
void DrawElementTimesColumn(const Rect &r, StringID heading_str, const CachedDecimal *values) const
Render a column of formatted average durations.
CachedDecimal speed_gameloop
cached game loop speed factor
CachedDecimal rate_gameloop
cached game loop tick rate
CachedDecimal times_longterm[PFE_MAX]
cached long term average times
static constexpr int MIN_ELEMENTS
smallest number of elements to display
CachedDecimal times_shortterm[PFE_MAX]
cached short term average times
int horizontal_scale
number of half-second units horizontally
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
Called periodically.
void UpdateScale()
Recalculate the graph scaling factors based on current recorded data.
IntervalTimer< TimerWindow > update_interval
Update the scaling on a regular interval.
Dimension graph_size
size of the main graph area (excluding axis labels)
PerformanceElement element
what element this window renders graph for
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.
int vertical_scale
number of TIMESTAMP_PRECISION units vertically
uint16_t refresh_rate
How often we refresh the screen (time between draw-ticks).
Coordinates of a point in 2D.
static Titem * Get(size_t index)
Returns Titem with given index.
Specification of a rectangle with absolute coordinates of all edges.
High level window description.
Data structure for an opened window.
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
ResizeInfo resize
Resize information.
int scale
Scale of this window – used to determine how to resize.
bool IsShaded() const
Is window shaded currently?
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.
@ WDP_AUTO
Find a place automatically.
int32_t WindowNumber
Number to differentiate different windows of the same class.
@ WC_FRAMETIME_GRAPH
Frame time graph; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
@ WC_FRAMERATE_DISPLAY
Framerate display; Window numbers: