OpenTTD Source: src/framerate_gui.cpp Source File

OpenTTD Source 15.0-beta1

framerate_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "framerate_type.h"
11#include <chrono>
12#include "gfx_func.h"
13#include "newgrf_sound.h"
14#include "window_gui.h"
15#include "window_func.h"
16#include "table/sprites.h"
17#include "string_func.h"
18#include "strings_func.h"
19#include "console_func.h"
20#include "console_type.h"
21#include "company_base.h"
22#include "ai/ai_info.hpp"
23#include "ai/ai_instance.hpp"
24#include "game/game.hpp"
25#include "game/game_instance.hpp"
26#include "timer/timer.h"
27#include "timer/timer_window.h"
28
29#include "widgets/framerate_widget.h"
30
31#include <atomic>
32#include <mutex>
33
34#include "safeguards.h"
35
36static std::mutex _sound_perf_lock;
37static std::atomic<bool> _sound_perf_pending;
38static std::vector<TimingMeasurement> _sound_perf_measurements;
39
43 namespace {
44
46 const int NUM_FRAMERATE_POINTS = 512;
48 const TimingMeasurement TIMESTAMP_PRECISION = 1000000;
49
50 struct PerformanceData {
52 static const TimingMeasurement INVALID_DURATION = UINT64_MAX;
53
55 TimingMeasurement durations[NUM_FRAMERATE_POINTS];
57 TimingMeasurement timestamps[NUM_FRAMERATE_POINTS];
59 double expected_rate;
61 int next_index;
63 int prev_index;
65 int num_valid;
66
68 TimingMeasurement acc_duration;
70 TimingMeasurement acc_timestamp;
71
78 explicit PerformanceData(double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
79
81 void Add(TimingMeasurement start_time, TimingMeasurement end_time)
82 {
83 this->durations[this->next_index] = end_time - start_time;
84 this->timestamps[this->next_index] = start_time;
85 this->prev_index = this->next_index;
86 this->next_index += 1;
87 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
88 this->num_valid = std::min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
89 }
90
92 void BeginAccumulate(TimingMeasurement start_time)
93 {
94 this->timestamps[this->next_index] = this->acc_timestamp;
95 this->durations[this->next_index] = this->acc_duration;
96 this->prev_index = this->next_index;
97 this->next_index += 1;
98 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
99 this->num_valid = std::min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
100
101 this->acc_duration = 0;
102 this->acc_timestamp = start_time;
103 }
104
106 void AddAccumulate(TimingMeasurement duration)
107 {
108 this->acc_duration += duration;
109 }
110
112 void AddPause(TimingMeasurement start_time)
113 {
114 if (this->durations[this->prev_index] != INVALID_DURATION) {
115 this->timestamps[this->next_index] = start_time;
116 this->durations[this->next_index] = INVALID_DURATION;
117 this->prev_index = this->next_index;
118 this->next_index += 1;
119 if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
120 this->num_valid += 1;
121 }
122 }
123
125 double GetAverageDurationMilliseconds(int count)
126 {
127 count = std::min(count, this->num_valid);
128
129 int first_point = this->prev_index - count;
130 if (first_point < 0) first_point += NUM_FRAMERATE_POINTS;
131
132 /* Sum durations, skipping invalid points */
133 double sumtime = 0;
134 for (int i = first_point; i < first_point + count; i++) {
135 auto d = this->durations[i % NUM_FRAMERATE_POINTS];
136 if (d != INVALID_DURATION) {
137 sumtime += d;
138 } else {
139 /* Don't count the invalid durations */
140 count--;
141 }
142 }
143
144 if (count == 0) return 0; // avoid div by zero
145 return sumtime * 1000 / count / TIMESTAMP_PRECISION;
146 }
147
149 double GetRate()
150 {
151 /* Start at last recorded point, end at latest when reaching the earliest recorded point */
152 int point = this->prev_index;
153 int last_point = this->next_index - this->num_valid;
154 if (last_point < 0) last_point += NUM_FRAMERATE_POINTS;
155
156 /* Number of data points collected */
157 int count = 0;
158 /* Time of previous data point */
159 TimingMeasurement last = this->timestamps[point];
160 /* Total duration covered by collected points */
161 TimingMeasurement total = 0;
162
163 /* We have nothing to compare the first point against */
164 point--;
165 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
166
167 while (point != last_point) {
168 /* Only record valid data points, but pretend the gaps in measurements aren't there */
169 if (this->durations[point] != INVALID_DURATION) {
170 total += last - this->timestamps[point];
171 count++;
172 }
173 last = this->timestamps[point];
174 if (total >= TIMESTAMP_PRECISION) break; // end after 1 second has been collected
175 point--;
176 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
177 }
178
179 if (total == 0 || count == 0) return 0;
180 return (double)count * TIMESTAMP_PRECISION / total;
181 }
182 };
183
185 static const double GL_RATE = 1000.0 / MILLISECONDS_PER_TICK;
186
192 PerformanceData _pf_data[PFE_MAX] = {
193 PerformanceData(GL_RATE), // PFE_GAMELOOP
194 PerformanceData(1), // PFE_ACC_GL_ECONOMY
195 PerformanceData(1), // PFE_ACC_GL_TRAINS
196 PerformanceData(1), // PFE_ACC_GL_ROADVEHS
197 PerformanceData(1), // PFE_ACC_GL_SHIPS
198 PerformanceData(1), // PFE_ACC_GL_AIRCRAFT
199 PerformanceData(1), // PFE_GL_LANDSCAPE
200 PerformanceData(1), // PFE_GL_LINKGRAPH
201 PerformanceData(1000.0 / 30), // PFE_DRAWING
202 PerformanceData(1), // PFE_ACC_DRAWWORLD
203 PerformanceData(60.0), // PFE_VIDEO
204 PerformanceData(1000.0 * 8192 / 44100), // PFE_SOUND
205 PerformanceData(1), // PFE_ALLSCRIPTS
206 PerformanceData(1), // PFE_GAMESCRIPT
207 PerformanceData(1), // PFE_AI0 ...
208 PerformanceData(1),
209 PerformanceData(1),
210 PerformanceData(1),
211 PerformanceData(1),
212 PerformanceData(1),
213 PerformanceData(1),
214 PerformanceData(1),
215 PerformanceData(1),
216 PerformanceData(1),
217 PerformanceData(1),
218 PerformanceData(1),
219 PerformanceData(1),
220 PerformanceData(1),
221 PerformanceData(1), // PFE_AI14
222 };
223
224}
225
226
232 static TimingMeasurement GetPerformanceTimer()
233{
234 using namespace std::chrono;
235 return (TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
236}
237
238
243PerformanceMeasurer::PerformanceMeasurer(PerformanceElement elem)
244{
245 assert(elem < PFE_MAX);
246
247 this->elem = elem;
248 this->start_time = GetPerformanceTimer();
249}
250
252PerformanceMeasurer::~PerformanceMeasurer()
253{
254 if (this->elem == PFE_ALLSCRIPTS) {
255 /* Hack to not record scripts total when no scripts are active */
256 bool any_active = _pf_data[PFE_GAMESCRIPT].num_valid > 0;
257 for (uint e = PFE_AI0; e < PFE_MAX; e++) any_active |= _pf_data[e].num_valid > 0;
258 if (!any_active) {
259 PerformanceMeasurer::SetInactive(PFE_ALLSCRIPTS);
260 return;
261 }
262 }
263 if (this->elem == PFE_SOUND) {
264 /* PFE_SOUND measurements are made from the mixer thread.
265 * _pf_data cannot be concurrently accessed from the mixer thread
266 * and the main thread, so store the measurement results in a
267 * mutex-protected queue which is drained by the main thread.
268 * See: ProcessPendingPerformanceMeasurements() */
269 TimingMeasurement end = GetPerformanceTimer();
270 std::lock_guard lk(_sound_perf_lock);
271 if (_sound_perf_measurements.size() >= NUM_FRAMERATE_POINTS * 2) return;
272 _sound_perf_measurements.push_back(this->start_time);
273 _sound_perf_measurements.push_back(end);
274 _sound_perf_pending.store(true, std::memory_order_release);
275 return;
276 }
277 _pf_data[this->elem].Add(this->start_time, GetPerformanceTimer());
278}
279
281 void PerformanceMeasurer::SetExpectedRate(double rate)
282{
283 _pf_data[this->elem].expected_rate = rate;
284}
285
287/* static */ void PerformanceMeasurer::SetInactive(PerformanceElement elem)
288{
289 _pf_data[elem].num_valid = 0;
290 _pf_data[elem].next_index = 0;
291 _pf_data[elem].prev_index = 0;
292}
293
298/* static */ void PerformanceMeasurer::Paused(PerformanceElement elem)
299{
300 PerformanceMeasurer::SetInactive(elem);
301 _pf_data[elem].AddPause(GetPerformanceTimer());
302}
303
304
309PerformanceAccumulator::PerformanceAccumulator(PerformanceElement elem)
310{
311 assert(elem < PFE_MAX);
312
313 this->elem = elem;
314 this->start_time = GetPerformanceTimer();
315}
316
318PerformanceAccumulator::~PerformanceAccumulator()
319{
320 _pf_data[this->elem].AddAccumulate(GetPerformanceTimer() - this->start_time);
321}
322
328 void PerformanceAccumulator::Reset(PerformanceElement elem)
329{
330 _pf_data[elem].BeginAccumulate(GetPerformanceTimer());
331}
332
333
334void ShowFrametimeGraphWindow(PerformanceElement elem);
335
336
337static const PerformanceElement DISPLAY_ORDER_PFE[PFE_MAX] = {
338 PFE_GAMELOOP,
339 PFE_GL_ECONOMY,
340 PFE_GL_TRAINS,
341 PFE_GL_ROADVEHS,
342 PFE_GL_SHIPS,
343 PFE_GL_AIRCRAFT,
344 PFE_GL_LANDSCAPE,
345 PFE_ALLSCRIPTS,
346 PFE_GAMESCRIPT,
347 PFE_AI0,
348 PFE_AI1,
349 PFE_AI2,
350 PFE_AI3,
351 PFE_AI4,
352 PFE_AI5,
353 PFE_AI6,
354 PFE_AI7,
355 PFE_AI8,
356 PFE_AI9,
357 PFE_AI10,
358 PFE_AI11,
359 PFE_AI12,
360 PFE_AI13,
361 PFE_AI14,
362 PFE_GL_LINKGRAPH,
363 PFE_DRAWING,
364 PFE_DRAWWORLD,
365 PFE_VIDEO,
366 PFE_SOUND,
367};
368
369static const char * GetAIName(int ai_index)
370{
371 if (!Company::IsValidAiID(ai_index)) return "";
372 return Company::Get(ai_index)->ai_info->GetName().c_str();
373}
374
376static constexpr NWidgetPart _framerate_window_widgets[] = {
377 NWidget(NWID_HORIZONTAL),
378 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
379 NWidget(WWT_CAPTION, COLOUR_GREY, WID_FRW_CAPTION), SetDataTip(STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
380 NWidget(WWT_SHADEBOX, COLOUR_GREY),
381 NWidget(WWT_STICKYBOX, COLOUR_GREY),
382 EndContainer(),
383 NWidget(WWT_PANEL, COLOUR_GREY),
384 NWidget(NWID_VERTICAL), SetPadding(WidgetDimensions::unscaled.frametext), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
385 NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_GAMELOOP), SetDataTip(STR_FRAMERATE_RATE_GAMELOOP, STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
386 NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_DRAWING), SetDataTip(STR_FRAMERATE_RATE_BLITTER, STR_FRAMERATE_RATE_BLITTER_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
387 NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_FACTOR), SetDataTip(STR_FRAMERATE_SPEED_FACTOR, STR_FRAMERATE_SPEED_FACTOR_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
388 EndContainer(),
389 EndContainer(),
390 NWidget(NWID_HORIZONTAL),
391 NWidget(WWT_PANEL, COLOUR_GREY),
392 NWidget(NWID_VERTICAL), SetPadding(WidgetDimensions::unscaled.frametext), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0),
393 NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
394 NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_NAMES), SetScrollbar(WID_FRW_SCROLLBAR),
395 NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_CURRENT), SetScrollbar(WID_FRW_SCROLLBAR),
396 NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_AVERAGE), SetScrollbar(WID_FRW_SCROLLBAR),
397 NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_ALLOCSIZE), SetScrollbar(WID_FRW_SCROLLBAR),
398 EndContainer(),
399 NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_INFO_DATA_POINTS), SetDataTip(STR_FRAMERATE_DATA_POINTS, 0x0), SetFill(1, 0), SetResize(1, 0),
400 EndContainer(),
401 EndContainer(),
402 NWidget(NWID_VERTICAL),
403 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_FRW_SCROLLBAR),
404 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
405 EndContainer(),
406 EndContainer(),
407};
408
409 struct FramerateWindow : Window {
410 bool small;
411 int num_active;
412 int num_displayed;
413
414 struct CachedDecimal {
415 StringID strid;
416 uint32_t value;
417
418 inline void SetRate(double value, double target)
419 {
420 const double threshold_good = target * 0.95;
421 const double threshold_bad = target * 2 / 3;
422 this->value = (uint32_t)(value * 100);
423 this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
424 }
425
426 inline void SetTime(double value, double target)
427 {
428 const double threshold_good = target / 3;
429 const double threshold_bad = target;
430 this->value = (uint32_t)(value * 100);
431 this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
432 }
433
434 inline void InsertDParams(uint n) const
435 {
436 SetDParam(n, this->value);
437 SetDParam(n + 1, 2);
438 }
439 };
440
441 CachedDecimal rate_gameloop;
442 CachedDecimal rate_drawing;
443 CachedDecimal speed_gameloop;
444 CachedDecimal times_shortterm[PFE_MAX];
445 CachedDecimal times_longterm[PFE_MAX];
446
447 static constexpr int MIN_ELEMENTS = 5;
448
449 FramerateWindow(WindowDesc &desc, WindowNumber number) : Window(desc)
450 {
451 this->InitNested(number);
452 this->small = this->IsShaded();
453 this->UpdateData();
454 this->num_displayed = this->num_active;
455
456 /* Window is always initialised to MIN_ELEMENTS height, resize to contain num_displayed */
457 ResizeWindow(this, 0, (std::max(MIN_ELEMENTS, this->num_displayed) - MIN_ELEMENTS) * GetCharacterHeight(FS_NORMAL));
458 }
459
460 void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
461 {
462 /* Check if the shaded state has changed, switch caption text if it has */
463 if (this->small != this->IsShaded()) {
464 this->small = this->IsShaded();
465 this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
466 this->UpdateData();
467 this->SetDirty();
468 }
469 }
470
472 IntervalTimer<TimerWindow> update_interval = {std::chrono::milliseconds(100), [this](auto) {
473 this->UpdateData();
474 this->SetDirty();
475 }};
476
477 void UpdateData()
478 {
479 double gl_rate = _pf_data[PFE_GAMELOOP].GetRate();
480 this->rate_gameloop.SetRate(gl_rate, _pf_data[PFE_GAMELOOP].expected_rate);
481 this->speed_gameloop.SetRate(gl_rate / _pf_data[PFE_GAMELOOP].expected_rate, 1.0);
482 if (this->small) return; // in small mode, this is everything needed
483
484 this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _settings_client.gui.refresh_rate);
485
486 int new_active = 0;
487 for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
488 this->times_shortterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(8), MILLISECONDS_PER_TICK);
489 this->times_longterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(NUM_FRAMERATE_POINTS), MILLISECONDS_PER_TICK);
490 if (_pf_data[e].num_valid > 0) {
491 new_active++;
492 }
493 }
494
495 if (new_active != this->num_active) {
496 this->num_active = new_active;
497 Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
498 sb->SetCount(this->num_active);
499 sb->SetCapacity(std::min(this->num_displayed, this->num_active));
500 }
501 }
502
503 void SetStringParameters(WidgetID widget) const override
504 {
505 switch (widget) {
506 case WID_FRW_CAPTION:
507 /* When the window is shaded, the caption shows game loop rate and speed factor */
508 if (!this->small) break;
509 SetDParam(0, this->rate_gameloop.strid);
510 this->rate_gameloop.InsertDParams(1);
511 this->speed_gameloop.InsertDParams(3);
512 break;
513
514 case WID_FRW_RATE_GAMELOOP:
515 SetDParam(0, this->rate_gameloop.strid);
516 this->rate_gameloop.InsertDParams(1);
517 break;
518 case WID_FRW_RATE_DRAWING:
519 SetDParam(0, this->rate_drawing.strid);
520 this->rate_drawing.InsertDParams(1);
521 break;
522 case WID_FRW_RATE_FACTOR:
523 this->speed_gameloop.InsertDParams(0);
524 break;
525 case WID_FRW_INFO_DATA_POINTS:
526 SetDParam(0, NUM_FRAMERATE_POINTS);
527 break;
528 }
529 }
530
531 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
532 {
533 switch (widget) {
534 case WID_FRW_RATE_GAMELOOP:
535 SetDParam(0, STR_FRAMERATE_FPS_GOOD);
536 SetDParamMaxDigits(1, 6);
537 SetDParam(2, 2);
538 size = GetStringBoundingBox(STR_FRAMERATE_RATE_GAMELOOP);
539 break;
540 case WID_FRW_RATE_DRAWING:
541 SetDParam(0, STR_FRAMERATE_FPS_GOOD);
542 SetDParamMaxDigits(1, 6);
543 SetDParam(2, 2);
544 size = GetStringBoundingBox(STR_FRAMERATE_RATE_BLITTER);
545 break;
546 case WID_FRW_RATE_FACTOR:
547 SetDParamMaxDigits(0, 6);
548 SetDParam(1, 2);
549 size = GetStringBoundingBox(STR_FRAMERATE_SPEED_FACTOR);
550 break;
551
552 case WID_FRW_TIMES_NAMES: {
553 size.width = 0;
554 size.height = GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal + MIN_ELEMENTS * GetCharacterHeight(FS_NORMAL);
555 resize.width = 0;
556 resize.height = GetCharacterHeight(FS_NORMAL);
557 for (PerformanceElement e : DISPLAY_ORDER_PFE) {
558 if (_pf_data[e].num_valid == 0) continue;
559 Dimension line_size;
560 if (e < PFE_AI0) {
561 line_size = GetStringBoundingBox(STR_FRAMERATE_GAMELOOP + e);
562 } else {
563 SetDParam(0, e - PFE_AI0 + 1);
564 SetDParamStr(1, GetAIName(e - PFE_AI0));
565 line_size = GetStringBoundingBox(STR_FRAMERATE_AI);
566 }
567 size.width = std::max(size.width, line_size.width);
568 }
569 break;
570 }
571
572 case WID_FRW_TIMES_CURRENT:
573 case WID_FRW_TIMES_AVERAGE:
574 case WID_FRW_ALLOCSIZE: {
575 size = GetStringBoundingBox(STR_FRAMERATE_CURRENT + (widget - WID_FRW_TIMES_CURRENT));
576 SetDParamMaxDigits(0, 6);
577 SetDParam(1, 2);
578 Dimension item_size = GetStringBoundingBox(STR_FRAMERATE_MS_GOOD);
579 size.width = std::max(size.width, item_size.width);
580 size.height += GetCharacterHeight(FS_NORMAL) * MIN_ELEMENTS + WidgetDimensions::scaled.vsep_normal;
581 resize.width = 0;
582 resize.height = GetCharacterHeight(FS_NORMAL);
583 break;
584 }
585 }
586 }
587
589 void DrawElementTimesColumn(const Rect &r, StringID heading_str, const CachedDecimal *values) const
590 {
591 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
592 int32_t skip = sb->GetPosition();
593 int drawable = this->num_displayed;
594 int y = r.top;
595 DrawString(r.left, r.right, y, heading_str, TC_FROMSTRING, SA_CENTER, true);
596 y += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
597 for (PerformanceElement e : DISPLAY_ORDER_PFE) {
598 if (_pf_data[e].num_valid == 0) continue;
599 if (skip > 0) {
600 skip--;
601 } else {
602 values[e].InsertDParams(0);
603 DrawString(r.left, r.right, y, values[e].strid, TC_FROMSTRING, SA_RIGHT);
604 y += GetCharacterHeight(FS_NORMAL);
605 drawable--;
606 if (drawable == 0) break;
607 }
608 }
609 }
610
611 void DrawElementAllocationsColumn(const Rect &r) const
612 {
613 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
614 int32_t skip = sb->GetPosition();
615 int drawable = this->num_displayed;
616 int y = r.top;
617 DrawString(r.left, r.right, y, STR_FRAMERATE_MEMORYUSE, TC_FROMSTRING, SA_CENTER, true);
618 y += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
619 for (PerformanceElement e : DISPLAY_ORDER_PFE) {
620 if (_pf_data[e].num_valid == 0) continue;
621 if (skip > 0) {
622 skip--;
623 } else if (e == PFE_GAMESCRIPT || e >= PFE_AI0) {
624 if (e == PFE_GAMESCRIPT) {
625 SetDParam(0, Game::GetInstance()->GetAllocatedMemory());
626 } else {
627 SetDParam(0, Company::Get(e - PFE_AI0)->ai_instance->GetAllocatedMemory());
628 }
629 DrawString(r.left, r.right, y, STR_FRAMERATE_BYTES_GOOD, TC_FROMSTRING, SA_RIGHT);
630 y += GetCharacterHeight(FS_NORMAL);
631 drawable--;
632 if (drawable == 0) break;
633 } else if (e == PFE_SOUND) {
634 SetDParam(0, GetSoundPoolAllocatedMemory());
635 DrawString(r.left, r.right, y, STR_FRAMERATE_BYTES_GOOD, TC_FROMSTRING, SA_RIGHT);
636 y += GetCharacterHeight(FS_NORMAL);
637 drawable--;
638 if (drawable == 0) break;
639 } else {
640 /* skip non-script */
641 y += GetCharacterHeight(FS_NORMAL);
642 drawable--;
643 if (drawable == 0) break;
644 }
645 }
646 }
647
648 void DrawWidget(const Rect &r, WidgetID widget) const override
649 {
650 switch (widget) {
651 case WID_FRW_TIMES_NAMES: {
652 /* Render a column of titles for performance element names */
653 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
654 int32_t skip = sb->GetPosition();
655 int drawable = this->num_displayed;
656 int y = r.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; // first line contains headings in the value columns
657 for (PerformanceElement e : DISPLAY_ORDER_PFE) {
658 if (_pf_data[e].num_valid == 0) continue;
659 if (skip > 0) {
660 skip--;
661 } else {
662 if (e < PFE_AI0) {
663 DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING, SA_LEFT);
664 } else {
665 SetDParam(0, e - PFE_AI0 + 1);
666 SetDParamStr(1, GetAIName(e - PFE_AI0));
667 DrawString(r.left, r.right, y, STR_FRAMERATE_AI, TC_FROMSTRING, SA_LEFT);
668 }
669 y += GetCharacterHeight(FS_NORMAL);
670 drawable--;
671 if (drawable == 0) break;
672 }
673 }
674 break;
675 }
676 case WID_FRW_TIMES_CURRENT:
677 /* Render short-term average values */
678 DrawElementTimesColumn(r, STR_FRAMERATE_CURRENT, this->times_shortterm);
679 break;
680 case WID_FRW_TIMES_AVERAGE:
681 /* Render averages of all recorded values */
682 DrawElementTimesColumn(r, STR_FRAMERATE_AVERAGE, this->times_longterm);
683 break;
684 case WID_FRW_ALLOCSIZE:
685 DrawElementAllocationsColumn(r);
686 break;
687 }
688 }
689
690 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
691 {
692 switch (widget) {
693 case WID_FRW_TIMES_NAMES:
694 case WID_FRW_TIMES_CURRENT:
695 case WID_FRW_TIMES_AVERAGE: {
696 /* Open time graph windows when clicking detail measurement lines */
697 const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
698 int32_t line = sb->GetScrolledRowFromWidget(pt.y, this, widget, WidgetDimensions::scaled.vsep_normal + GetCharacterHeight(FS_NORMAL));
699 if (line != INT32_MAX) {
700 line++;
701 /* Find the visible line that was clicked */
702 for (PerformanceElement e : DISPLAY_ORDER_PFE) {
703 if (_pf_data[e].num_valid > 0) line--;
704 if (line == 0) {
705 ShowFrametimeGraphWindow(e);
706 break;
707 }
708 }
709 }
710 break;
711 }
712 }
713 }
714
715 void OnResize() override
716 {
717 auto *wid = this->GetWidget<NWidgetResizeBase>(WID_FRW_TIMES_NAMES);
718 this->num_displayed = (wid->current_y - wid->min_y - WidgetDimensions::scaled.vsep_normal) / GetCharacterHeight(FS_NORMAL) - 1; // subtract 1 for headings
719 this->GetScrollbar(WID_FRW_SCROLLBAR)->SetCapacity(this->num_displayed);
720 }
721};
722
723static WindowDesc _framerate_display_desc(
724 WDP_AUTO, "framerate_display", 0, 0,
725 WC_FRAMERATE_DISPLAY, WC_NONE,
726 0,
727 _framerate_window_widgets
728);
729
730
732static constexpr NWidgetPart _frametime_graph_window_widgets[] = {
733 NWidget(NWID_HORIZONTAL),
734 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
735 NWidget(WWT_CAPTION, COLOUR_GREY, WID_FGW_CAPTION), SetDataTip(STR_JUST_STRING2, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), SetTextStyle(TC_WHITE),
736 NWidget(WWT_STICKYBOX, COLOUR_GREY),
737 EndContainer(),
738 NWidget(WWT_PANEL, COLOUR_GREY),
739 NWidget(NWID_VERTICAL), SetPadding(6),
740 NWidget(WWT_EMPTY, COLOUR_GREY, WID_FGW_GRAPH),
741 EndContainer(),
742 EndContainer(),
743};
744
745 struct FrametimeGraphWindow : Window {
746 int vertical_scale;
747 int horizontal_scale;
748
749 PerformanceElement element;
750 Dimension graph_size;
751
752 FrametimeGraphWindow(WindowDesc &desc, WindowNumber number) : Window(desc)
753 {
754 this->element = (PerformanceElement)number;
755 this->horizontal_scale = 4;
756 this->vertical_scale = TIMESTAMP_PRECISION / 10;
757
758 this->InitNested(number);
759 this->UpdateScale();
760 }
761
762 void SetStringParameters(WidgetID widget) const override
763 {
764 switch (widget) {
765 case WID_FGW_CAPTION:
766 if (this->element < PFE_AI0) {
767 SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
768 } else {
769 SetDParam(0, STR_FRAMETIME_CAPTION_AI);
770 SetDParam(1, this->element - PFE_AI0 + 1);
771 SetDParamStr(2, GetAIName(this->element - PFE_AI0));
772 }
773 break;
774 }
775 }
776
777 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
778 {
779 if (widget == WID_FGW_GRAPH) {
780 SetDParam(0, 100);
781 Dimension size_ms_label = GetStringBoundingBox(STR_FRAMERATE_GRAPH_MILLISECONDS);
782 SetDParam(0, 100);
783 Dimension size_s_label = GetStringBoundingBox(STR_FRAMERATE_GRAPH_SECONDS);
784
785 /* Size graph in height to fit at least 10 vertical labels with space between, or at least 100 pixels */
786 graph_size.height = std::max(100u, 10 * (size_ms_label.height + 1));
787 /* Always 2:1 graph area */
788 graph_size.width = 2 * graph_size.height;
789 size = graph_size;
790
791 size.width += size_ms_label.width + 2;
792 size.height += size_s_label.height + 2;
793 }
794 }
795
796 void SelectHorizontalScale(TimingMeasurement range)
797 {
798 /* 60 Hz graphical drawing results in a value of approximately TIMESTAMP_PRECISION,
799 * this lands exactly on the scale = 2 vs scale = 4 boundary.
800 * To avoid excessive switching of the horizontal scale, bias these performance
801 * categories away from this scale boundary. */
802 if (this->element == PFE_DRAWING || this->element == PFE_DRAWWORLD) range += (range / 2);
803
804 /* Determine horizontal scale based on period covered by 60 points
805 * (slightly less than 2 seconds at full game speed) */
806 struct ScaleDef { TimingMeasurement range; int scale; };
807 static const std::initializer_list<ScaleDef> hscales = {
808 { TIMESTAMP_PRECISION * 120, 60 },
809 { TIMESTAMP_PRECISION * 10, 20 },
810 { TIMESTAMP_PRECISION * 5, 10 },
811 { TIMESTAMP_PRECISION * 3, 4 },
812 { TIMESTAMP_PRECISION * 1, 2 },
813 };
814 for (const auto &sc : hscales) {
815 if (range < sc.range) this->horizontal_scale = sc.scale;
816 }
817 }
818
819 void SelectVerticalScale(TimingMeasurement range)
820 {
821 /* Determine vertical scale based on peak value (within the horizontal scale + a bit) */
822 static const std::initializer_list<TimingMeasurement> vscales = {
823 TIMESTAMP_PRECISION * 100,
824 TIMESTAMP_PRECISION * 10,
825 TIMESTAMP_PRECISION * 5,
826 TIMESTAMP_PRECISION,
827 TIMESTAMP_PRECISION / 2,
828 TIMESTAMP_PRECISION / 5,
829 TIMESTAMP_PRECISION / 10,
830 TIMESTAMP_PRECISION / 50,
831 TIMESTAMP_PRECISION / 200,
832 };
833 for (const auto &sc : vscales) {
834 if (range < sc) this->vertical_scale = (int)sc;
835 }
836 }
837
839 void UpdateScale()
840 {
841 const TimingMeasurement *durations = _pf_data[this->element].durations;
842 const TimingMeasurement *timestamps = _pf_data[this->element].timestamps;
843 int num_valid = _pf_data[this->element].num_valid;
844 int point = _pf_data[this->element].prev_index;
845
846 TimingMeasurement lastts = timestamps[point];
847 TimingMeasurement time_sum = 0;
848 TimingMeasurement peak_value = 0;
849 int count = 0;
850
851 /* Sensible default for when too few measurements are available */
852 this->horizontal_scale = 4;
853
854 for (int i = 1; i < num_valid; i++) {
855 point--;
856 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
857
858 TimingMeasurement value = durations[point];
859 if (value == PerformanceData::INVALID_DURATION) {
860 /* Skip gaps in data by pretending time is continuous across them */
861 lastts = timestamps[point];
862 continue;
863 }
864 if (value > peak_value) peak_value = value;
865 count++;
866
867 /* Accumulate period of time covered by data */
868 time_sum += lastts - timestamps[point];
869 lastts = timestamps[point];
870
871 /* Enough data to select a range and get decent data density */
872 if (count == 60) this->SelectHorizontalScale(time_sum);
873
874 /* End when enough points have been collected and the horizontal scale has been exceeded */
875 if (count >= 60 && time_sum >= (this->horizontal_scale + 2) * TIMESTAMP_PRECISION / 2) break;
876 }
877
878 this->SelectVerticalScale(peak_value);
879 }
880
882 IntervalTimer<TimerWindow> update_interval = {std::chrono::milliseconds(500), [this](auto) {
883 this->UpdateScale();
884 }};
885
886 void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
887 {
888 this->SetDirty();
889 }
890
892 template<typename T>
893 static inline T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
894 {
895 T dst_diff = dst_max - dst_min;
896 T src_diff = src_max - src_min;
897 return (value - src_min) * dst_diff / src_diff + dst_min;
898 }
899
900 void DrawWidget(const Rect &r, WidgetID widget) const override
901 {
902 if (widget == WID_FGW_GRAPH) {
903 const TimingMeasurement *durations = _pf_data[this->element].durations;
904 const TimingMeasurement *timestamps = _pf_data[this->element].timestamps;
905 int point = _pf_data[this->element].prev_index;
906
907 const int x_zero = r.right - (int)this->graph_size.width;
908 const int x_max = r.right;
909 const int y_zero = r.top + (int)this->graph_size.height;
910 const int y_max = r.top;
911 const int c_grid = PC_DARK_GREY;
912 const int c_lines = PC_BLACK;
913 const int c_peak = PC_DARK_RED;
914
915 const TimingMeasurement draw_horz_scale = (TimingMeasurement)this->horizontal_scale * TIMESTAMP_PRECISION / 2;
916 const TimingMeasurement draw_vert_scale = (TimingMeasurement)this->vertical_scale;
917
918 /* Number of \c horizontal_scale units in each horizontal division */
919 const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
920 /* Number of divisions of the horizontal axis */
921 const uint horz_divisions = this->horizontal_scale / horz_div_scl;
922 /* Number of divisions of the vertical axis */
923 const uint vert_divisions = 10;
924
925 /* Draw division lines and labels for the vertical axis */
926 for (uint division = 0; division < vert_divisions; division++) {
927 int y = Scinterlate(y_zero, y_max, 0, (int)vert_divisions, (int)division);
928 GfxDrawLine(x_zero, y, x_max, y, c_grid);
929 if (division % 2 == 0) {
930 if ((TimingMeasurement)this->vertical_scale > TIMESTAMP_PRECISION) {
931 SetDParam(0, this->vertical_scale * division / 10 / TIMESTAMP_PRECISION);
932 DrawString(r.left, x_zero - 2, y - GetCharacterHeight(FS_SMALL), STR_FRAMERATE_GRAPH_SECONDS, TC_GREY, SA_RIGHT | SA_FORCE, false, FS_SMALL);
933 } else {
934 SetDParam(0, this->vertical_scale * division / 10 * 1000 / TIMESTAMP_PRECISION);
935 DrawString(r.left, x_zero - 2, y - GetCharacterHeight(FS_SMALL), STR_FRAMERATE_GRAPH_MILLISECONDS, TC_GREY, SA_RIGHT | SA_FORCE, false, FS_SMALL);
936 }
937 }
938 }
939 /* Draw division lines and labels for the horizontal axis */
940 for (uint division = horz_divisions; division > 0; division--) {
941 int x = Scinterlate(x_zero, x_max, 0, (int)horz_divisions, (int)horz_divisions - (int)division);
942 GfxDrawLine(x, y_max, x, y_zero, c_grid);
943 if (division % 2 == 0) {
944 SetDParam(0, division * horz_div_scl / 2);
945 DrawString(x, x_max, y_zero + 2, STR_FRAMERATE_GRAPH_SECONDS, TC_GREY, SA_LEFT | SA_FORCE, false, FS_SMALL);
946 }
947 }
948
949 /* Position of last rendered data point */
950 Point lastpoint = {
951 x_max,
952 (int)Scinterlate<int64_t>(y_zero, y_max, 0, this->vertical_scale, durations[point])
953 };
954 /* Timestamp of last rendered data point */
955 TimingMeasurement lastts = timestamps[point];
956
957 TimingMeasurement peak_value = 0;
958 Point peak_point = { 0, 0 };
959 TimingMeasurement value_sum = 0;
960 TimingMeasurement time_sum = 0;
961 int points_drawn = 0;
962
963 for (int i = 1; i < NUM_FRAMERATE_POINTS; i++) {
964 point--;
965 if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
966
967 TimingMeasurement value = durations[point];
968 if (value == PerformanceData::INVALID_DURATION) {
969 /* Skip gaps in measurements, pretend the data points on each side are continuous */
970 lastts = timestamps[point];
971 continue;
972 }
973
974 /* Use total time period covered for value along horizontal axis */
975 time_sum += lastts - timestamps[point];
976 lastts = timestamps[point];
977 /* Stop if past the width of the graph */
978 if (time_sum > draw_horz_scale) break;
979
980 /* Draw line from previous point to new point */
981 Point newpoint = {
982 (int)Scinterlate<int64_t>(x_zero, x_max, 0, (int64_t)draw_horz_scale, (int64_t)draw_horz_scale - (int64_t)time_sum),
983 (int)Scinterlate<int64_t>(y_zero, y_max, 0, (int64_t)draw_vert_scale, (int64_t)value)
984 };
985 if (newpoint.x > lastpoint.x) continue; // don't draw backwards
986 GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
987 lastpoint = newpoint;
988
989 /* Record peak and average value across graphed data */
990 value_sum += value;
991 points_drawn++;
992 if (value > peak_value) {
993 peak_value = value;
994 peak_point = newpoint;
995 }
996 }
997
998 /* If the peak value is significantly larger than the average, mark and label it */
999 if (points_drawn > 0 && peak_value > TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
1000 TextColour tc_peak = (TextColour)(TC_IS_PALETTE_COLOUR | c_peak);
1001 GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
1002 SetDParam(0, peak_value * 1000 / TIMESTAMP_PRECISION);
1003 int label_y = std::max(y_max, peak_point.y - GetCharacterHeight(FS_SMALL));
1004 if (peak_point.x - x_zero > (int)this->graph_size.width / 2) {
1005 DrawString(x_zero, peak_point.x - 2, label_y, STR_FRAMERATE_GRAPH_MILLISECONDS, tc_peak, SA_RIGHT | SA_FORCE, false, FS_SMALL);
1006 } else {
1007 DrawString(peak_point.x + 2, x_max, label_y, STR_FRAMERATE_GRAPH_MILLISECONDS, tc_peak, SA_LEFT | SA_FORCE, false, FS_SMALL);
1008 }
1009 }
1010 }
1011 }
1012};
1013
1014static WindowDesc _frametime_graph_window_desc(
1015 WDP_AUTO, "frametime_graph", 140, 90,
1016 WC_FRAMETIME_GRAPH, WC_NONE,
1017 0,
1018 _frametime_graph_window_widgets
1019);
1020
1021
1022
1024 void ShowFramerateWindow()
1025{
1026 AllocateWindowDescFront<FramerateWindow>(_framerate_display_desc, 0);
1027}
1028
1030 void ShowFrametimeGraphWindow(PerformanceElement elem)
1031{
1032 if (elem < PFE_FIRST || elem >= PFE_MAX) return; // maybe warn?
1033 AllocateWindowDescFront<FrametimeGraphWindow>(_frametime_graph_window_desc, elem, true);
1034}
1035
1037 void ConPrintFramerate()
1038{
1039 const int count1 = NUM_FRAMERATE_POINTS / 8;
1040 const int count2 = NUM_FRAMERATE_POINTS / 4;
1041 const int count3 = NUM_FRAMERATE_POINTS / 1;
1042
1043 IConsolePrint(TC_SILVER, "Based on num. data points: {} {} {}", count1, count2, count3);
1044
1045 static const std::array<std::string_view, PFE_MAX> MEASUREMENT_NAMES = {
1046 "Game loop",
1047 " GL station ticks",
1048 " GL train ticks",
1049 " GL road vehicle ticks",
1050 " GL ship ticks",
1051 " GL aircraft ticks",
1052 " GL landscape ticks",
1053 " GL link graph delays",
1054 "Drawing",
1055 " Viewport drawing",
1056 "Video output",
1057 "Sound mixing",
1058 "AI/GS scripts total",
1059 "Game script",
1060 };
1061 std::string ai_name_buf;
1062
1063 bool printed_anything = false;
1064
1065 for (const auto &e : { PFE_GAMELOOP, PFE_DRAWING, PFE_VIDEO }) {
1066 auto &pf = _pf_data[e];
1067 if (pf.num_valid == 0) continue;
1068 IConsolePrint(TC_GREEN, "{} rate: {:.2f}fps (expected: {:.2f}fps)",
1069 MEASUREMENT_NAMES[e],
1070 pf.GetRate(),
1071 pf.expected_rate);
1072 printed_anything = true;
1073 }
1074
1075 for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
1076 auto &pf = _pf_data[e];
1077 if (pf.num_valid == 0) continue;
1078 std::string_view name;
1079 if (e < PFE_AI0) {
1080 name = MEASUREMENT_NAMES[e];
1081 } else {
1082 ai_name_buf = fmt::format("AI {} {}", e - PFE_AI0 + 1, GetAIName(e - PFE_AI0));
1083 name = ai_name_buf;
1084 }
1085 IConsolePrint(TC_LIGHT_BLUE, "{} times: {:.2f}ms {:.2f}ms {:.2f}ms",
1086 name,
1087 pf.GetAverageDurationMilliseconds(count1),
1088 pf.GetAverageDurationMilliseconds(count2),
1089 pf.GetAverageDurationMilliseconds(count3));
1090 printed_anything = true;
1091 }
1092
1093 if (!printed_anything) {
1094 IConsolePrint(CC_ERROR, "No performance measurements have been taken yet.");
1095 }
1096}
1097
1105 void ProcessPendingPerformanceMeasurements()
1106{
1107 if (_sound_perf_pending.load(std::memory_order_acquire)) {
1108 std::lock_guard lk(_sound_perf_lock);
1109 for (size_t i = 0; i < _sound_perf_measurements.size(); i += 2) {
1110 _pf_data[PFE_SOUND].Add(_sound_perf_measurements[i], _sound_perf_measurements[i + 1]);
1111 }
1112 _sound_perf_measurements.clear();
1113 _sound_perf_pending.store(false, std::memory_order_relaxed);
1114 }
1115}
ai_info.hpp
AIInfo keeps track of all information of an AI, like Author, Description, ...
ai_instance.hpp
The AIInstance tracks an AI.
Game::GetInstance
static class GameInstance * GetInstance()
Get the current active instance.
Definition game.hpp:101
IntervalTimer
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
PerformanceAccumulator::~PerformanceAccumulator
~PerformanceAccumulator()
Finish and add one block of the accumulating value.
Definition framerate_gui.cpp:318
PerformanceAccumulator::Reset
static void Reset(PerformanceElement elem)
Store the previous accumulator value and reset for a new cycle of accumulating measurements.
Definition framerate_gui.cpp:328
PerformanceAccumulator::PerformanceAccumulator
PerformanceAccumulator(PerformanceElement elem)
Begin measuring one block of the accumulating value.
Definition framerate_gui.cpp:309
PerformanceMeasurer::SetInactive
static void SetInactive(PerformanceElement elem)
Mark a performance element as not currently in use.
Definition framerate_gui.cpp:287
PerformanceMeasurer::Paused
static void Paused(PerformanceElement elem)
Indicate that a cycle of "pause" where no processing occurs.
Definition framerate_gui.cpp:298
PerformanceMeasurer::SetExpectedRate
void SetExpectedRate(double rate)
Set the rate of expected cycles per second of a performance element.
Definition framerate_gui.cpp:281
PerformanceMeasurer::PerformanceMeasurer
PerformanceMeasurer(PerformanceElement elem)
Begin a cycle of a measured element.
Definition framerate_gui.cpp:243
PerformanceMeasurer::~PerformanceMeasurer
~PerformanceMeasurer()
Finish a cycle of a measured element and store the measurement taken.
Definition framerate_gui.cpp:252
Scrollbar
Scrollbar data structure.
Definition widget_type.h:694
Scrollbar::SetCount
void SetCount(size_t num)
Sets the number of elements in the list.
Definition widget_type.h:780
Scrollbar::SetCapacity
void SetCapacity(size_t capacity)
Set the capacity of visible elements.
Definition widget_type.h:794
Scrollbar::GetScrolledRowFromWidget
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition widget.cpp:2377
Scrollbar::GetPosition
size_type GetPosition() const
Gets the position of the first visible element in the list.
Definition widget_type.h:740
WidgetDimensions::scaled
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:28
WidgetDimensions::vsep_normal
int vsep_normal
Normal vertical spacing.
Definition window_gui.h:60
WidgetDimensions::unscaled
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:96
company_base.h
Definition of stuff that is very close to a company, like the company struct itself.
IConsolePrint
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.
Definition console.cpp:89
console_func.h
Console functions used outside of the console code.
console_type.h
Globally used console related types.
CC_ERROR
static const TextColour CC_ERROR
Colour for error lines.
Definition console_type.h:24
GetCharacterHeight
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
ShowFramerateWindow
void ShowFramerateWindow()
Open the general framerate window.
Definition framerate_gui.cpp:1024
GetPerformanceTimer
static TimingMeasurement GetPerformanceTimer()
Return a timestamp with TIMESTAMP_PRECISION ticks per second precision.
Definition framerate_gui.cpp:232
ProcessPendingPerformanceMeasurements
void ProcessPendingPerformanceMeasurements()
This drains the PFE_SOUND measurement data queue into _pf_data.
Definition framerate_gui.cpp:1105
ShowFrametimeGraphWindow
void ShowFrametimeGraphWindow(PerformanceElement elem)
Open a graph window for a performance element.
Definition framerate_gui.cpp:1030
ConPrintFramerate
void ConPrintFramerate()
Print performance statistics to game console.
Definition framerate_gui.cpp:1037
framerate_type.h
Types for recording game performance data.
PerformanceElement
PerformanceElement
Elements of game performance that can be measured.
Definition framerate_type.h:47
PFE_AI6
@ PFE_AI6
AI execution for player slot 7.
Definition framerate_type.h:69
PFE_AI1
@ PFE_AI1
AI execution for player slot 2.
Definition framerate_type.h:64
PFE_GAMELOOP
@ PFE_GAMELOOP
Speed of gameloop processing.
Definition framerate_type.h:49
PFE_AI9
@ PFE_AI9
AI execution for player slot 10.
Definition framerate_type.h:72
PFE_GL_SHIPS
@ PFE_GL_SHIPS
Time spent processing ships.
Definition framerate_type.h:53
PFE_AI3
@ PFE_AI3
AI execution for player slot 4.
Definition framerate_type.h:66
PFE_AI4
@ PFE_AI4
AI execution for player slot 5.
Definition framerate_type.h:67
PFE_AI11
@ PFE_AI11
AI execution for player slot 12.
Definition framerate_type.h:74
PFE_AI8
@ PFE_AI8
AI execution for player slot 9.
Definition framerate_type.h:71
PFE_GAMESCRIPT
@ PFE_GAMESCRIPT
Game script execution.
Definition framerate_type.h:62
PFE_VIDEO
@ PFE_VIDEO
Speed of painting drawn video buffer.
Definition framerate_type.h:59
PFE_GL_LINKGRAPH
@ PFE_GL_LINKGRAPH
Time spent waiting for link graph background jobs.
Definition framerate_type.h:56
PFE_AI13
@ PFE_AI13
AI execution for player slot 14.
Definition framerate_type.h:76
PFE_AI7
@ PFE_AI7
AI execution for player slot 8.
Definition framerate_type.h:70
PFE_GL_AIRCRAFT
@ PFE_GL_AIRCRAFT
Time spent processing aircraft.
Definition framerate_type.h:54
PFE_GL_ECONOMY
@ PFE_GL_ECONOMY
Time spent processing cargo movement.
Definition framerate_type.h:50
PFE_AI0
@ PFE_AI0
AI execution for player slot 1.
Definition framerate_type.h:63
PFE_DRAWING
@ PFE_DRAWING
Speed of drawing world and GUI.
Definition framerate_type.h:57
PFE_AI12
@ PFE_AI12
AI execution for player slot 13.
Definition framerate_type.h:75
PFE_AI2
@ PFE_AI2
AI execution for player slot 3.
Definition framerate_type.h:65
PFE_AI10
@ PFE_AI10
AI execution for player slot 11.
Definition framerate_type.h:73
PFE_GL_LANDSCAPE
@ PFE_GL_LANDSCAPE
Time spent processing other world features.
Definition framerate_type.h:55
PFE_GL_ROADVEHS
@ PFE_GL_ROADVEHS
Time spend processing road vehicles.
Definition framerate_type.h:52
PFE_GL_TRAINS
@ PFE_GL_TRAINS
Time spent processing trains.
Definition framerate_type.h:51
PFE_MAX
@ PFE_MAX
End of enum, must be last.
Definition framerate_type.h:78
PFE_ALLSCRIPTS
@ PFE_ALLSCRIPTS
Sum of all GS/AI scripts.
Definition framerate_type.h:61
PFE_SOUND
@ PFE_SOUND
Speed of mixing audio samples.
Definition framerate_type.h:60
PFE_DRAWWORLD
@ PFE_DRAWWORLD
Time spent drawing world viewports in GUI.
Definition framerate_type.h:58
PFE_AI14
@ PFE_AI14
AI execution for player slot 15.
Definition framerate_type.h:77
PFE_AI5
@ PFE_AI5
AI execution for player slot 6.
Definition framerate_type.h:68
TimingMeasurement
uint64_t TimingMeasurement
Type used to hold a performance timing measurement.
Definition framerate_type.h:83
framerate_widget.h
Types related to the framerate windows widgets.
game.hpp
Base functions for all Games.
game_instance.hpp
The GameInstance tracks games.
GetStringBoundingBox
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:851
DrawString
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.
Definition gfx.cpp:657
GfxFillRect
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.
Definition gfx.cpp:114
gfx_func.h
Functions related to the gfx engine.
SA_LEFT
@ SA_LEFT
Left align the text.
Definition gfx_type.h:343
SA_RIGHT
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:345
SA_FORCE
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:355
SA_CENTER
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:353
FS_SMALL
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:210
FS_NORMAL
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:209
TextColour
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:260
TC_IS_PALETTE_COLOUR
@ TC_IS_PALETTE_COLOUR
Colour value is already a real palette colour index, not an index of a StringColour.
Definition gfx_type.h:283
MILLISECONDS_PER_TICK
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
Definition gfx_type.h:325
SetFill
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
Definition widget_type.h:1181
SetPIP
constexpr NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
Definition widget_type.h:1271
SetScrollbar
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
Definition widget_type.h:1295
SetPadding
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
Definition widget_type.h:1228
SetDataTip
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
Definition widget_type.h:1202
SetTextStyle
constexpr NWidgetPart SetTextStyle(TextColour colour, FontSize size=FS_NORMAL)
Widget part function for setting the text style.
Definition widget_type.h:1160
NWidget
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
Definition widget_type.h:1320
EndContainer
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Definition widget_type.h:1191
SetResize
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Definition widget_type.h:1126
Window::SetDirty
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:940
anonymous_namespace{framerate_gui.cpp}::TIMESTAMP_PRECISION
const TimingMeasurement TIMESTAMP_PRECISION
Units a second is divided into in performance measurements
Definition framerate_gui.cpp:48
anonymous_namespace{framerate_gui.cpp}::GL_RATE
static const double GL_RATE
Game loop rate, cycles per second
Definition framerate_gui.cpp:185
anonymous_namespace{framerate_gui.cpp}::NUM_FRAMERATE_POINTS
const int NUM_FRAMERATE_POINTS
Number of data points to keep in buffer for each performance measurement.
Definition framerate_gui.cpp:46
anonymous_namespace{framerate_gui.cpp}::_pf_data
PerformanceData _pf_data[PFE_MAX]
Storage for all performance element measurements.
Definition framerate_gui.cpp:192
GetSoundPoolAllocatedMemory
size_t GetSoundPoolAllocatedMemory()
Get size of memory allocated to sound effects.
Definition newgrf_sound.cpp:66
newgrf_sound.h
Functions related to NewGRF provided sounds.
PC_DARK_GREY
static const uint8_t PC_DARK_GREY
Dark grey palette colour.
Definition palette_func.h:68
PC_BLACK
static const uint8_t PC_BLACK
Black palette colour.
Definition palette_func.h:67
PC_DARK_RED
static const uint8_t PC_DARK_RED
Dark red palette colour.
Definition palette_func.h:73
safeguards.h
A number of safeguards to prevent using unsafe methods.
_settings_client
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
sprites.h
This file contains all sprite-related enums and defines.
string_func.h
Functions related to low-level strings.
SetDParam
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
SetDParamStr
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition strings.cpp:371
SetDParamMaxDigits
void SetDParamMaxDigits(size_t n, uint count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition strings.cpp:143
strings_func.h
Functions related to OTTD's strings.
StringID
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition strings_type.h:16
ClientSettings::gui
GUISettings gui
settings related to the GUI
Definition settings_type.h:610
Company::IsValidAiID
static bool IsValidAiID(size_t index)
Is this company a valid company, controlled by the computer (a NoAI program)?
Definition company_base.h:159
Dimension
Dimensions (a width and height) of a rectangle in 2D.
Definition geometry_type.hpp:30
FramerateWindow::CachedDecimal
Definition framerate_gui.cpp:414
FramerateWindow
Definition framerate_gui.cpp:409
FramerateWindow::OnResize
void OnResize() override
Called after the window got resized.
Definition framerate_gui.cpp:715
FramerateWindow::rate_drawing
CachedDecimal rate_drawing
cached drawing frame rate
Definition framerate_gui.cpp:442
FramerateWindow::UpdateWidgetSize
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.
Definition framerate_gui.cpp:531
FramerateWindow::update_interval
IntervalTimer< TimerWindow > update_interval
Update the window on a regular interval.
Definition framerate_gui.cpp:472
FramerateWindow::DrawElementTimesColumn
void DrawElementTimesColumn(const Rect &r, StringID heading_str, const CachedDecimal *values) const
Render a column of formatted average durations.
Definition framerate_gui.cpp:589
FramerateWindow::speed_gameloop
CachedDecimal speed_gameloop
cached game loop speed factor
Definition framerate_gui.cpp:443
FramerateWindow::OnClick
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Definition framerate_gui.cpp:690
FramerateWindow::SetStringParameters
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
Definition framerate_gui.cpp:503
FramerateWindow::OnRealtimeTick
void OnRealtimeTick(uint delta_ms) override
Called periodically.
Definition framerate_gui.cpp:460
FramerateWindow::rate_gameloop
CachedDecimal rate_gameloop
cached game loop tick rate
Definition framerate_gui.cpp:441
FramerateWindow::times_longterm
CachedDecimal times_longterm[PFE_MAX]
cached long term average times
Definition framerate_gui.cpp:445
FramerateWindow::DrawWidget
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Definition framerate_gui.cpp:648
FramerateWindow::MIN_ELEMENTS
static constexpr int MIN_ELEMENTS
smallest number of elements to display
Definition framerate_gui.cpp:447
FramerateWindow::times_shortterm
CachedDecimal times_shortterm[PFE_MAX]
cached short term average times
Definition framerate_gui.cpp:444
FrametimeGraphWindow
Definition framerate_gui.cpp:745
FrametimeGraphWindow::horizontal_scale
int horizontal_scale
number of half-second units horizontally
Definition framerate_gui.cpp:747
FrametimeGraphWindow::SetStringParameters
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
Definition framerate_gui.cpp:762
FrametimeGraphWindow::OnRealtimeTick
void OnRealtimeTick(uint delta_ms) override
Called periodically.
Definition framerate_gui.cpp:886
FrametimeGraphWindow::UpdateScale
void UpdateScale()
Recalculate the graph scaling factors based on current recorded data.
Definition framerate_gui.cpp:839
FrametimeGraphWindow::DrawWidget
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Definition framerate_gui.cpp:900
FrametimeGraphWindow::update_interval
IntervalTimer< TimerWindow > update_interval
Update the scaling on a regular interval.
Definition framerate_gui.cpp:882
FrametimeGraphWindow::graph_size
Dimension graph_size
size of the main graph area (excluding axis labels)
Definition framerate_gui.cpp:750
FrametimeGraphWindow::element
PerformanceElement element
what element this window renders graph for
Definition framerate_gui.cpp:749
FrametimeGraphWindow::UpdateWidgetSize
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.
Definition framerate_gui.cpp:777
FrametimeGraphWindow::Scinterlate
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.
Definition framerate_gui.cpp:893
FrametimeGraphWindow::vertical_scale
int vertical_scale
number of TIMESTAMP_PRECISION units vertically
Definition framerate_gui.cpp:746
GUISettings::refresh_rate
uint16_t refresh_rate
How often we refresh the screen (time between draw-ticks).
Definition settings_type.h:209
NWidgetPart
Partial widget specification to allow NWidgets to be written nested.
Definition widget_type.h:1075
Point
Coordinates of a point in 2D.
Definition geometry_type.hpp:21
Pool::PoolItem<&_company_pool >::Get
static Titem * Get(size_t index)
Returns Titem with given index.
Definition pool_type.hpp:339
Rect
Specification of a rectangle with absolute coordinates of all edges.
Definition geometry_type.hpp:75
WindowDesc
High level window description.
Definition window_gui.h:159
Window
Data structure for an opened window.
Definition window_gui.h:273
Window::resize
ResizeInfo resize
Resize information.
Definition window_gui.h:314
Window::scale
int scale
Scale of this window – used to determine how to resize.
Definition window_gui.h:304
Window::IsShaded
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:563
Window::GetWidget
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:977
Window::InitNested
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1746
Window::GetScrollbar
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:314
anonymous_namespace{framerate_gui.cpp}::PerformanceData
Definition framerate_gui.cpp:50
anonymous_namespace{framerate_gui.cpp}::PerformanceData::acc_duration
TimingMeasurement acc_duration
Current accumulated duration.
Definition framerate_gui.cpp:68
anonymous_namespace{framerate_gui.cpp}::PerformanceData::next_index
int next_index
Next index to write to in durations and timestamps.
Definition framerate_gui.cpp:61
anonymous_namespace{framerate_gui.cpp}::PerformanceData::AddAccumulate
void AddAccumulate(TimingMeasurement duration)
Accumulate a period onto the current measurement.
Definition framerate_gui.cpp:106
anonymous_namespace{framerate_gui.cpp}::PerformanceData::acc_timestamp
TimingMeasurement acc_timestamp
Start time for current accumulation cycle.
Definition framerate_gui.cpp:70
anonymous_namespace{framerate_gui.cpp}::PerformanceData::GetAverageDurationMilliseconds
double GetAverageDurationMilliseconds(int count)
Get average cycle processing time over a number of data points.
Definition framerate_gui.cpp:125
anonymous_namespace{framerate_gui.cpp}::PerformanceData::expected_rate
double expected_rate
Expected number of cycles per second when the system is running without slowdowns.
Definition framerate_gui.cpp:59
anonymous_namespace{framerate_gui.cpp}::PerformanceData::prev_index
int prev_index
Last index written to in durations and timestamps.
Definition framerate_gui.cpp:63
anonymous_namespace{framerate_gui.cpp}::PerformanceData::GetRate
double GetRate()
Get current rate of a performance element, based on approximately the past one second of data.
Definition framerate_gui.cpp:149
anonymous_namespace{framerate_gui.cpp}::PerformanceData::num_valid
int num_valid
Number of data points recorded, clamped to NUM_FRAMERATE_POINTS.
Definition framerate_gui.cpp:65
anonymous_namespace{framerate_gui.cpp}::PerformanceData::timestamps
TimingMeasurement timestamps[NUM_FRAMERATE_POINTS]
Start time of each cycle of the performance element, circular buffer.
Definition framerate_gui.cpp:57
anonymous_namespace{framerate_gui.cpp}::PerformanceData::AddPause
void AddPause(TimingMeasurement start_time)
Indicate a pause/expected discontinuity in processing the element.
Definition framerate_gui.cpp:112
anonymous_namespace{framerate_gui.cpp}::PerformanceData::PerformanceData
PerformanceData(double expected_rate)
Initialize a data element with an expected collection rate.
Definition framerate_gui.cpp:78
anonymous_namespace{framerate_gui.cpp}::PerformanceData::INVALID_DURATION
static const TimingMeasurement INVALID_DURATION
Duration value indicating the value is not valid should be considered a gap in measurements.
Definition framerate_gui.cpp:52
anonymous_namespace{framerate_gui.cpp}::PerformanceData::BeginAccumulate
void BeginAccumulate(TimingMeasurement start_time)
Begin an accumulation of multiple measurements into a single value, from a given start time.
Definition framerate_gui.cpp:92
anonymous_namespace{framerate_gui.cpp}::PerformanceData::Add
void Add(TimingMeasurement start_time, TimingMeasurement end_time)
Collect a complete measurement, given start and ending times for a processing block.
Definition framerate_gui.cpp:81
anonymous_namespace{framerate_gui.cpp}::PerformanceData::durations
TimingMeasurement durations[NUM_FRAMERATE_POINTS]
Time spent processing each cycle of the performance element, circular buffer.
Definition framerate_gui.cpp:55
timer.h
Definition of Interval and OneShot timers.
timer_window.h
Definition of the Window system.
NWID_HORIZONTAL
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:75
WWT_PANEL
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:50
WWT_STICKYBOX
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:66
WWT_SHADEBOX
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:64
WWT_CAPTION
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:61
NWID_VSCROLLBAR
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:85
NWID_VERTICAL
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:77
WWT_CLOSEBOX
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:69
WWT_EMPTY
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:48
WWT_RESIZEBOX
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:68
WWT_TEXT
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:58
ResizeWindow
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2022
window_func.h
Window functions not directly related to making/drawing windows.
window_gui.h
Functions, definitions and such used only by the GUI.
WDP_AUTO
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:147
WidgetID
int WidgetID
Widget ID.
Definition window_type.h:18
WindowNumber
int32_t WindowNumber
Number to differentiate different windows of the same class.
Definition window_type.h:740
WC_FRAMETIME_GRAPH
@ WC_FRAMETIME_GRAPH
Frame time graph; Window numbers:
Definition window_type.h:710
WC_NONE
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:45
WC_FRAMERATE_DISPLAY
@ WC_FRAMERATE_DISPLAY
Framerate display; Window numbers:
Definition window_type.h:704