69 explicit PerformanceData(
double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
74 this->durations[this->next_index] = end_time - start_time;
75 this->timestamps[this->next_index] = start_time;
76 this->prev_index = this->next_index;
77 this->next_index += 1;
78 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
79 this->num_valid =
min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
85 this->timestamps[this->next_index] = this->acc_timestamp;
86 this->durations[this->next_index] = this->acc_duration;
87 this->prev_index = this->next_index;
88 this->next_index += 1;
89 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
90 this->num_valid =
min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
92 this->acc_duration = 0;
93 this->acc_timestamp = start_time;
99 this->acc_duration += duration;
105 if (this->durations[this->prev_index] != INVALID_DURATION) {
106 this->timestamps[this->next_index] = start_time;
107 this->durations[this->next_index] = INVALID_DURATION;
108 this->prev_index = this->next_index;
109 this->next_index += 1;
110 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
111 this->num_valid += 1;
118 count =
min(count, this->num_valid);
120 int first_point = this->prev_index - count;
125 for (
int i = first_point; i < first_point + count; i++) {
127 if (d != INVALID_DURATION) {
135 if (count == 0)
return 0;
143 int point = this->prev_index;
144 int last_point = this->next_index - this->num_valid;
154 while (point != last_point) {
156 if (this->durations[point] != INVALID_DURATION) {
157 total += last - this->timestamps[point];
160 last = this->timestamps[point];
161 if (total >= TIMESTAMP_PRECISION)
break;
163 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
166 if (total == 0 || count == 0)
return 0;
167 return (
double)count * TIMESTAMP_PRECISION / total;
221 using namespace std::chrono;
222 return (
TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
341 static const char * GetAIName(
int ai_index)
348 static const NWidgetPart _framerate_window_widgets[] = {
357 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_GAMELOOP),
SetDataTip(STR_FRAMERATE_RATE_GAMELOOP, STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP),
358 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_DRAWING),
SetDataTip(STR_FRAMERATE_RATE_BLITTER, STR_FRAMERATE_RATE_BLITTER_TOOLTIP),
359 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_FACTOR),
SetDataTip(STR_FRAMERATE_SPEED_FACTOR, STR_FRAMERATE_SPEED_FACTOR_TOOLTIP),
394 inline void SetRate(
double value,
double target)
396 const double threshold_good = target * 0.95;
397 const double threshold_bad = target * 2 / 3;
398 value =
min(9999.99, value);
399 this->value = (uint32)(value * 100);
400 this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
403 inline void SetTime(
double value,
double target)
405 const double threshold_good = target / 3;
406 const double threshold_bad = target;
407 value =
min(9999.99, value);
408 this->value = (uint32)(value * 100);
409 this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
412 inline void InsertDParams(uint n)
const 425 static const int VSPACING = 3;
426 static const int MIN_ELEMENTS = 5;
430 this->InitNested(number);
431 this->small = this->IsShaded();
432 this->showing_memory =
true;
434 this->num_displayed = this->num_active;
435 this->next_update.SetInterval(100);
443 bool elapsed = this->next_update.
Elapsed(delta_ms);
446 if (this->small != this->IsShaded()) {
447 this->small = this->IsShaded();
448 this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->
SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
455 this->next_update.SetInterval(100);
462 bool have_script =
false;
465 if (this->small)
return;
479 if (this->showing_memory != have_script) {
480 NWidgetStacked *plane = this->GetWidget<NWidgetStacked>(WID_FRW_SEL_MEMORY);
482 this->showing_memory = have_script;
485 if (new_active != this->num_active) {
486 this->num_active = new_active;
487 Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
497 case WID_FRW_CAPTION:
499 if (!this->small)
break;
501 this->rate_gameloop.InsertDParams(1);
502 this->speed_gameloop.InsertDParams(3);
505 case WID_FRW_RATE_GAMELOOP:
507 this->rate_gameloop.InsertDParams(1);
509 case WID_FRW_RATE_DRAWING:
511 this->rate_drawing.InsertDParams(1);
513 case WID_FRW_RATE_FACTOR:
514 this->speed_gameloop.InsertDParams(0);
516 case WID_FRW_INFO_DATA_POINTS:
525 case WID_FRW_RATE_GAMELOOP:
531 case WID_FRW_RATE_DRAWING:
537 case WID_FRW_RATE_FACTOR:
543 case WID_FRW_TIMES_NAMES: {
549 if (
_pf_data[e].num_valid == 0)
continue;
558 size->width =
max(size->width, line_size.width);
563 case WID_FRW_TIMES_CURRENT:
564 case WID_FRW_TIMES_AVERAGE:
565 case WID_FRW_ALLOCSIZE: {
570 size->width =
max(size->width, item_size.width);
582 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
584 int drawable = this->num_displayed;
589 if (
_pf_data[e].num_valid == 0)
continue;
593 values[e].InsertDParams(0);
597 if (drawable == 0)
break;
602 void DrawElementAllocationsColumn(
const Rect &r)
const 604 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
606 int drawable = this->num_displayed;
611 if (
_pf_data[e].num_valid == 0)
continue;
623 if (drawable == 0)
break;
628 if (drawable == 0)
break;
636 case WID_FRW_TIMES_NAMES: {
638 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
640 int drawable = this->num_displayed;
643 if (
_pf_data[e].num_valid == 0)
continue;
648 DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING,
SA_LEFT);
656 if (drawable == 0)
break;
661 case WID_FRW_TIMES_CURRENT:
663 DrawElementTimesColumn(r, STR_FRAMERATE_CURRENT, this->times_shortterm);
665 case WID_FRW_TIMES_AVERAGE:
667 DrawElementTimesColumn(r, STR_FRAMERATE_AVERAGE, this->times_longterm);
669 case WID_FRW_ALLOCSIZE:
670 DrawElementAllocationsColumn(r);
678 case WID_FRW_TIMES_NAMES:
679 case WID_FRW_TIMES_CURRENT:
680 case WID_FRW_TIMES_AVERAGE: {
682 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
684 if (line != INT_MAX) {
688 if (
_pf_data[e].num_valid > 0) line--;
702 auto *wid = this->GetWidget<NWidgetResizeBase>(WID_FRW_TIMES_NAMES);
703 this->num_displayed = (wid->current_y - wid->min_y - VSPACING) /
FONT_HEIGHT_NORMAL - 1;
704 this->GetScrollbar(WID_FRW_SCROLLBAR)->SetCapacity(this->num_displayed);
709 WDP_AUTO,
"framerate_display", 0, 0,
712 _framerate_window_widgets,
lengthof(_framerate_window_widgets)
717 static const NWidgetPart _frametime_graph_window_widgets[] = {
741 this->horizontal_scale = 4;
743 this->next_scale_update.SetInterval(1);
745 this->InitNested(number);
751 case WID_FGW_CAPTION:
753 SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
765 if (widget == WID_FGW_GRAPH) {
772 graph_size.height = max<uint>(100, 10 * (size_ms_label.height + 1));
774 graph_size.width = 2 * graph_size.height;
777 size->width += size_ms_label.width + 2;
778 size->height += size_s_label.height + 2;
787 static const ScaleDef hscales[] = {
794 for (
const ScaleDef *sc = hscales; sc < hscales +
lengthof(hscales); sc++) {
795 if (range < sc->range) this->horizontal_scale = sc->scale;
805 TIMESTAMP_PRECISION * 5,
807 TIMESTAMP_PRECISION / 2,
808 TIMESTAMP_PRECISION / 5,
809 TIMESTAMP_PRECISION / 10,
810 TIMESTAMP_PRECISION / 50,
811 TIMESTAMP_PRECISION / 200,
814 if (range < *sc) this->vertical_scale = (int)*sc;
832 this->horizontal_scale = 4;
834 for (
int i = 1; i < num_valid; i++) {
839 if (value == PerformanceData::INVALID_DURATION) {
841 lastts = timestamps[point];
844 if (value > peak_value) peak_value = value;
848 time_sum += lastts - timestamps[point];
849 lastts = timestamps[point];
855 if (count >= 60 && time_sum >= (this->horizontal_scale + 2) *
TIMESTAMP_PRECISION / 2)
break;
858 this->SelectVerticalScale(peak_value);
865 if (this->next_scale_update.
Elapsed(delta_ms)) {
866 this->next_scale_update.SetInterval(500);
873 static inline T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
875 T dst_diff = dst_max - dst_min;
876 T src_diff = src_max - src_min;
877 return (value - src_min) * dst_diff / src_diff + dst_min;
882 if (widget == WID_FGW_GRAPH) {
887 const int x_zero = r.right - (int)this->graph_size.width;
888 const int x_max = r.right;
889 const int y_zero = r.top + (
int)this->graph_size.height;
890 const int y_max = r.top;
899 const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
901 const uint horz_divisions = this->horizontal_scale / horz_div_scl;
903 const uint vert_divisions = 10;
906 for (uint division = 0; division < vert_divisions; division++) {
907 int y = Scinterlate(y_zero, y_max, 0, (
int)vert_divisions, (
int)division);
908 GfxDrawLine(x_zero, y, x_max, y, c_grid);
909 if (division % 2 == 0) {
920 for (uint division = horz_divisions; division > 0; division--) {
921 int x = Scinterlate(x_zero, x_max, 0, (
int)horz_divisions, (
int)horz_divisions - (
int)division);
922 GfxDrawLine(x, y_max, x, y_zero, c_grid);
923 if (division % 2 == 0) {
924 SetDParam(0, division * horz_div_scl / 2);
932 (int)Scinterlate<int64>(y_zero, y_max, 0, this->vertical_scale, durations[point])
938 Point peak_point = { 0, 0 };
941 int points_drawn = 0;
945 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
948 if (value == PerformanceData::INVALID_DURATION) {
950 lastts = timestamps[point];
955 time_sum += lastts - timestamps[point];
956 lastts = timestamps[point];
958 if (time_sum > draw_horz_scale)
break;
962 (int)Scinterlate<int64>(x_zero, x_max, 0, (int64)draw_horz_scale, (int64)draw_horz_scale - (int64)time_sum),
963 (
int)Scinterlate<int64>(y_zero, y_max, 0, (int64)draw_vert_scale, (int64)value)
965 assert(newpoint.x <= lastpoint.x);
966 GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
967 lastpoint = newpoint;
972 if (value > peak_value) {
974 peak_point = newpoint;
979 if (points_drawn > 0 && peak_value >
TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
981 GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
984 if (peak_point.x - x_zero > (
int)this->graph_size.width / 2) {
994 static WindowDesc _frametime_graph_window_desc(
995 WDP_AUTO,
"frametime_graph", 140, 90,
998 _frametime_graph_window_widgets,
lengthof(_frametime_graph_window_widgets)
1006 AllocateWindowDescFront<FramerateWindow>(&_framerate_display_desc, 0);
1012 if (elem < PFE_FIRST || elem >=
PFE_MAX)
return;
1013 AllocateWindowDescFront<FrametimeGraphWindow>(&_frametime_graph_window_desc, elem,
true);
1023 IConsolePrintF(TC_SILVER,
"Based on num. data points: %d %d %d", count1, count2, count3);
1025 static const char *MEASUREMENT_NAMES[
PFE_MAX] = {
1027 " GL station ticks",
1029 " GL road vehicle ticks",
1031 " GL aircraft ticks",
1032 " GL landscape ticks",
1033 " GL link graph delays",
1035 " Viewport drawing",
1038 "AI/GS scripts total",
1041 char ai_name_buf[128];
1045 bool printed_anything =
false;
1049 if (pf.num_valid == 0)
continue;
1051 MEASUREMENT_NAMES[*e],
1054 printed_anything =
true;
1059 if (pf.num_valid == 0)
continue;
1062 name = MEASUREMENT_NAMES[e];
1069 pf.GetAverageDurationMilliseconds(count1),
1070 pf.GetAverageDurationMilliseconds(count2),
1071 pf.GetAverageDurationMilliseconds(count3));
1072 printed_anything =
true;
1075 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.