68 explicit PerformanceData(
double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
73 this->durations[this->next_index] = end_time - start_time;
74 this->timestamps[this->next_index] = start_time;
75 this->prev_index = this->next_index;
76 this->next_index += 1;
77 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
78 this->num_valid =
min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
84 this->timestamps[this->next_index] = this->acc_timestamp;
85 this->durations[this->next_index] = this->acc_duration;
86 this->prev_index = this->next_index;
87 this->next_index += 1;
88 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
89 this->num_valid =
min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
91 this->acc_duration = 0;
92 this->acc_timestamp = start_time;
98 this->acc_duration += duration;
104 if (this->durations[this->prev_index] != INVALID_DURATION) {
105 this->timestamps[this->next_index] = start_time;
106 this->durations[this->next_index] = INVALID_DURATION;
107 this->prev_index = this->next_index;
108 this->next_index += 1;
109 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
110 this->num_valid += 1;
117 count =
min(count, this->num_valid);
119 int first_point = this->prev_index - count;
124 for (
int i = first_point; i < first_point + count; i++) {
126 if (d != INVALID_DURATION) {
134 if (count == 0)
return 0;
142 int point = this->prev_index;
143 int last_point = this->next_index - this->num_valid;
153 while (point != last_point) {
155 if (this->durations[point] != INVALID_DURATION) {
156 total += last - this->timestamps[point];
159 last = this->timestamps[point];
160 if (total >= TIMESTAMP_PRECISION)
break;
162 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
165 if (total == 0 || count == 0)
return 0;
166 return (
double)count * TIMESTAMP_PRECISION / total;
220 using namespace std::chrono;
221 return (
TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
340 static const char * GetAIName(
int ai_index)
347 static const NWidgetPart _framerate_window_widgets[] = {
356 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_GAMELOOP),
SetDataTip(STR_FRAMERATE_RATE_GAMELOOP, STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP),
357 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_DRAWING),
SetDataTip(STR_FRAMERATE_RATE_BLITTER, STR_FRAMERATE_RATE_BLITTER_TOOLTIP),
358 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_FACTOR),
SetDataTip(STR_FRAMERATE_SPEED_FACTOR, STR_FRAMERATE_SPEED_FACTOR_TOOLTIP),
389 inline void SetRate(
double value,
double target)
391 const double threshold_good = target * 0.95;
392 const double threshold_bad = target * 2 / 3;
393 value =
min(9999.99, value);
394 this->value = (uint32)(value * 100);
395 this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
398 inline void SetTime(
double value,
double target)
400 const double threshold_good = target / 3;
401 const double threshold_bad = target;
402 value =
min(9999.99, value);
403 this->value = (uint32)(value * 100);
404 this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
407 inline void InsertDParams(uint n)
const 420 static const int VSPACING = 3;
421 static const int MIN_ELEMENTS = 5;
425 this->InitNested(number);
426 this->small = this->IsShaded();
428 this->num_displayed = this->num_active;
429 this->next_update.SetInterval(100);
437 bool elapsed = this->next_update.
Elapsed(delta_ms);
440 if (this->small != this->IsShaded()) {
441 this->small = this->IsShaded();
442 this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->
SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
449 this->next_update.SetInterval(100);
458 if (this->small)
return;
466 if (
_pf_data[e].num_valid > 0) new_active++;
469 if (new_active != this->num_active) {
470 this->num_active = new_active;
471 Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
481 case WID_FRW_CAPTION:
483 if (!this->small)
break;
485 this->rate_gameloop.InsertDParams(1);
486 this->speed_gameloop.InsertDParams(3);
489 case WID_FRW_RATE_GAMELOOP:
491 this->rate_gameloop.InsertDParams(1);
493 case WID_FRW_RATE_DRAWING:
495 this->rate_drawing.InsertDParams(1);
497 case WID_FRW_RATE_FACTOR:
498 this->speed_gameloop.InsertDParams(0);
500 case WID_FRW_INFO_DATA_POINTS:
509 case WID_FRW_RATE_GAMELOOP:
515 case WID_FRW_RATE_DRAWING:
521 case WID_FRW_RATE_FACTOR:
527 case WID_FRW_TIMES_NAMES: {
533 if (
_pf_data[e].num_valid == 0)
continue;
542 size->width =
max(size->width, line_size.width);
547 case WID_FRW_TIMES_CURRENT:
548 case WID_FRW_TIMES_AVERAGE: {
553 size->width =
max(size->width, item_size.width);
565 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
567 int drawable = this->num_displayed;
572 if (
_pf_data[e].num_valid == 0)
continue;
576 values[e].InsertDParams(0);
580 if (drawable == 0)
break;
588 case WID_FRW_TIMES_NAMES: {
590 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
592 int drawable = this->num_displayed;
595 if (
_pf_data[e].num_valid == 0)
continue;
600 DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING,
SA_LEFT);
608 if (drawable == 0)
break;
613 case WID_FRW_TIMES_CURRENT:
615 DrawElementTimesColumn(r, STR_FRAMERATE_CURRENT, this->times_shortterm);
617 case WID_FRW_TIMES_AVERAGE:
619 DrawElementTimesColumn(r, STR_FRAMERATE_AVERAGE, this->times_longterm);
627 case WID_FRW_TIMES_NAMES:
628 case WID_FRW_TIMES_CURRENT:
629 case WID_FRW_TIMES_AVERAGE: {
631 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
633 if (line != INT_MAX) {
637 if (
_pf_data[e].num_valid > 0) line--;
651 auto *wid = this->GetWidget<NWidgetResizeBase>(WID_FRW_TIMES_NAMES);
652 this->num_displayed = (wid->current_y - wid->min_y - VSPACING) /
FONT_HEIGHT_NORMAL - 1;
653 this->GetScrollbar(WID_FRW_SCROLLBAR)->SetCapacity(this->num_displayed);
658 WDP_AUTO,
"framerate_display", 0, 0,
661 _framerate_window_widgets,
lengthof(_framerate_window_widgets)
666 static const NWidgetPart _frametime_graph_window_widgets[] = {
690 this->horizontal_scale = 4;
692 this->next_scale_update.SetInterval(1);
694 this->InitNested(number);
700 case WID_FGW_CAPTION:
702 SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
714 if (widget == WID_FGW_GRAPH) {
721 graph_size.height = max<uint>(100, 10 * (size_ms_label.height + 1));
723 graph_size.width = 2 * graph_size.height;
726 size->width += size_ms_label.width + 2;
727 size->height += size_s_label.height + 2;
736 static const ScaleDef hscales[] = {
743 for (
const ScaleDef *sc = hscales; sc < hscales +
lengthof(hscales); sc++) {
744 if (range < sc->range) this->horizontal_scale = sc->scale;
754 TIMESTAMP_PRECISION * 5,
756 TIMESTAMP_PRECISION / 2,
757 TIMESTAMP_PRECISION / 5,
758 TIMESTAMP_PRECISION / 10,
759 TIMESTAMP_PRECISION / 50,
760 TIMESTAMP_PRECISION / 200,
763 if (range < *sc) this->vertical_scale = (int)*sc;
781 this->horizontal_scale = 4;
783 for (
int i = 1; i < num_valid; i++) {
788 if (value == PerformanceData::INVALID_DURATION) {
790 lastts = timestamps[point];
793 if (value > peak_value) peak_value = value;
797 time_sum += lastts - timestamps[point];
798 lastts = timestamps[point];
804 if (count >= 60 && time_sum >= (this->horizontal_scale + 2) *
TIMESTAMP_PRECISION / 2)
break;
807 this->SelectVerticalScale(peak_value);
814 if (this->next_scale_update.
Elapsed(delta_ms)) {
815 this->next_scale_update.SetInterval(500);
822 static inline T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
824 T dst_diff = dst_max - dst_min;
825 T src_diff = src_max - src_min;
826 return (value - src_min) * dst_diff / src_diff + dst_min;
831 if (widget == WID_FGW_GRAPH) {
836 const int x_zero = r.right - (int)this->graph_size.width;
837 const int x_max = r.right;
838 const int y_zero = r.top + (
int)this->graph_size.height;
839 const int y_max = r.top;
848 const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
850 const uint horz_divisions = this->horizontal_scale / horz_div_scl;
852 const uint vert_divisions = 10;
855 for (uint division = 0; division < vert_divisions; division++) {
856 int y = Scinterlate(y_zero, y_max, 0, (
int)vert_divisions, (
int)division);
857 GfxDrawLine(x_zero, y, x_max, y, c_grid);
858 if (division % 2 == 0) {
869 for (uint division = horz_divisions; division > 0; division--) {
870 int x = Scinterlate(x_zero, x_max, 0, (
int)horz_divisions, (
int)horz_divisions - (
int)division);
871 GfxDrawLine(x, y_max, x, y_zero, c_grid);
872 if (division % 2 == 0) {
873 SetDParam(0, division * horz_div_scl / 2);
881 (int)Scinterlate<int64>(y_zero, y_max, 0, this->vertical_scale, durations[point])
887 Point peak_point = { 0, 0 };
890 int points_drawn = 0;
894 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
897 if (value == PerformanceData::INVALID_DURATION) {
899 lastts = timestamps[point];
904 time_sum += lastts - timestamps[point];
905 lastts = timestamps[point];
907 if (time_sum > draw_horz_scale)
break;
911 (int)Scinterlate<int64>(x_zero, x_max, 0, (int64)draw_horz_scale, (int64)draw_horz_scale - (int64)time_sum),
912 (
int)Scinterlate<int64>(y_zero, y_max, 0, (int64)draw_vert_scale, (int64)value)
914 assert(newpoint.x <= lastpoint.x);
915 GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
916 lastpoint = newpoint;
921 if (value > peak_value) {
923 peak_point = newpoint;
928 if (points_drawn > 0 && peak_value >
TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
930 GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
933 if (peak_point.x - x_zero > (
int)this->graph_size.width / 2) {
943 static WindowDesc _frametime_graph_window_desc(
944 WDP_AUTO,
"frametime_graph", 140, 90,
947 _frametime_graph_window_widgets,
lengthof(_frametime_graph_window_widgets)
955 AllocateWindowDescFront<FramerateWindow>(&_framerate_display_desc, 0);
961 if (elem < PFE_FIRST || elem >=
PFE_MAX)
return;
962 AllocateWindowDescFront<FrametimeGraphWindow>(&_frametime_graph_window_desc, elem,
true);
972 IConsolePrintF(TC_SILVER,
"Based on num. data points: %d %d %d", count1, count2, count3);
974 static const char *MEASUREMENT_NAMES[
PFE_MAX] = {
978 " GL road vehicle ticks",
980 " GL aircraft ticks",
981 " GL landscape ticks",
982 " GL link graph delays",
987 "AI/GS scripts total",
990 char ai_name_buf[128];
994 bool printed_anything =
false;
998 if (pf.num_valid == 0)
continue;
1000 MEASUREMENT_NAMES[*e],
1003 printed_anything =
true;
1008 if (pf.num_valid == 0)
continue;
1011 name = MEASUREMENT_NAMES[e];
1018 pf.GetAverageDurationMilliseconds(count1),
1019 pf.GetAverageDurationMilliseconds(count2),
1020 pf.GetAverageDurationMilliseconds(count3));
1021 printed_anything =
true;
1024 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.
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
Update size and resize step of a widget in the window.
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
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
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.
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.
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.
virtual void SetStringParameters(int widget) const
Initialize string parameters for a widget.
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.
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.
virtual void OnClick(Point pt, int widget, int click_count)
A click with the left mouse button has been made on the window.
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)?
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.
virtual void DrawWidget(const Rect &r, int widget) const
Draw the contents of a nested widget.
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.
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
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.
virtual void DrawWidget(const Rect &r, int widget) const
Draw the contents of a nested widget.
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.
#define lengthof(x)
Return the length of an fixed size array.
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.
virtual void OnResize()
Called after the window got resized.
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.
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
Update size and resize step of a widget in the window.
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.
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
Dimensions (a width and height) of a rectangle in 2D.
This file contains all sprite-related enums and defines.
Time spent processing trains.
virtual void SetStringParameters(int widget) const
Initialize string parameters for a widget.
Time spent waiting for link graph background jobs.
CachedDecimal rate_gameloop
cached game loop tick rate
AI execution for player slot 4.
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
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.