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