71 explicit PerformanceData(
double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
76 this->durations[this->next_index] = end_time - start_time;
77 this->timestamps[this->next_index] = start_time;
78 this->prev_index = this->next_index;
79 this->next_index += 1;
80 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
81 this->num_valid =
min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
87 this->timestamps[this->next_index] = this->acc_timestamp;
88 this->durations[this->next_index] = this->acc_duration;
89 this->prev_index = this->next_index;
90 this->next_index += 1;
91 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
92 this->num_valid =
min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
94 this->acc_duration = 0;
95 this->acc_timestamp = start_time;
101 this->acc_duration += duration;
107 if (this->durations[this->prev_index] != INVALID_DURATION) {
108 this->timestamps[this->next_index] = start_time;
109 this->durations[this->next_index] = INVALID_DURATION;
110 this->prev_index = this->next_index;
111 this->next_index += 1;
112 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
113 this->num_valid += 1;
120 count =
min(count, this->num_valid);
122 int first_point = this->prev_index - count;
127 for (
int i = first_point; i < first_point + count; i++) {
129 if (d != INVALID_DURATION) {
137 if (count == 0)
return 0;
145 int point = this->prev_index;
146 int last_point = this->next_index - this->num_valid;
156 while (point != last_point) {
158 if (this->durations[point] != INVALID_DURATION) {
159 total += last - this->timestamps[point];
162 last = this->timestamps[point];
163 if (total >= TIMESTAMP_PRECISION)
break;
165 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
168 if (total == 0 || count == 0)
return 0;
169 return (
double)count * TIMESTAMP_PRECISION / total;
223 using namespace std::chrono;
224 return (
TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
343 static const char * GetAIName(
int ai_index)
350 static const NWidgetPart _framerate_window_widgets[] = {
359 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_GAMELOOP),
SetDataTip(STR_FRAMERATE_RATE_GAMELOOP, STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP),
360 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_DRAWING),
SetDataTip(STR_FRAMERATE_RATE_BLITTER, STR_FRAMERATE_RATE_BLITTER_TOOLTIP),
361 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_FACTOR),
SetDataTip(STR_FRAMERATE_SPEED_FACTOR, STR_FRAMERATE_SPEED_FACTOR_TOOLTIP),
396 inline void SetRate(
double value,
double target)
398 const double threshold_good = target * 0.95;
399 const double threshold_bad = target * 2 / 3;
400 value =
min(9999.99, value);
401 this->value = (uint32)(value * 100);
402 this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
405 inline void SetTime(
double value,
double target)
407 const double threshold_good = target / 3;
408 const double threshold_bad = target;
409 value =
min(9999.99, value);
410 this->value = (uint32)(value * 100);
411 this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
414 inline void InsertDParams(uint n)
const 427 static const int VSPACING = 3;
428 static const int MIN_ELEMENTS = 5;
432 this->InitNested(number);
433 this->small = this->IsShaded();
434 this->showing_memory =
true;
436 this->num_displayed = this->num_active;
437 this->next_update.SetInterval(100);
445 bool elapsed = this->next_update.
Elapsed(delta_ms);
448 if (this->small != this->IsShaded()) {
449 this->small = this->IsShaded();
450 this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->
SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
457 this->next_update.SetInterval(100);
464 bool have_script =
false;
467 if (this->small)
return;
481 if (this->showing_memory != have_script) {
482 NWidgetStacked *plane = this->GetWidget<NWidgetStacked>(WID_FRW_SEL_MEMORY);
484 this->showing_memory = have_script;
487 if (new_active != this->num_active) {
488 this->num_active = new_active;
489 Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
499 case WID_FRW_CAPTION:
501 if (!this->small)
break;
503 this->rate_gameloop.InsertDParams(1);
504 this->speed_gameloop.InsertDParams(3);
507 case WID_FRW_RATE_GAMELOOP:
509 this->rate_gameloop.InsertDParams(1);
511 case WID_FRW_RATE_DRAWING:
513 this->rate_drawing.InsertDParams(1);
515 case WID_FRW_RATE_FACTOR:
516 this->speed_gameloop.InsertDParams(0);
518 case WID_FRW_INFO_DATA_POINTS:
527 case WID_FRW_RATE_GAMELOOP:
533 case WID_FRW_RATE_DRAWING:
539 case WID_FRW_RATE_FACTOR:
545 case WID_FRW_TIMES_NAMES: {
551 if (
_pf_data[e].num_valid == 0)
continue;
560 size->width =
max(size->width, line_size.width);
565 case WID_FRW_TIMES_CURRENT:
566 case WID_FRW_TIMES_AVERAGE:
567 case WID_FRW_ALLOCSIZE: {
572 size->width =
max(size->width, item_size.width);
584 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
586 int drawable = this->num_displayed;
591 if (
_pf_data[e].num_valid == 0)
continue;
595 values[e].InsertDParams(0);
599 if (drawable == 0)
break;
604 void DrawElementAllocationsColumn(
const Rect &r)
const 606 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
608 int drawable = this->num_displayed;
613 if (
_pf_data[e].num_valid == 0)
continue;
625 if (drawable == 0)
break;
630 if (drawable == 0)
break;
638 case WID_FRW_TIMES_NAMES: {
640 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
642 int drawable = this->num_displayed;
645 if (
_pf_data[e].num_valid == 0)
continue;
650 DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING,
SA_LEFT);
658 if (drawable == 0)
break;
663 case WID_FRW_TIMES_CURRENT:
665 DrawElementTimesColumn(r, STR_FRAMERATE_CURRENT, this->times_shortterm);
667 case WID_FRW_TIMES_AVERAGE:
669 DrawElementTimesColumn(r, STR_FRAMERATE_AVERAGE, this->times_longterm);
671 case WID_FRW_ALLOCSIZE:
672 DrawElementAllocationsColumn(r);
680 case WID_FRW_TIMES_NAMES:
681 case WID_FRW_TIMES_CURRENT:
682 case WID_FRW_TIMES_AVERAGE: {
684 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
686 if (line != INT_MAX) {
690 if (
_pf_data[e].num_valid > 0) line--;
704 auto *wid = this->GetWidget<NWidgetResizeBase>(WID_FRW_TIMES_NAMES);
705 this->num_displayed = (wid->current_y - wid->min_y - VSPACING) /
FONT_HEIGHT_NORMAL - 1;
706 this->GetScrollbar(WID_FRW_SCROLLBAR)->SetCapacity(this->num_displayed);
711 WDP_AUTO,
"framerate_display", 0, 0,
714 _framerate_window_widgets,
lengthof(_framerate_window_widgets)
719 static const NWidgetPart _frametime_graph_window_widgets[] = {
743 this->horizontal_scale = 4;
745 this->next_scale_update.SetInterval(1);
747 this->InitNested(number);
753 case WID_FGW_CAPTION:
755 SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
767 if (widget == WID_FGW_GRAPH) {
774 graph_size.height = max<uint>(100, 10 * (size_ms_label.height + 1));
776 graph_size.width = 2 * graph_size.height;
779 size->width += size_ms_label.width + 2;
780 size->height += size_s_label.height + 2;
789 static const ScaleDef hscales[] = {
796 for (
const ScaleDef *sc = hscales; sc < hscales +
lengthof(hscales); sc++) {
797 if (range < sc->range) this->horizontal_scale = sc->scale;
807 TIMESTAMP_PRECISION * 5,
809 TIMESTAMP_PRECISION / 2,
810 TIMESTAMP_PRECISION / 5,
811 TIMESTAMP_PRECISION / 10,
812 TIMESTAMP_PRECISION / 50,
813 TIMESTAMP_PRECISION / 200,
816 if (range < *sc) this->vertical_scale = (int)*sc;
834 this->horizontal_scale = 4;
836 for (
int i = 1; i < num_valid; i++) {
841 if (value == PerformanceData::INVALID_DURATION) {
843 lastts = timestamps[point];
846 if (value > peak_value) peak_value = value;
850 time_sum += lastts - timestamps[point];
851 lastts = timestamps[point];
857 if (count >= 60 && time_sum >= (this->horizontal_scale + 2) *
TIMESTAMP_PRECISION / 2)
break;
860 this->SelectVerticalScale(peak_value);
867 if (this->next_scale_update.
Elapsed(delta_ms)) {
868 this->next_scale_update.SetInterval(500);
875 static inline T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
877 T dst_diff = dst_max - dst_min;
878 T src_diff = src_max - src_min;
879 return (value - src_min) * dst_diff / src_diff + dst_min;
884 if (widget == WID_FGW_GRAPH) {
889 const int x_zero = r.right - (int)this->graph_size.width;
890 const int x_max = r.right;
891 const int y_zero = r.top + (
int)this->graph_size.height;
892 const int y_max = r.top;
901 const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
903 const uint horz_divisions = this->horizontal_scale / horz_div_scl;
905 const uint vert_divisions = 10;
908 for (uint division = 0; division < vert_divisions; division++) {
909 int y = Scinterlate(y_zero, y_max, 0, (
int)vert_divisions, (
int)division);
910 GfxDrawLine(x_zero, y, x_max, y, c_grid);
911 if (division % 2 == 0) {
922 for (uint division = horz_divisions; division > 0; division--) {
923 int x = Scinterlate(x_zero, x_max, 0, (
int)horz_divisions, (
int)horz_divisions - (
int)division);
924 GfxDrawLine(x, y_max, x, y_zero, c_grid);
925 if (division % 2 == 0) {
926 SetDParam(0, division * horz_div_scl / 2);
934 (int)Scinterlate<int64>(y_zero, y_max, 0, this->vertical_scale, durations[point])
940 Point peak_point = { 0, 0 };
943 int points_drawn = 0;
947 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
950 if (value == PerformanceData::INVALID_DURATION) {
952 lastts = timestamps[point];
957 time_sum += lastts - timestamps[point];
958 lastts = timestamps[point];
960 if (time_sum > draw_horz_scale)
break;
964 (int)Scinterlate<int64>(x_zero, x_max, 0, (int64)draw_horz_scale, (int64)draw_horz_scale - (int64)time_sum),
965 (
int)Scinterlate<int64>(y_zero, y_max, 0, (int64)draw_vert_scale, (int64)value)
967 assert(newpoint.x <= lastpoint.x);
968 GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
969 lastpoint = newpoint;
974 if (value > peak_value) {
976 peak_point = newpoint;
981 if (points_drawn > 0 && peak_value >
TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
983 GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
986 if (peak_point.x - x_zero > (
int)this->graph_size.width / 2) {
996 static WindowDesc _frametime_graph_window_desc(
997 WDP_AUTO,
"frametime_graph", 140, 90,
1000 _frametime_graph_window_widgets,
lengthof(_frametime_graph_window_widgets)
1008 AllocateWindowDescFront<FramerateWindow>(&_framerate_display_desc, 0);
1014 if (elem < PFE_FIRST || elem >=
PFE_MAX)
return;
1015 AllocateWindowDescFront<FrametimeGraphWindow>(&_frametime_graph_window_desc, elem,
true);
1025 IConsolePrintF(TC_SILVER,
"Based on num. data points: %d %d %d", count1, count2, count3);
1027 static const char *MEASUREMENT_NAMES[
PFE_MAX] = {
1029 " GL station ticks",
1031 " GL road vehicle ticks",
1033 " GL aircraft ticks",
1034 " GL landscape ticks",
1035 " GL link graph delays",
1037 " Viewport drawing",
1040 "AI/GS scripts total",
1043 char ai_name_buf[128];
1047 bool printed_anything =
false;
1051 if (pf.num_valid == 0)
continue;
1053 MEASUREMENT_NAMES[*e],
1056 printed_anything =
true;
1061 if (pf.num_valid == 0)
continue;
1064 name = MEASUREMENT_NAMES[e];
1071 pf.GetAverageDurationMilliseconds(count1),
1072 pf.GetAverageDurationMilliseconds(count2),
1073 pf.GetAverageDurationMilliseconds(count3));
1074 printed_anything =
true;
1077 if (!printed_anything) {
Functions related to OTTD's strings.
Time spent processing cargo movement.
void IConsoleWarning(const char *string)
It is possible to print warnings to the console.
Definition of stuff that is very close to a company, like the company struct itself.
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...
PerformanceElement element
what element this window renders graph for
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
int vertical_scale
number of TIMESTAMP_PRECISION units vertically
Dimension graph_size
size of the main graph area (excluding axis labels)
High level window description.
int horizontal_scale
number of half-second units horizontally
void OnRealtimeTick(uint delta_ms) override
Called periodically.
static Titem * Get(size_t index)
Returns Titem with given index.
Framerate display; Window numbers:
const TimingMeasurement TIMESTAMP_PRECISION
Units a second is divided into in performance measurements
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
bool Elapsed(uint delta)
Test if a timer has elapsed.
AI execution for player slot 5.
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
void OnResize() override
Called after the window got resized.
Types for recording game performance data.
PerformanceElement
Elements of game performance that can be measured.
void UpdateScale()
Recalculate the graph scaling factors based on current recorded data.
End of enum, must be last.
#define lastof(x)
Get the last element of an fixed size array.
The AIInstance tracks an AI.
static T max(const T a, const T b)
Returns the maximum of two values.
Speed of drawing world and GUI.
CachedDecimal speed_gameloop
cached game loop speed factor
Time spent processing aircraft.
AI execution for player slot 12.
Speed of gameloop processing.
static TimingMeasurement GetPerformanceTimer()
Return a timestamp with TIMESTAMP_PRECISION ticks per second precision.
Functions, definitions and such used only by the GUI.
Force the alignment, i.e. don't swap for RTL languages.
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
Data structure for an opened window.
void SetDParamStr(uint n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Functions related to low-level strings.
static const uint8 PC_DARK_GREY
Dark grey palette colour.
#define FONT_HEIGHT_SMALL
Height of characters in the small (FS_SMALL) font.
AI execution for player slot 2.
AI execution for player slot 9.
static bool IsValidAiID(size_t index)
Is this company a valid company, controlled by the computer (a NoAI program)?
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
Time spend processing road vehicles.
#define FONT_HEIGHT_NORMAL
Height of characters in the normal (FS_NORMAL) font.
Functions related to the gfx engine.
void CDECL IConsolePrintF(TextColour colour_code, const char *format,...)
Handle the printing of text entered into the console or redirected there by any other means...
AI execution for player slot 15.
Center both horizontally and vertically.
A number of safeguards to prevent using unsafe methods.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
AI execution for player slot 7.
AI execution for player slot 6.
void ShowFramerateWindow()
Open the general framerate window.
AI execution for player slot 14.
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
The GameInstance tracks games.
Console functions used outside of the console code.
void ConPrintFramerate()
Print performance statistics to game console.
void ShowFrametimeGraphWindow(PerformanceElement elem)
Open a graph window for a performance element.
const int NUM_FRAMERATE_POINTS
Number of data points to keep in buffer for each performance measurement.
int DrawString(int left, int right, int top, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Time spent processing other world features.
static class GameInstance * GetInstance()
Get the current active instance.
void OnRealtimeTick(uint delta_ms) override
Called periodically.
#define lengthof(x)
Return the length of an fixed size array.
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
static T min(const T a, const T b)
Returns the minimum of two values.
Frame time graph; Window numbers:
GUITimer next_scale_update
interval for next scale update
uint32 StringID
Numeric value that represents a string, independent of the selected language.
static const uint8 PC_BLACK
Black palette colour.
AI execution for player slot 1.
uint64 TimingMeasurement
Type used to hold a performance timing measurement.
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
Return the string dimension in pixels.
CachedDecimal rate_drawing
cached drawing frame rate
No window, redirects to WC_MAIN_WINDOW.
static const double GL_RATE
Game loop rate, cycles per second
static const uint8 PC_DARK_RED
Dark red palette colour.
void DrawElementTimesColumn(const Rect &r, StringID heading_str, const CachedDecimal *values) const
Render a column of formatted average durations.
AI execution for player slot 10.
Time spent processing ships.
Time spent drawing world viewports in GUI.
Speed of painting drawn video buffer.
AI execution for player slot 8.
Base functions for all Games.
AI execution for player slot 3.
AI execution for player slot 11.
Coordinates of a point in 2D.
Index of the small font in the font tables.
Globally used console related types.
Colour value is already a real palette colour index, not an index of a StringColour.
int32 WindowNumber
Number to differentiate different windows of the same class.
Specification of a rectangle with absolute coordinates of all edges.
Right align the text (must be a single bit).
AI execution for player slot 13.
Window functions not directly related to making/drawing windows.
AIInfo keeps track of all information of an AI, like Author, Description, ...
Find a place automatically.
PerformanceData _pf_data[PFE_MAX]
Storage for all performance element measurements.
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
Resize the window.
Dimensions (a width and height) of a rectangle in 2D.
This file contains all sprite-related enums and defines.
Time spent processing trains.
Time spent waiting for link graph background jobs.
CachedDecimal rate_gameloop
cached game loop tick rate
AI execution for player slot 4.
Sum of all GS/AI scripts.
static void SetDParam(uint n, uint64 v)
Set a string parameter v at index n in the global string parameter array.
Speed of mixing audio samples.