36#include "table/strings.h"
40static std::mutex _sound_perf_lock;
41static std::atomic<bool> _sound_perf_pending;
42static std::vector<TimingMeasurement> _sound_perf_measurements;
59 std::array<TimingMeasurement, NUM_FRAMERATE_POINTS>
durations{};
61 std::array<TimingMeasurement, NUM_FRAMERATE_POINTS>
timestamps{};
91 this->durations[this->next_index] = end_time - start_time;
92 this->timestamps[this->next_index] = start_time;
93 this->prev_index = this->next_index;
94 this->next_index += 1;
105 this->timestamps[this->next_index] = this->acc_timestamp;
106 this->durations[this->next_index] = this->acc_duration;
107 this->prev_index = this->next_index;
108 this->next_index += 1;
112 this->acc_duration = 0;
113 this->acc_timestamp = start_time;
122 this->acc_duration += duration;
132 this->timestamps[this->next_index] = start_time;
134 this->prev_index = this->next_index;
135 this->next_index += 1;
137 this->num_valid += 1;
148 count = std::min(count, this->num_valid);
150 int first_point = this->prev_index - count;
155 const int last_point = first_point + count;
156 for (
int i = first_point; i < last_point; i++) {
166 if (count == 0)
return 0;
177 int point = this->prev_index;
178 int last_point = this->next_index - this->num_valid;
192 while (point != last_point) {
195 total += last - this->timestamps[point];
198 last = this->timestamps[point];
204 if (total == 0 || count == 0)
return 0;
260 using namespace std::chrono;
261 return (
TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
283 for (uint e =
PFE_AI0; e <
PFE_MAX; e++) any_active |= _pf_data[e].num_valid > 0;
296 std::lock_guard lk(_sound_perf_lock);
297 if (_sound_perf_measurements.size() >= NUM_FRAMERATE_POINTS * 2)
return;
298 _sound_perf_measurements.push_back(this->start_time);
299 _sound_perf_measurements.push_back(end);
300 _sound_perf_pending.store(
true, std::memory_order_release);
401static std::string_view GetAIName(
int ai_index)
408static constexpr std::initializer_list<NWidgetPart> _framerate_window_widgets = {
443 int num_displayed = 0;
449 inline void SetRate(
double value,
double target)
451 const double threshold_good = target * 0.95;
452 const double threshold_bad = target * 2 / 3;
453 this->value = (uint32_t)(value * 100);
454 this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
457 inline void SetTime(
double value,
double target)
459 const double threshold_good = target / 3;
460 const double threshold_bad = target;
461 this->value = (uint32_t)(value * 100);
462 this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
465 inline uint32_t GetValue()
const {
return this->value; }
466 inline uint32_t GetDecimals()
const {
return 2; }
481 this->num_displayed = this->num_active;
511 if (new_active != this->num_active) {
512 this->num_active = new_active;
515 sb->
SetCapacity(std::min(this->num_displayed, this->num_active));
522 case WID_FRW_CAPTION:
526 return GetString(STR_FRAMERATE_CAPTION_SMALL, this->rate_gameloop.strid, this->rate_gameloop.GetValue(), this->rate_gameloop.GetDecimals(), this->speed_gameloop.GetValue(), this->speed_gameloop.GetDecimals());
528 case WID_FRW_RATE_GAMELOOP:
529 return GetString(STR_FRAMERATE_RATE_GAMELOOP, this->rate_gameloop.strid, this->rate_gameloop.GetValue(), this->rate_gameloop.GetDecimals());
531 case WID_FRW_RATE_DRAWING:
532 return GetString(STR_FRAMERATE_RATE_BLITTER, this->rate_drawing.strid, this->rate_drawing.GetValue(), this->rate_drawing.GetDecimals());
534 case WID_FRW_RATE_FACTOR:
535 return GetString(STR_FRAMERATE_SPEED_FACTOR, this->speed_gameloop.GetValue(), this->speed_gameloop.GetDecimals());
537 case WID_FRW_INFO_DATA_POINTS:
548 case WID_FRW_RATE_GAMELOOP:
551 case WID_FRW_RATE_DRAWING:
554 case WID_FRW_RATE_FACTOR:
558 case WID_FRW_TIMES_NAMES: {
564 if (
_pf_data[e].num_valid == 0)
continue;
571 size.width = std::max(size.width, line_size.width);
576 case WID_FRW_TIMES_CURRENT:
577 case WID_FRW_TIMES_AVERAGE:
578 case WID_FRW_ALLOCSIZE: {
581 size.width = std::max(size.width, item_size.width);
600 int drawable = this->num_displayed;
605 if (
_pf_data[e].num_valid == 0)
continue;
612 if (drawable == 0)
break;
617 void DrawElementAllocationsColumn(
const Rect &r)
const
621 int drawable = this->num_displayed;
626 if (
_pf_data[e].num_valid == 0)
continue;
634 if (drawable == 0)
break;
639 if (drawable == 0)
break;
644 if (drawable == 0)
break;
652 case WID_FRW_TIMES_NAMES: {
656 int drawable = this->num_displayed;
659 if (
_pf_data[e].num_valid == 0)
continue;
664 DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING,
SA_LEFT);
670 if (drawable == 0)
break;
675 case WID_FRW_TIMES_CURRENT:
679 case WID_FRW_TIMES_AVERAGE:
683 case WID_FRW_ALLOCSIZE:
684 DrawElementAllocationsColumn(r);
692 case WID_FRW_TIMES_NAMES:
693 case WID_FRW_TIMES_CURRENT:
694 case WID_FRW_TIMES_AVERAGE: {
698 if (line != INT32_MAX) {
702 if (
_pf_data[e].num_valid > 0) line--;
727 _framerate_window_widgets
732static constexpr std::initializer_list<NWidgetPart> _frametime_graph_window_widgets = {
761 case WID_FGW_CAPTION:
763 return GetString(STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
774 if (widget == WID_FGW_GRAPH) {
800 static const std::initializer_list<ScaleDef> hscales = {
807 for (
const auto &sc : hscales) {
815 static const std::initializer_list<TimingMeasurement> vscales = {
826 for (
const auto &sc : vscales) {
834 const auto &durations =
_pf_data[this->element].durations;
835 const auto ×tamps =
_pf_data[this->element].timestamps;
836 int num_valid =
_pf_data[this->element].num_valid;
837 int point =
_pf_data[this->element].prev_index;
845 this->horizontal_scale = 4;
847 for (
int i = 1; i < num_valid; i++) {
854 lastts = timestamps[point];
857 if (value > peak_value) peak_value = value;
861 time_sum += lastts - timestamps[point];
862 lastts = timestamps[point];
865 if (count == 60) this->SelectHorizontalScale(time_sum);
868 if (count >= 60 && time_sum >= (this->horizontal_scale + 2) *
TIMESTAMP_PRECISION / 2)
break;
871 this->SelectVerticalScale(peak_value);
893 template <
typename T>
896 T dst_diff = dst_max - dst_min;
897 T src_diff = src_max - src_min;
898 return (value - src_min) * dst_diff / src_diff + dst_min;
903 if (widget == WID_FGW_GRAPH) {
904 const auto &durations =
_pf_data[this->element].durations;
905 const auto ×tamps =
_pf_data[this->element].timestamps;
906 int point =
_pf_data[this->element].prev_index;
908 const int x_zero = r.right - (int)this->graph_size.width;
909 const int x_max = r.right;
910 const int y_zero = r.top + (int)this->graph_size.height;
911 const int y_max = r.top;
920 const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
922 const uint horz_divisions = this->horizontal_scale / horz_div_scl;
924 const uint vert_divisions = 10;
927 for (uint division = 0; division < vert_divisions; division++) {
928 int y =
Scinterlate(y_zero, y_max, 0, (
int)vert_divisions, (
int)division);
929 GfxDrawLine(x_zero, y, x_max, y, c_grid);
930 if (division % 2 == 0) {
943 for (uint division = horz_divisions; division > 0; division--) {
944 int x =
Scinterlate(x_zero, x_max, 0, (
int)horz_divisions, (
int)horz_divisions - (
int)division);
945 GfxDrawLine(x, y_max, x, y_zero, c_grid);
946 if (division % 2 == 0) {
948 GetString(STR_FRAMERATE_GRAPH_SECONDS, division * horz_div_scl / 2),
962 Point peak_point = { 0, 0 };
965 int points_drawn = 0;
974 lastts = timestamps[point];
979 time_sum += lastts - timestamps[point];
980 lastts = timestamps[point];
982 if (time_sum > draw_horz_scale)
break;
986 (int)
Scinterlate<int64_t>(x_zero, x_max, 0, (int64_t)draw_horz_scale, (int64_t)draw_horz_scale - (int64_t)time_sum),
989 if (newpoint.
x > lastpoint.
x)
continue;
990 GfxDrawLine(lastpoint.
x, lastpoint.
y, newpoint.
x, newpoint.
y, c_lines);
991 lastpoint = newpoint;
996 if (value > peak_value) {
998 peak_point = newpoint;
1003 if (points_drawn > 0 && peak_value >
TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
1005 GfxFillRect(peak_point.
x - 1, peak_point.
y - 1, peak_point.
x + 1, peak_point.
y + 1, c_peak);
1008 if (peak_point.
x - x_zero > (
int)this->graph_size.width / 2) {
1023 _frametime_graph_window_widgets
1040 if (elem < PFE_FIRST || elem >=
PFE_MAX)
return;
1051 IConsolePrint(TC_SILVER,
"Based on num. data points: {} {} {}", count1, count2, count3);
1053 static const std::array<std::string_view, PFE_MAX> MEASUREMENT_NAMES = {
1055 " GL station ticks",
1057 " GL road vehicle ticks",
1059 " GL aircraft ticks",
1060 " GL landscape ticks",
1061 " GL link graph delays",
1063 " Viewport drawing",
1066 "AI/GS scripts total",
1069 std::string ai_name_buf;
1071 bool printed_anything =
false;
1075 if (pf.num_valid == 0)
continue;
1076 IConsolePrint(TC_GREEN,
"{} rate: {:.2f}fps (expected: {:.2f}fps)",
1077 MEASUREMENT_NAMES[e],
1080 printed_anything =
true;
1085 if (pf.num_valid == 0)
continue;
1086 std::string_view name;
1088 name = MEASUREMENT_NAMES[e];
1090 ai_name_buf = fmt::format(
"AI {} {}", e -
PFE_AI0 + 1, GetAIName(e -
PFE_AI0));
1093 IConsolePrint(TC_LIGHT_BLUE,
"{} times: {:.2f}ms {:.2f}ms {:.2f}ms",
1095 pf.GetAverageDurationMilliseconds(count1),
1096 pf.GetAverageDurationMilliseconds(count2),
1097 pf.GetAverageDurationMilliseconds(count3));
1098 printed_anything =
true;
1101 if (!printed_anything) {
1115 if (_sound_perf_pending.load(std::memory_order_acquire)) {
1116 std::lock_guard lk(_sound_perf_lock);
1117 for (
size_t i = 0; i < _sound_perf_measurements.size(); i += 2) {
1120 _sound_perf_measurements.clear();
1121 _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.
An interval timer will fire every interval, and will continue to fire until it is deleted.
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.
#define T
Climate temperate.
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.
static WindowDesc _framerate_display_desc(WindowPosition::Automatic, "framerate_display", 0, 0, WC_FRAMERATE_DISPLAY, WC_NONE, {}, _framerate_window_widgets)
Window definition for the frame rate window.
static WindowDesc _frametime_graph_window_desc(WindowPosition::Automatic, "frametime_graph", 140, 90, WC_FRAMETIME_GRAPH, WC_NONE, {}, _frametime_graph_window_widgets)
Window definition for the frame rate graph window.
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, const std::variant< PixelColour, PaletteID > &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.
@ Small
Index of the small font in the font tables.
@ Normal
Index of the normal font in the font tables.
@ 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.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
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).
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
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.
size_t GetSoundPoolAllocatedMemory()
Get size of memory allocated to sound effects.
Functions related to NewGRF provided sounds.
static constexpr PixelColour PC_DARK_RED
Dark red palette colour.
static constexpr PixelColour PC_DARK_GREY
Dark grey palette colour.
static constexpr PixelColour PC_BLACK
Black palette colour.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get 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.
static bool IsValidAiID(auto 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.
void DrawElementTimesColumn(const Rect &r, StringID heading_str, std::span< const CachedDecimal > values) const
Render a column of formatted average durations.
CachedDecimal rate_drawing
cached drawing frame rate
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
std::array< CachedDecimal, PFE_MAX > times_longterm
cached long term average times
std::array< CachedDecimal, PFE_MAX > times_shortterm
cached short term average times
CachedDecimal speed_gameloop
cached game loop speed factor
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
const IntervalTimer< TimerWindow > update_interval
Update the window on a regular interval.
CachedDecimal rate_gameloop
cached game loop tick rate
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
static constexpr int MIN_ELEMENTS
smallest number of elements to display
int horizontal_scale
number of half-second units horizontally
void OnRealtimeTick(uint delta_ms) override
Called periodically.
void UpdateScale()
Recalculate the graph scaling factors based on current recorded data.
const IntervalTimer< TimerWindow > update_interval
Update the scaling on a regular interval.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Dimension graph_size
size of the main graph area (excluding axis labels)
PerformanceElement element
what element this window renders graph for
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
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.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
int vertical_scale
number of TIMESTAMP_PRECISION units vertically
Colour for pixel/line drawing.
static Company * Get(auto index)
Specification of a rectangle with absolute coordinates of all edges.
High level window description.
Number to differentiate different windows of the same class.
Data structure for an opened window.
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
ResizeInfo resize
Resize information.
bool IsShaded() const
Is window shaded currently?
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
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.
Twindow * AllocateWindowDescFront(WindowDesc &desc, WindowNumber window_number, Targs... extra_arguments)
Open a new window.
@ Automatic
Find a place automatically.
@ WC_FRAMETIME_GRAPH
Frame time graph; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
@ WC_FRAMERATE_DISPLAY
Framerate display; Window numbers:
Functions related to zooming.