OpenTTD Source: src/framerate_gui.cpp Source File

OpenTTD Source 13.1

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