OpenTTD
framerate_gui.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
9 
12 #include "framerate_type.h"
13 #include <chrono>
14 #include "gfx_func.h"
15 #include "window_gui.h"
16 #include "window_func.h"
17 #include "table/sprites.h"
18 #include "string_func.h"
19 #include "strings_func.h"
20 #include "console_func.h"
21 #include "console_type.h"
22 #include "guitimer_func.h"
23 #include "company_base.h"
24 #include "ai/ai_info.hpp"
25 #include "ai/ai_instance.hpp"
26 #include "game/game.hpp"
27 #include "game/game_instance.hpp"
28 
30 #include "safeguards.h"
31 
32 
36 namespace {
37 
39  const int NUM_FRAMERATE_POINTS = 512;
42 
43  struct PerformanceData {
45  static const TimingMeasurement INVALID_DURATION = UINT64_MAX;
46 
52  double expected_rate;
58  int num_valid;
59 
64 
71  explicit PerformanceData(double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
72 
74  void Add(TimingMeasurement start_time, TimingMeasurement end_time)
75  {
76  this->durations[this->next_index] = end_time - start_time;
77  this->timestamps[this->next_index] = start_time;
78  this->prev_index = this->next_index;
79  this->next_index += 1;
80  if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
81  this->num_valid = min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
82  }
83 
86  {
87  this->timestamps[this->next_index] = this->acc_timestamp;
88  this->durations[this->next_index] = this->acc_duration;
89  this->prev_index = this->next_index;
90  this->next_index += 1;
91  if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
92  this->num_valid = min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
93 
94  this->acc_duration = 0;
95  this->acc_timestamp = start_time;
96  }
97 
100  {
101  this->acc_duration += duration;
102  }
103 
105  void AddPause(TimingMeasurement start_time)
106  {
107  if (this->durations[this->prev_index] != INVALID_DURATION) {
108  this->timestamps[this->next_index] = start_time;
109  this->durations[this->next_index] = INVALID_DURATION;
110  this->prev_index = this->next_index;
111  this->next_index += 1;
112  if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
113  this->num_valid += 1;
114  }
115  }
116 
119  {
120  count = min(count, this->num_valid);
121 
122  int first_point = this->prev_index - count;
123  if (first_point < 0) first_point += NUM_FRAMERATE_POINTS;
124 
125  /* Sum durations, skipping invalid points */
126  double sumtime = 0;
127  for (int i = first_point; i < first_point + count; i++) {
128  auto d = this->durations[i % NUM_FRAMERATE_POINTS];
129  if (d != INVALID_DURATION) {
130  sumtime += d;
131  } else {
132  /* Don't count the invalid durations */
133  count--;
134  }
135  }
136 
137  if (count == 0) return 0; // avoid div by zero
138  return sumtime * 1000 / count / TIMESTAMP_PRECISION;
139  }
140 
142  double GetRate()
143  {
144  /* Start at last recorded point, end at latest when reaching the earliest recorded point */
145  int point = this->prev_index;
146  int last_point = this->next_index - this->num_valid;
147  if (last_point < 0) last_point += NUM_FRAMERATE_POINTS;
148 
149  /* Number of data points collected */
150  int count = 0;
151  /* Time of previous data point */
152  TimingMeasurement last = this->timestamps[point];
153  /* Total duration covered by collected points */
154  TimingMeasurement total = 0;
155 
156  while (point != last_point) {
157  /* Only record valid data points, but pretend the gaps in measurements aren't there */
158  if (this->durations[point] != INVALID_DURATION) {
159  total += last - this->timestamps[point];
160  count++;
161  }
162  last = this->timestamps[point];
163  if (total >= TIMESTAMP_PRECISION) break; // end after 1 second has been collected
164  point--;
165  if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
166  }
167 
168  if (total == 0 || count == 0) return 0;
169  return (double)count * TIMESTAMP_PRECISION / total;
170  }
171  };
172 
174  static const double GL_RATE = 1000.0 / MILLISECONDS_PER_TICK;
175 
182  PerformanceData(GL_RATE), // PFE_GAMELOOP
183  PerformanceData(1), // PFE_ACC_GL_ECONOMY
184  PerformanceData(1), // PFE_ACC_GL_TRAINS
185  PerformanceData(1), // PFE_ACC_GL_ROADVEHS
186  PerformanceData(1), // PFE_ACC_GL_SHIPS
187  PerformanceData(1), // PFE_ACC_GL_AIRCRAFT
188  PerformanceData(1), // PFE_GL_LANDSCAPE
189  PerformanceData(1), // PFE_GL_LINKGRAPH
190  PerformanceData(GL_RATE), // PFE_DRAWING
191  PerformanceData(1), // PFE_ACC_DRAWWORLD
192  PerformanceData(60.0), // PFE_VIDEO
193  PerformanceData(1000.0 * 8192 / 44100), // PFE_SOUND
194  PerformanceData(1), // PFE_ALLSCRIPTS
195  PerformanceData(1), // PFE_GAMESCRIPT
196  PerformanceData(1), // PFE_AI0 ...
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),
209  PerformanceData(1),
210  PerformanceData(1), // PFE_AI14
211  };
212 
213 }
214 
215 
222 {
223  using namespace std::chrono;
224  return (TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
225 }
226 
227 
233 {
234  assert(elem < PFE_MAX);
235 
236  this->elem = elem;
237  this->start_time = GetPerformanceTimer();
238 }
239 
242 {
243  if (this->elem == PFE_ALLSCRIPTS) {
244  /* Hack to not record scripts total when no scripts are active */
245  bool any_active = _pf_data[PFE_GAMESCRIPT].num_valid > 0;
246  for (uint e = PFE_AI0; e < PFE_MAX; e++) any_active |= _pf_data[e].num_valid > 0;
247  if (!any_active) {
249  return;
250  }
251  }
252  _pf_data[this->elem].Add(this->start_time, GetPerformanceTimer());
253 }
254 
257 {
258  _pf_data[this->elem].expected_rate = rate;
259 }
260 
263 {
264  _pf_data[elem].num_valid = 0;
265  _pf_data[elem].next_index = 0;
266  _pf_data[elem].prev_index = 0;
267 }
268 
274 {
276 }
277 
278 
284 {
285  assert(elem < PFE_MAX);
286 
287  this->elem = elem;
288  this->start_time = GetPerformanceTimer();
289 }
290 
293 {
294  _pf_data[this->elem].AddAccumulate(GetPerformanceTimer() - this->start_time);
295 }
296 
303 {
305 }
306 
307 
309 
310 
311 static const PerformanceElement DISPLAY_ORDER_PFE[PFE_MAX] = {
312  PFE_GAMELOOP,
316  PFE_GL_SHIPS,
321  PFE_AI0,
322  PFE_AI1,
323  PFE_AI2,
324  PFE_AI3,
325  PFE_AI4,
326  PFE_AI5,
327  PFE_AI6,
328  PFE_AI7,
329  PFE_AI8,
330  PFE_AI9,
331  PFE_AI10,
332  PFE_AI11,
333  PFE_AI12,
334  PFE_AI13,
335  PFE_AI14,
337  PFE_DRAWING,
339  PFE_VIDEO,
340  PFE_SOUND,
341 };
342 
343 static const char * GetAIName(int ai_index)
344 {
345  if (!Company::IsValidAiID(ai_index)) return "";
346  return Company::Get(ai_index)->ai_info->GetName();
347 }
348 
350 static const NWidgetPart _framerate_window_widgets[] = {
352  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
353  NWidget(WWT_CAPTION, COLOUR_GREY, WID_FRW_CAPTION), SetDataTip(STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
354  NWidget(WWT_SHADEBOX, COLOUR_GREY),
355  NWidget(WWT_STICKYBOX, COLOUR_GREY),
356  EndContainer(),
357  NWidget(WWT_PANEL, COLOUR_GREY),
358  NWidget(NWID_VERTICAL), SetPadding(6), SetPIP(0, 3, 0),
359  NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_GAMELOOP), SetDataTip(STR_FRAMERATE_RATE_GAMELOOP, STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP),
360  NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_DRAWING), SetDataTip(STR_FRAMERATE_RATE_BLITTER, STR_FRAMERATE_RATE_BLITTER_TOOLTIP),
361  NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_FACTOR), SetDataTip(STR_FRAMERATE_SPEED_FACTOR, STR_FRAMERATE_SPEED_FACTOR_TOOLTIP),
362  EndContainer(),
363  EndContainer(),
365  NWidget(WWT_PANEL, COLOUR_GREY),
366  NWidget(NWID_VERTICAL), SetPadding(6), SetPIP(0, 3, 0),
367  NWidget(NWID_HORIZONTAL), SetPIP(0, 6, 0),
368  NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_NAMES), SetScrollbar(WID_FRW_SCROLLBAR),
369  NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_CURRENT), SetScrollbar(WID_FRW_SCROLLBAR),
370  NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_AVERAGE), SetScrollbar(WID_FRW_SCROLLBAR),
371  NWidget(NWID_SELECTION, INVALID_COLOUR, WID_FRW_SEL_MEMORY),
372  NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_ALLOCSIZE), SetScrollbar(WID_FRW_SCROLLBAR),
373  EndContainer(),
374  EndContainer(),
375  NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_INFO_DATA_POINTS), SetDataTip(STR_FRAMERATE_DATA_POINTS, 0x0),
376  EndContainer(),
377  EndContainer(),
379  NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_FRW_SCROLLBAR),
380  NWidget(WWT_RESIZEBOX, COLOUR_GREY),
381  EndContainer(),
382  EndContainer(),
383 };
384 
386  bool small;
387  bool showing_memory;
388  GUITimer next_update;
389  int num_active;
390  int num_displayed;
391 
392  struct CachedDecimal {
393  StringID strid;
394  uint32 value;
395 
396  inline void SetRate(double value, double target)
397  {
398  const double threshold_good = target * 0.95;
399  const double threshold_bad = target * 2 / 3;
400  value = min(9999.99, value);
401  this->value = (uint32)(value * 100);
402  this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
403  }
404 
405  inline void SetTime(double value, double target)
406  {
407  const double threshold_good = target / 3;
408  const double threshold_bad = target;
409  value = min(9999.99, value);
410  this->value = (uint32)(value * 100);
411  this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
412  }
413 
414  inline void InsertDParams(uint n) const
415  {
416  SetDParam(n, this->value);
417  SetDParam(n + 1, 2);
418  }
419  };
420 
424  CachedDecimal times_shortterm[PFE_MAX];
425  CachedDecimal times_longterm[PFE_MAX];
426 
427  static const int VSPACING = 3;
428  static const int MIN_ELEMENTS = 5;
429 
430  FramerateWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
431  {
432  this->InitNested(number);
433  this->small = this->IsShaded();
434  this->showing_memory = true;
435  this->UpdateData();
436  this->num_displayed = this->num_active;
437  this->next_update.SetInterval(100);
438 
439  /* Window is always initialised to MIN_ELEMENTS height, resize to contain num_displayed */
440  ResizeWindow(this, 0, (max(MIN_ELEMENTS, this->num_displayed) - MIN_ELEMENTS) * FONT_HEIGHT_NORMAL);
441  }
442 
443  void OnRealtimeTick(uint delta_ms) override
444  {
445  bool elapsed = this->next_update.Elapsed(delta_ms);
446 
447  /* Check if the shaded state has changed, switch caption text if it has */
448  if (this->small != this->IsShaded()) {
449  this->small = this->IsShaded();
450  this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
451  elapsed = true;
452  }
453 
454  if (elapsed) {
455  this->UpdateData();
456  this->SetDirty();
457  this->next_update.SetInterval(100);
458  }
459  }
460 
461  void UpdateData()
462  {
463  double gl_rate = _pf_data[PFE_GAMELOOP].GetRate();
464  bool have_script = false;
465  this->rate_gameloop.SetRate(gl_rate, _pf_data[PFE_GAMELOOP].expected_rate);
466  this->speed_gameloop.SetRate(gl_rate / _pf_data[PFE_GAMELOOP].expected_rate, 1.0);
467  if (this->small) return; // in small mode, this is everything needed
468 
469  this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _pf_data[PFE_DRAWING].expected_rate);
470 
471  int new_active = 0;
472  for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
473  this->times_shortterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(8), MILLISECONDS_PER_TICK);
474  this->times_longterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(NUM_FRAMERATE_POINTS), MILLISECONDS_PER_TICK);
475  if (_pf_data[e].num_valid > 0) {
476  new_active++;
477  if (e == PFE_GAMESCRIPT || e >= PFE_AI0) have_script = true;
478  }
479  }
480 
481  if (this->showing_memory != have_script) {
482  NWidgetStacked *plane = this->GetWidget<NWidgetStacked>(WID_FRW_SEL_MEMORY);
483  plane->SetDisplayedPlane(have_script ? 0 : SZSP_VERTICAL);
484  this->showing_memory = have_script;
485  }
486 
487  if (new_active != this->num_active) {
488  this->num_active = new_active;
489  Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
490  sb->SetCount(this->num_active);
491  sb->SetCapacity(min(this->num_displayed, this->num_active));
492  this->ReInit();
493  }
494  }
495 
496  void SetStringParameters(int widget) const override
497  {
498  switch (widget) {
499  case WID_FRW_CAPTION:
500  /* When the window is shaded, the caption shows game loop rate and speed factor */
501  if (!this->small) break;
502  SetDParam(0, this->rate_gameloop.strid);
503  this->rate_gameloop.InsertDParams(1);
504  this->speed_gameloop.InsertDParams(3);
505  break;
506 
507  case WID_FRW_RATE_GAMELOOP:
508  SetDParam(0, this->rate_gameloop.strid);
509  this->rate_gameloop.InsertDParams(1);
510  break;
511  case WID_FRW_RATE_DRAWING:
512  SetDParam(0, this->rate_drawing.strid);
513  this->rate_drawing.InsertDParams(1);
514  break;
515  case WID_FRW_RATE_FACTOR:
516  this->speed_gameloop.InsertDParams(0);
517  break;
518  case WID_FRW_INFO_DATA_POINTS:
520  break;
521  }
522  }
523 
524  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
525  {
526  switch (widget) {
527  case WID_FRW_RATE_GAMELOOP:
528  SetDParam(0, STR_FRAMERATE_FPS_GOOD);
529  SetDParam(1, 999999);
530  SetDParam(2, 2);
531  *size = GetStringBoundingBox(STR_FRAMERATE_RATE_GAMELOOP);
532  break;
533  case WID_FRW_RATE_DRAWING:
534  SetDParam(0, STR_FRAMERATE_FPS_GOOD);
535  SetDParam(1, 999999);
536  SetDParam(2, 2);
537  *size = GetStringBoundingBox(STR_FRAMERATE_RATE_BLITTER);
538  break;
539  case WID_FRW_RATE_FACTOR:
540  SetDParam(0, 999999);
541  SetDParam(1, 2);
542  *size = GetStringBoundingBox(STR_FRAMERATE_SPEED_FACTOR);
543  break;
544 
545  case WID_FRW_TIMES_NAMES: {
546  size->width = 0;
547  size->height = FONT_HEIGHT_NORMAL + VSPACING + MIN_ELEMENTS * FONT_HEIGHT_NORMAL;
548  resize->width = 0;
549  resize->height = FONT_HEIGHT_NORMAL;
550  for (PerformanceElement e : DISPLAY_ORDER_PFE) {
551  if (_pf_data[e].num_valid == 0) continue;
552  Dimension line_size;
553  if (e < PFE_AI0) {
554  line_size = GetStringBoundingBox(STR_FRAMERATE_GAMELOOP + e);
555  } else {
556  SetDParam(0, e - PFE_AI0 + 1);
557  SetDParamStr(1, GetAIName(e - PFE_AI0));
558  line_size = GetStringBoundingBox(STR_FRAMERATE_AI);
559  }
560  size->width = max(size->width, line_size.width);
561  }
562  break;
563  }
564 
565  case WID_FRW_TIMES_CURRENT:
566  case WID_FRW_TIMES_AVERAGE:
567  case WID_FRW_ALLOCSIZE: {
568  *size = GetStringBoundingBox(STR_FRAMERATE_CURRENT + (widget - WID_FRW_TIMES_CURRENT));
569  SetDParam(0, 999999);
570  SetDParam(1, 2);
571  Dimension item_size = GetStringBoundingBox(STR_FRAMERATE_MS_GOOD);
572  size->width = max(size->width, item_size.width);
573  size->height += FONT_HEIGHT_NORMAL * MIN_ELEMENTS + VSPACING;
574  resize->width = 0;
575  resize->height = FONT_HEIGHT_NORMAL;
576  break;
577  }
578  }
579  }
580 
582  void DrawElementTimesColumn(const Rect &r, StringID heading_str, const CachedDecimal *values) const
583  {
584  const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
585  uint16 skip = sb->GetPosition();
586  int drawable = this->num_displayed;
587  int y = r.top;
588  DrawString(r.left, r.right, y, heading_str, TC_FROMSTRING, SA_CENTER, true);
589  y += FONT_HEIGHT_NORMAL + VSPACING;
590  for (PerformanceElement e : DISPLAY_ORDER_PFE) {
591  if (_pf_data[e].num_valid == 0) continue;
592  if (skip > 0) {
593  skip--;
594  } else {
595  values[e].InsertDParams(0);
596  DrawString(r.left, r.right, y, values[e].strid, TC_FROMSTRING, SA_RIGHT);
597  y += FONT_HEIGHT_NORMAL;
598  drawable--;
599  if (drawable == 0) break;
600  }
601  }
602  }
603 
604  void DrawElementAllocationsColumn(const Rect &r) 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, STR_FRAMERATE_MEMORYUSE, TC_FROMSTRING, SA_CENTER, true);
611  y += FONT_HEIGHT_NORMAL + VSPACING;
612  for (PerformanceElement e : DISPLAY_ORDER_PFE) {
613  if (_pf_data[e].num_valid == 0) continue;
614  if (skip > 0) {
615  skip--;
616  } else if (e == PFE_GAMESCRIPT || e >= PFE_AI0) {
617  if (e == PFE_GAMESCRIPT) {
618  SetDParam(0, Game::GetInstance()->GetAllocatedMemory());
619  } else {
620  SetDParam(0, Company::Get(e - PFE_AI0)->ai_instance->GetAllocatedMemory());
621  }
622  DrawString(r.left, r.right, y, STR_FRAMERATE_BYTES_GOOD, TC_FROMSTRING, SA_RIGHT);
623  y += FONT_HEIGHT_NORMAL;
624  drawable--;
625  if (drawable == 0) break;
626  } else {
627  /* skip non-script */
628  y += FONT_HEIGHT_NORMAL;
629  drawable--;
630  if (drawable == 0) break;
631  }
632  }
633  }
634 
635  void DrawWidget(const Rect &r, int widget) const override
636  {
637  switch (widget) {
638  case WID_FRW_TIMES_NAMES: {
639  /* Render a column of titles for performance element names */
640  const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
641  uint16 skip = sb->GetPosition();
642  int drawable = this->num_displayed;
643  int y = r.top + FONT_HEIGHT_NORMAL + VSPACING; // first line contains headings in the value columns
644  for (PerformanceElement e : DISPLAY_ORDER_PFE) {
645  if (_pf_data[e].num_valid == 0) continue;
646  if (skip > 0) {
647  skip--;
648  } else {
649  if (e < PFE_AI0) {
650  DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING, SA_LEFT);
651  } else {
652  SetDParam(0, e - PFE_AI0 + 1);
653  SetDParamStr(1, GetAIName(e - PFE_AI0));
654  DrawString(r.left, r.right, y, STR_FRAMERATE_AI, TC_FROMSTRING, SA_LEFT);
655  }
656  y += FONT_HEIGHT_NORMAL;
657  drawable--;
658  if (drawable == 0) break;
659  }
660  }
661  break;
662  }
663  case WID_FRW_TIMES_CURRENT:
664  /* Render short-term average values */
665  DrawElementTimesColumn(r, STR_FRAMERATE_CURRENT, this->times_shortterm);
666  break;
667  case WID_FRW_TIMES_AVERAGE:
668  /* Render averages of all recorded values */
669  DrawElementTimesColumn(r, STR_FRAMERATE_AVERAGE, this->times_longterm);
670  break;
671  case WID_FRW_ALLOCSIZE:
672  DrawElementAllocationsColumn(r);
673  break;
674  }
675  }
676 
677  void OnClick(Point pt, int widget, int click_count) override
678  {
679  switch (widget) {
680  case WID_FRW_TIMES_NAMES:
681  case WID_FRW_TIMES_CURRENT:
682  case WID_FRW_TIMES_AVERAGE: {
683  /* Open time graph windows when clicking detail measurement lines */
684  const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
685  int line = sb->GetScrolledRowFromWidget(pt.y - FONT_HEIGHT_NORMAL - VSPACING, this, widget, VSPACING, FONT_HEIGHT_NORMAL);
686  if (line != INT_MAX) {
687  line++;
688  /* Find the visible line that was clicked */
689  for (PerformanceElement e : DISPLAY_ORDER_PFE) {
690  if (_pf_data[e].num_valid > 0) line--;
691  if (line == 0) {
693  break;
694  }
695  }
696  }
697  break;
698  }
699  }
700  }
701 
702  void OnResize() override
703  {
704  auto *wid = this->GetWidget<NWidgetResizeBase>(WID_FRW_TIMES_NAMES);
705  this->num_displayed = (wid->current_y - wid->min_y - VSPACING) / FONT_HEIGHT_NORMAL - 1; // subtract 1 for headings
706  this->GetScrollbar(WID_FRW_SCROLLBAR)->SetCapacity(this->num_displayed);
707  }
708 };
709 
710 static WindowDesc _framerate_display_desc(
711  WDP_AUTO, "framerate_display", 0, 0,
713  0,
714  _framerate_window_widgets, lengthof(_framerate_window_widgets)
715 );
716 
717 
719 static const NWidgetPart _frametime_graph_window_widgets[] = {
721  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
722  NWidget(WWT_CAPTION, COLOUR_GREY, WID_FGW_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
723  NWidget(WWT_STICKYBOX, COLOUR_GREY),
724  EndContainer(),
725  NWidget(WWT_PANEL, COLOUR_GREY),
727  NWidget(WWT_EMPTY, COLOUR_GREY, WID_FGW_GRAPH),
728  EndContainer(),
729  EndContainer(),
730 };
731 
736 
739 
740  FrametimeGraphWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
741  {
742  this->element = (PerformanceElement)number;
743  this->horizontal_scale = 4;
744  this->vertical_scale = TIMESTAMP_PRECISION / 10;
745  this->next_scale_update.SetInterval(1);
746 
747  this->InitNested(number);
748  }
749 
750  void SetStringParameters(int widget) const override
751  {
752  switch (widget) {
753  case WID_FGW_CAPTION:
754  if (this->element < PFE_AI0) {
755  SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
756  } else {
757  SetDParam(0, STR_FRAMETIME_CAPTION_AI);
758  SetDParam(1, this->element - PFE_AI0 + 1);
759  SetDParamStr(2, GetAIName(this->element - PFE_AI0));
760  }
761  break;
762  }
763  }
764 
765  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
766  {
767  if (widget == WID_FGW_GRAPH) {
768  SetDParam(0, 100);
769  Dimension size_ms_label = GetStringBoundingBox(STR_FRAMERATE_GRAPH_MILLISECONDS);
770  SetDParam(0, 100);
771  Dimension size_s_label = GetStringBoundingBox(STR_FRAMERATE_GRAPH_SECONDS);
772 
773  /* Size graph in height to fit at least 10 vertical labels with space between, or at least 100 pixels */
774  graph_size.height = max<uint>(100, 10 * (size_ms_label.height + 1));
775  /* Always 2:1 graph area */
776  graph_size.width = 2 * graph_size.height;
777  *size = graph_size;
778 
779  size->width += size_ms_label.width + 2;
780  size->height += size_s_label.height + 2;
781  }
782  }
783 
784  void SelectHorizontalScale(TimingMeasurement range)
785  {
786  /* Determine horizontal scale based on period covered by 60 points
787  * (slightly less than 2 seconds at full game speed) */
788  struct ScaleDef { TimingMeasurement range; int scale; };
789  static const ScaleDef hscales[] = {
790  { 120, 60 },
791  { 10, 20 },
792  { 5, 10 },
793  { 3, 4 },
794  { 1, 2 },
795  };
796  for (const ScaleDef *sc = hscales; sc < hscales + lengthof(hscales); sc++) {
797  if (range < sc->range) this->horizontal_scale = sc->scale;
798  }
799  }
800 
801  void SelectVerticalScale(TimingMeasurement range)
802  {
803  /* Determine vertical scale based on peak value (within the horizontal scale + a bit) */
804  static const TimingMeasurement vscales[] = {
805  TIMESTAMP_PRECISION * 100,
806  TIMESTAMP_PRECISION * 10,
807  TIMESTAMP_PRECISION * 5,
809  TIMESTAMP_PRECISION / 2,
810  TIMESTAMP_PRECISION / 5,
811  TIMESTAMP_PRECISION / 10,
812  TIMESTAMP_PRECISION / 50,
813  TIMESTAMP_PRECISION / 200,
814  };
815  for (const TimingMeasurement *sc = vscales; sc < vscales + lengthof(vscales); sc++) {
816  if (range < *sc) this->vertical_scale = (int)*sc;
817  }
818  }
819 
821  void UpdateScale()
822  {
823  const TimingMeasurement *durations = _pf_data[this->element].durations;
824  const TimingMeasurement *timestamps = _pf_data[this->element].timestamps;
825  int num_valid = _pf_data[this->element].num_valid;
826  int point = _pf_data[this->element].prev_index;
827 
828  TimingMeasurement lastts = timestamps[point];
829  TimingMeasurement time_sum = 0;
830  TimingMeasurement peak_value = 0;
831  int count = 0;
832 
833  /* Sensible default for when too few measurements are available */
834  this->horizontal_scale = 4;
835 
836  for (int i = 1; i < num_valid; i++) {
837  point--;
838  if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
839 
840  TimingMeasurement value = durations[point];
841  if (value == PerformanceData::INVALID_DURATION) {
842  /* Skip gaps in data by pretending time is continuous across them */
843  lastts = timestamps[point];
844  continue;
845  }
846  if (value > peak_value) peak_value = value;
847  count++;
848 
849  /* Accumulate period of time covered by data */
850  time_sum += lastts - timestamps[point];
851  lastts = timestamps[point];
852 
853  /* Enough data to select a range and get decent data density */
854  if (count == 60) this->SelectHorizontalScale(time_sum / TIMESTAMP_PRECISION);
855 
856  /* End when enough points have been collected and the horizontal scale has been exceeded */
857  if (count >= 60 && time_sum >= (this->horizontal_scale + 2) * TIMESTAMP_PRECISION / 2) break;
858  }
859 
860  this->SelectVerticalScale(peak_value);
861  }
862 
863  void OnRealtimeTick(uint delta_ms) override
864  {
865  this->SetDirty();
866 
867  if (this->next_scale_update.Elapsed(delta_ms)) {
868  this->next_scale_update.SetInterval(500);
869  this->UpdateScale();
870  }
871  }
872 
874  template<typename T>
875  static inline T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
876  {
877  T dst_diff = dst_max - dst_min;
878  T src_diff = src_max - src_min;
879  return (value - src_min) * dst_diff / src_diff + dst_min;
880  }
881 
882  void DrawWidget(const Rect &r, int widget) const override
883  {
884  if (widget == WID_FGW_GRAPH) {
885  const TimingMeasurement *durations = _pf_data[this->element].durations;
886  const TimingMeasurement *timestamps = _pf_data[this->element].timestamps;
887  int point = _pf_data[this->element].prev_index;
888 
889  const int x_zero = r.right - (int)this->graph_size.width;
890  const int x_max = r.right;
891  const int y_zero = r.top + (int)this->graph_size.height;
892  const int y_max = r.top;
893  const int c_grid = PC_DARK_GREY;
894  const int c_lines = PC_BLACK;
895  const int c_peak = PC_DARK_RED;
896 
897  const TimingMeasurement draw_horz_scale = (TimingMeasurement)this->horizontal_scale * TIMESTAMP_PRECISION / 2;
898  const TimingMeasurement draw_vert_scale = (TimingMeasurement)this->vertical_scale;
899 
900  /* Number of \c horizontal_scale units in each horizontal division */
901  const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
902  /* Number of divisions of the horizontal axis */
903  const uint horz_divisions = this->horizontal_scale / horz_div_scl;
904  /* Number of divisions of the vertical axis */
905  const uint vert_divisions = 10;
906 
907  /* Draw division lines and labels for the vertical axis */
908  for (uint division = 0; division < vert_divisions; division++) {
909  int y = Scinterlate(y_zero, y_max, 0, (int)vert_divisions, (int)division);
910  GfxDrawLine(x_zero, y, x_max, y, c_grid);
911  if (division % 2 == 0) {
912  if ((TimingMeasurement)this->vertical_scale > TIMESTAMP_PRECISION) {
913  SetDParam(0, this->vertical_scale * division / 10 / TIMESTAMP_PRECISION);
914  DrawString(r.left, x_zero - 2, y - FONT_HEIGHT_SMALL, STR_FRAMERATE_GRAPH_SECONDS, TC_GREY, SA_RIGHT | SA_FORCE, false, FS_SMALL);
915  } else {
916  SetDParam(0, this->vertical_scale * division / 10 * 1000 / TIMESTAMP_PRECISION);
917  DrawString(r.left, x_zero - 2, y - FONT_HEIGHT_SMALL, STR_FRAMERATE_GRAPH_MILLISECONDS, TC_GREY, SA_RIGHT | SA_FORCE, false, FS_SMALL);
918  }
919  }
920  }
921  /* Draw division lines and labels for the horizontal axis */
922  for (uint division = horz_divisions; division > 0; division--) {
923  int x = Scinterlate(x_zero, x_max, 0, (int)horz_divisions, (int)horz_divisions - (int)division);
924  GfxDrawLine(x, y_max, x, y_zero, c_grid);
925  if (division % 2 == 0) {
926  SetDParam(0, division * horz_div_scl / 2);
927  DrawString(x, x_max, y_zero + 2, STR_FRAMERATE_GRAPH_SECONDS, TC_GREY, SA_LEFT | SA_FORCE, false, FS_SMALL);
928  }
929  }
930 
931  /* Position of last rendered data point */
932  Point lastpoint = {
933  x_max,
934  (int)Scinterlate<int64>(y_zero, y_max, 0, this->vertical_scale, durations[point])
935  };
936  /* Timestamp of last rendered data point */
937  TimingMeasurement lastts = timestamps[point];
938 
939  TimingMeasurement peak_value = 0;
940  Point peak_point = { 0, 0 };
941  TimingMeasurement value_sum = 0;
942  TimingMeasurement time_sum = 0;
943  int points_drawn = 0;
944 
945  for (int i = 1; i < NUM_FRAMERATE_POINTS; i++) {
946  point--;
947  if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
948 
949  TimingMeasurement value = durations[point];
950  if (value == PerformanceData::INVALID_DURATION) {
951  /* Skip gaps in measurements, pretend the data points on each side are continuous */
952  lastts = timestamps[point];
953  continue;
954  }
955 
956  /* Use total time period covered for value along horizontal axis */
957  time_sum += lastts - timestamps[point];
958  lastts = timestamps[point];
959  /* Stop if past the width of the graph */
960  if (time_sum > draw_horz_scale) break;
961 
962  /* Draw line from previous point to new point */
963  Point newpoint = {
964  (int)Scinterlate<int64>(x_zero, x_max, 0, (int64)draw_horz_scale, (int64)draw_horz_scale - (int64)time_sum),
965  (int)Scinterlate<int64>(y_zero, y_max, 0, (int64)draw_vert_scale, (int64)value)
966  };
967  assert(newpoint.x <= lastpoint.x);
968  GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
969  lastpoint = newpoint;
970 
971  /* Record peak and average value across graphed data */
972  value_sum += value;
973  points_drawn++;
974  if (value > peak_value) {
975  peak_value = value;
976  peak_point = newpoint;
977  }
978  }
979 
980  /* If the peak value is significantly larger than the average, mark and label it */
981  if (points_drawn > 0 && peak_value > TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
982  TextColour tc_peak = (TextColour)(TC_IS_PALETTE_COLOUR | c_peak);
983  GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
984  SetDParam(0, peak_value * 1000 / TIMESTAMP_PRECISION);
985  int label_y = max(y_max, peak_point.y - FONT_HEIGHT_SMALL);
986  if (peak_point.x - x_zero > (int)this->graph_size.width / 2) {
987  DrawString(x_zero, peak_point.x - 2, label_y, STR_FRAMERATE_GRAPH_MILLISECONDS, tc_peak, SA_RIGHT | SA_FORCE, false, FS_SMALL);
988  } else {
989  DrawString(peak_point.x + 2, x_max, label_y, STR_FRAMERATE_GRAPH_MILLISECONDS, tc_peak, SA_LEFT | SA_FORCE, false, FS_SMALL);
990  }
991  }
992  }
993  }
994 };
995 
996 static WindowDesc _frametime_graph_window_desc(
997  WDP_AUTO, "frametime_graph", 140, 90,
999  0,
1000  _frametime_graph_window_widgets, lengthof(_frametime_graph_window_widgets)
1001 );
1002 
1003 
1004 
1007 {
1008  AllocateWindowDescFront<FramerateWindow>(&_framerate_display_desc, 0);
1009 }
1010 
1013 {
1014  if (elem < PFE_FIRST || elem >= PFE_MAX) return; // maybe warn?
1015  AllocateWindowDescFront<FrametimeGraphWindow>(&_frametime_graph_window_desc, elem, true);
1016 }
1017 
1020 {
1021  const int count1 = NUM_FRAMERATE_POINTS / 8;
1022  const int count2 = NUM_FRAMERATE_POINTS / 4;
1023  const int count3 = NUM_FRAMERATE_POINTS / 1;
1024 
1025  IConsolePrintF(TC_SILVER, "Based on num. data points: %d %d %d", count1, count2, count3);
1026 
1027  static const char *MEASUREMENT_NAMES[PFE_MAX] = {
1028  "Game loop",
1029  " GL station ticks",
1030  " GL train ticks",
1031  " GL road vehicle ticks",
1032  " GL ship ticks",
1033  " GL aircraft ticks",
1034  " GL landscape ticks",
1035  " GL link graph delays",
1036  "Drawing",
1037  " Viewport drawing",
1038  "Video output",
1039  "Sound mixing",
1040  "AI/GS scripts total",
1041  "Game script",
1042  };
1043  char ai_name_buf[128];
1044 
1045  static const PerformanceElement rate_elements[] = { PFE_GAMELOOP, PFE_DRAWING, PFE_VIDEO };
1046 
1047  bool printed_anything = false;
1048 
1049  for (const PerformanceElement *e = rate_elements; e < rate_elements + lengthof(rate_elements); e++) {
1050  auto &pf = _pf_data[*e];
1051  if (pf.num_valid == 0) continue;
1052  IConsolePrintF(TC_GREEN, "%s rate: %.2ffps (expected: %.2ffps)",
1053  MEASUREMENT_NAMES[*e],
1054  pf.GetRate(),
1055  pf.expected_rate);
1056  printed_anything = true;
1057  }
1058 
1059  for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
1060  auto &pf = _pf_data[e];
1061  if (pf.num_valid == 0) continue;
1062  const char *name;
1063  if (e < PFE_AI0) {
1064  name = MEASUREMENT_NAMES[e];
1065  } else {
1066  seprintf(ai_name_buf, lastof(ai_name_buf), "AI %d %s", e - PFE_AI0 + 1, GetAIName(e - PFE_AI0)),
1067  name = ai_name_buf;
1068  }
1069  IConsolePrintF(TC_LIGHT_BLUE, "%s times: %.2fms %.2fms %.2fms",
1070  name,
1071  pf.GetAverageDurationMilliseconds(count1),
1072  pf.GetAverageDurationMilliseconds(count2),
1073  pf.GetAverageDurationMilliseconds(count3));
1074  printed_anything = true;
1075  }
1076 
1077  if (!printed_anything) {
1078  IConsoleWarning("No performance measurements have been taken yet");
1079  }
1080 }
Functions related to OTTD&#39;s strings.
Empty widget, place holder to reserve space in widget array.
Definition: widget_type.h:48
Time spent processing cargo movement.
void IConsoleWarning(const char *string)
It is possible to print warnings to the console.
Definition: console.cpp:159
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:112
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:168
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:246
Framerate display; Window numbers:
Definition: window_type.h:688
const TimingMeasurement TIMESTAMP_PRECISION
Units a second is divided into in performance measurements
Scrollbar data structure.
Definition: widget_type.h:589
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:409
Stacked widgets, widgets all occupying the same space in the window.
Definition: widget_type.h:405
PerformanceMeasurer(PerformanceElement elem)
Begin a cycle of a measured element.
Horizontal container.
Definition: widget_type.h:75
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:57
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:68
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:69
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
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:26
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:58
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:686
void SetCount(int num)
Sets the number of elements in the list.
Definition: widget_type.h:670
Force the alignment, i.e. don&#39;t swap for RTL languages.
Definition: gfx_func.h:108
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:910
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:278
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:1046
void SetDParamStr(uint n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition: strings.cpp:281
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:206
GUI Timers.
#define FONT_HEIGHT_SMALL
Height of characters in the small (FS_SMALL) font.
Definition: gfx_func.h:175
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:1959
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:140
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:178
static NWidgetPart SetDataTip(uint32 data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1014
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:126
AI execution for player slot 15.
Center both horizontally and vertically.
Definition: gfx_func.h:106
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:247
AI execution for player slot 7.
AI execution for player slot 6.
Simple depressed panel.
Definition: widget_type.h:50
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:306
~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:1114
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:499
Time spent processing other world features.
static class GameInstance * GetInstance()
Get the current active instance.
Definition: game.hpp:113
void OnRealtimeTick(uint delta_ms) override
Called periodically.
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
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:42
Frame time graph; Window numbers:
Definition: window_type.h:694
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:18
static const uint8 PC_BLACK
Black palette colour.
Definition: gfx_func.h:205
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:698
CachedDecimal rate_drawing
cached drawing frame rate
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:40
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:211
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:61
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:77
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:999
~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:1084
Index of the small font in the font tables.
Definition: gfx_type.h:205
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:270
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:388
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition: widget_type.h:66
int32 WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:707
Specification of a rectangle with absolute coordinates of all edges.
Vertical scrollbar.
Definition: widget_type.h:84
Right align the text (must be a single bit).
Definition: gfx_func.h:98
Left align the text.
Definition: gfx_func.h:96
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:156
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:80
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
Resize the window.
Definition: window.cpp:2126
static NWidgetPart SetScrollbar(int index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1095
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:64
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:1076
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:631
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:201
Speed of mixing audio samples.