63 explicit PerformanceData(
double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
68 this->durations[this->next_index] = end_time - start_time;
69 this->timestamps[this->next_index] = start_time;
70 this->prev_index = this->next_index;
71 this->next_index += 1;
72 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
73 this->num_valid =
min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
79 this->timestamps[this->next_index] = this->acc_timestamp;
80 this->durations[this->next_index] = this->acc_duration;
81 this->prev_index = this->next_index;
82 this->next_index += 1;
83 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
84 this->num_valid =
min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
86 this->acc_duration = 0;
87 this->acc_timestamp = start_time;
93 this->acc_duration += duration;
99 if (this->durations[this->prev_index] != INVALID_DURATION) {
100 this->timestamps[this->next_index] = start_time;
101 this->durations[this->next_index] = INVALID_DURATION;
102 this->prev_index = this->next_index;
103 this->next_index += 1;
104 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
105 this->num_valid += 1;
112 count =
min(count, this->num_valid);
114 int first_point = this->prev_index - count;
119 for (
int i = first_point; i < first_point + count; i++) {
121 if (d != INVALID_DURATION) {
129 if (count == 0)
return 0;
137 int point = this->prev_index;
138 int last_point = this->next_index - this->num_valid;
148 while (point != last_point) {
150 if (this->durations[point] != INVALID_DURATION) {
151 total += last - this->timestamps[point];
154 last = this->timestamps[point];
155 if (total >= TIMESTAMP_PRECISION)
break;
157 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
160 if (total == 0 || count == 0)
return 0;
161 return (
double)count * TIMESTAMP_PRECISION / total;
198 using namespace std::chrono;
199 return (
TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
270 static const NWidgetPart _framerate_window_widgets[] = {
279 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_GAMELOOP),
SetDataTip(STR_FRAMERATE_RATE_GAMELOOP, STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP),
280 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_DRAWING),
SetDataTip(STR_FRAMERATE_RATE_BLITTER, STR_FRAMERATE_RATE_BLITTER_TOOLTIP),
281 NWidget(
WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_FACTOR),
SetDataTip(STR_FRAMERATE_SPEED_FACTOR, STR_FRAMERATE_SPEED_FACTOR_TOOLTIP),
304 inline void SetRate(
double value,
double target)
306 const double threshold_good = target * 0.95;
307 const double threshold_bad = target * 2 / 3;
308 value =
min(9999.99, value);
309 this->value = (uint32)(value * 100);
310 this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
313 inline void SetTime(
double value,
double target)
315 const double threshold_good = target / 3;
316 const double threshold_bad = target;
317 value =
min(9999.99, value);
318 this->value = (uint32)(value * 100);
319 this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
322 inline void InsertDParams(uint n)
const 335 static const int VSPACING = 3;
339 this->InitNested(number);
340 this->small = this->IsShaded();
342 this->next_update.SetInterval(100);
347 bool elapsed = this->next_update.
Elapsed(delta_ms);
350 if (this->small != this->IsShaded()) {
351 this->small = this->IsShaded();
352 this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->
SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
359 this->next_update.SetInterval(100);
368 if (this->small)
return;
381 case WID_FRW_CAPTION:
383 if (!this->small)
break;
385 this->rate_gameloop.InsertDParams(1);
386 this->speed_gameloop.InsertDParams(3);
389 case WID_FRW_RATE_GAMELOOP:
391 this->rate_gameloop.InsertDParams(1);
393 case WID_FRW_RATE_DRAWING:
395 this->rate_drawing.InsertDParams(1);
397 case WID_FRW_RATE_FACTOR:
398 this->speed_gameloop.InsertDParams(0);
400 case WID_FRW_INFO_DATA_POINTS:
409 case WID_FRW_RATE_GAMELOOP:
415 case WID_FRW_RATE_DRAWING:
421 case WID_FRW_RATE_FACTOR:
427 case WID_FRW_TIMES_NAMES: {
428 int linecount =
PFE_MAX - PFE_FIRST;
431 for (
int line = 0; line < linecount; line++) {
433 size->width =
max(size->width, line_size.width);
438 case WID_FRW_TIMES_CURRENT:
439 case WID_FRW_TIMES_AVERAGE: {
440 int linecount =
PFE_MAX - PFE_FIRST;
445 size->width =
max(size->width, item_size.width);
460 values[e].InsertDParams(0);
469 case WID_FRW_TIMES_NAMES: {
471 int linecount =
PFE_MAX - PFE_FIRST;
473 for (
int i = 0; i < linecount; i++) {
474 DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + i, TC_FROMSTRING,
SA_LEFT);
479 case WID_FRW_TIMES_CURRENT:
481 DrawElementTimesColumn(r, STR_FRAMERATE_CURRENT, this->times_shortterm);
483 case WID_FRW_TIMES_AVERAGE:
485 DrawElementTimesColumn(r, STR_FRAMERATE_AVERAGE, this->times_longterm);
493 case WID_FRW_TIMES_NAMES:
494 case WID_FRW_TIMES_CURRENT:
495 case WID_FRW_TIMES_AVERAGE: {
509 WDP_AUTO,
"framerate_display", 60, 40,
512 _framerate_window_widgets,
lengthof(_framerate_window_widgets)
517 static const NWidgetPart _frametime_graph_window_widgets[] = {
541 this->horizontal_scale = 4;
543 this->next_scale_update.SetInterval(1);
545 this->InitNested(number);
551 case WID_FGW_CAPTION:
552 SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
559 if (widget == WID_FGW_GRAPH) {
566 graph_size.height = max<uint>(100, 10 * (size_ms_label.height + 1));
568 graph_size.width = 2 * graph_size.height;
571 size->width += size_ms_label.width + 2;
572 size->height += size_s_label.height + 2;
581 static const ScaleDef hscales[] = {
588 for (
const ScaleDef *sc = hscales; sc < hscales +
lengthof(hscales); sc++) {
589 if (range < sc->range) this->horizontal_scale = sc->scale;
599 TIMESTAMP_PRECISION * 5,
601 TIMESTAMP_PRECISION / 2,
602 TIMESTAMP_PRECISION / 5,
603 TIMESTAMP_PRECISION / 10,
604 TIMESTAMP_PRECISION / 50,
605 TIMESTAMP_PRECISION / 200,
608 if (range < *sc) this->vertical_scale = (int)*sc;
626 this->horizontal_scale = 4;
628 for (
int i = 1; i < num_valid; i++) {
633 if (value == PerformanceData::INVALID_DURATION) {
635 lastts = timestamps[point];
638 if (value > peak_value) peak_value = value;
642 time_sum += lastts - timestamps[point];
643 lastts = timestamps[point];
649 if (count >= 60 && time_sum >= (this->horizontal_scale + 2) *
TIMESTAMP_PRECISION / 2)
break;
652 this->SelectVerticalScale(peak_value);
659 if (this->next_scale_update.
Elapsed(delta_ms)) {
660 this->next_scale_update.SetInterval(500);
667 static inline T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
669 T dst_diff = dst_max - dst_min;
670 T src_diff = src_max - src_min;
671 return (value - src_min) * dst_diff / src_diff + dst_min;
676 if (widget == WID_FGW_GRAPH) {
681 const int x_zero = r.right - (int)this->graph_size.width;
682 const int x_max = r.right;
683 const int y_zero = r.top + (
int)this->graph_size.height;
684 const int y_max = r.top;
693 const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
695 const uint horz_divisions = this->horizontal_scale / horz_div_scl;
697 const uint vert_divisions = 10;
700 for (uint division = 0; division < vert_divisions; division++) {
701 int y = Scinterlate(y_zero, y_max, 0, (
int)vert_divisions, (
int)division);
702 GfxDrawLine(x_zero, y, x_max, y, c_grid);
703 if (division % 2 == 0) {
714 for (uint division = horz_divisions; division > 0; division--) {
715 int x = Scinterlate(x_zero, x_max, 0, (
int)horz_divisions, (
int)horz_divisions - (
int)division);
716 GfxDrawLine(x, y_max, x, y_zero, c_grid);
717 if (division % 2 == 0) {
718 SetDParam(0, division * horz_div_scl / 2);
726 (int)Scinterlate<int64>(y_zero, y_max, 0, this->vertical_scale, durations[point])
732 Point peak_point = { 0, 0 };
735 int points_drawn = 0;
739 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
742 if (value == PerformanceData::INVALID_DURATION) {
744 lastts = timestamps[point];
749 time_sum += lastts - timestamps[point];
750 lastts = timestamps[point];
752 if (time_sum > draw_horz_scale)
break;
756 (int)Scinterlate<int64>(x_zero, x_max, 0, (int64)draw_horz_scale, (int64)draw_horz_scale - (int64)time_sum),
757 (
int)Scinterlate<int64>(y_zero, y_max, 0, (int64)draw_vert_scale, (int64)value)
759 assert(newpoint.x <= lastpoint.x);
760 GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
761 lastpoint = newpoint;
766 if (value > peak_value) {
768 peak_point = newpoint;
773 if (points_drawn > 0 && peak_value >
TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
775 GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
778 if (peak_point.x - x_zero > (
int)this->graph_size.width / 2) {
788 static WindowDesc _frametime_graph_window_desc(
789 WDP_AUTO,
"frametime_graph", 140, 90,
792 _frametime_graph_window_widgets,
lengthof(_frametime_graph_window_widgets)
800 AllocateWindowDescFront<FramerateWindow>(&_framerate_display_desc, 0);
806 if (elem < PFE_FIRST || elem >=
PFE_MAX)
return;
807 AllocateWindowDescFront<FrametimeGraphWindow>(&_frametime_graph_window_desc, elem,
true);
817 IConsolePrintF(TC_SILVER,
"Based on num. data points: %d %d %d", count1, count2, count3);
819 static const char *MEASUREMENT_NAMES[
PFE_MAX] = {
823 " GL road vehicle ticks",
825 " GL aircraft ticks",
826 " GL landscape ticks",
827 " GL link graph delays",
836 bool printed_anything =
false;
840 if (pf.num_valid == 0)
continue;
842 MEASUREMENT_NAMES[*e],
845 printed_anything =
true;
850 if (pf.num_valid == 0)
continue;
852 MEASUREMENT_NAMES[e],
853 pf.GetAverageDurationMilliseconds(count1),
854 pf.GetAverageDurationMilliseconds(count2),
855 pf.GetAverageDurationMilliseconds(count3));
856 printed_anything =
true;
859 if (!printed_anything) {
Functions related to OTTD's strings.
void IConsoleWarning(const char *string)
It is possible to print warnings to the console.
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
Framerate display; Window numbers:
const TimingMeasurement TIMESTAMP_PRECISION
Units a second is divided into in performance measurements
bool Elapsed(uint delta)
Test if a timer has elapsed.
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.
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
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.
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.
#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...
Center both horizontally and vertically.
virtual void DrawWidget(const Rect &r, int widget) const
Draw the contents of a nested widget.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
void ShowFramerateWindow()
Open the general framerate window.
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.
#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.
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.
Speed of painting drawn video buffer.
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.
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).
Find a place automatically.
PerformanceData _pf_data[PFE_MAX]
Storage for all performance element measurements.
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.
virtual void SetStringParameters(int widget) const
Initialize string parameters for a widget.
CachedDecimal rate_gameloop
cached game loop tick rate
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
static void SetDParam(uint n, uint64 v)
Set a string parameter v at index n in the global string parameter array.