Add: Show performance of AI and GS in framerate window

pull/78/head
Niels Martin Hansen 5 years ago
parent 4adb91202d
commit 7e1e2756d4

@ -476,6 +476,12 @@ The following is an explanation of the different statistics:
- *World ticks* - Time spent on other world/landscape processing. This
includes towns growing, building animations, updates of farmland and trees,
and station rating updates.
- *GS/AI total*, *Game script*, and *AI players* - Time spent running logic
for game scripts and AI players. The total may show as less than the current
sum of the individual scripts, this is because AI players at lower
difficulty settings do not run every game tick, and hence contribute less
to the average across all ticks. Keep in mind that the "Current" figure is
also an average, just only over short term.
- *Link graph delay* - Time overruns of the cargo distribution link graph
update thread. Usually the link graph is updated in a background thread,
but these updates need to synchronise with the main game loop occasionally,

@ -16,6 +16,7 @@
#include "../company_func.h"
#include "../network/network.h"
#include "../window_func.h"
#include "../framerate_type.h"
#include "ai_scanner.hpp"
#include "ai_instance.hpp"
#include "ai_config.hpp"
@ -79,8 +80,11 @@
const Company *c;
FOR_ALL_COMPANIES(c) {
if (c->is_ai) {
PerformanceMeasurer framerate((PerformanceElement)(PFE_AI0 + c->index));
cur_company.Change(c->index);
c->ai_instance->GameLoop();
} else {
PerformanceMeasurer::SetInactive((PerformanceElement)(PFE_AI0 + c->index));
}
}
cur_company.Restore();
@ -101,6 +105,7 @@
/* static */ void AI::Stop(CompanyID company)
{
if (_networking && !_network_server) return;
PerformanceMeasurer::SetInactive((PerformanceElement)(PFE_AI0 + company));
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
Company *c = Company::Get(company);

@ -13,13 +13,18 @@
#include <chrono>
#include "gfx_func.h"
#include "window_gui.h"
#include "window_func.h"
#include "table/sprites.h"
#include "string_func.h"
#include "strings_func.h"
#include "console_func.h"
#include "console_type.h"
#include "guitimer_func.h"
#include "company_base.h"
#include "ai/ai_info.hpp"
#include "widgets/framerate_widget.h"
#include "safeguards.h"
/**
@ -183,6 +188,23 @@ namespace {
PerformanceData(1), // PFE_ACC_DRAWWORLD
PerformanceData(60.0), // PFE_VIDEO
PerformanceData(1000.0 * 8192 / 44100), // PFE_SOUND
PerformanceData(1), // PFE_ALLSCRIPTS
PerformanceData(1), // PFE_GAMESCRIPT
PerformanceData(1), // PFE_AI0 ...
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1),
PerformanceData(1), // PFE_AI14
};
}
@ -215,6 +237,15 @@ PerformanceMeasurer::PerformanceMeasurer(PerformanceElement elem)
/** Finish a cycle of a measured element and store the measurement taken. */
PerformanceMeasurer::~PerformanceMeasurer()
{
if (this->elem == PFE_ALLSCRIPTS) {
/* Hack to not record scripts total when no scripts are active */
bool any_active = _pf_data[PFE_GAMESCRIPT].num_valid > 0;
for (uint e = PFE_AI0; e < PFE_MAX; e++) any_active |= _pf_data[e].num_valid > 0;
if (!any_active) {
PerformanceMeasurer::SetInactive(PFE_ALLSCRIPTS);
return;
}
}
_pf_data[this->elem].Add(this->start_time, GetPerformanceTimer());
}
@ -224,11 +255,19 @@ void PerformanceMeasurer::SetExpectedRate(double rate)
_pf_data[this->elem].expected_rate = rate;
}
/** Mark a performance element as not currently in use. */
/* static */ void PerformanceMeasurer::SetInactive(PerformanceElement elem)
{
_pf_data[elem].num_valid = 0;
_pf_data[elem].next_index = 0;
_pf_data[elem].prev_index = 0;
}
/**
* Indicate that a cycle of "pause" where no processing occurs.
* @param elem The element not currently being processed
*/
void PerformanceMeasurer::Paused(PerformanceElement elem)
/* static */ void PerformanceMeasurer::Paused(PerformanceElement elem)
{
_pf_data[elem].AddPause(GetPerformanceTimer());
}
@ -266,6 +305,44 @@ void PerformanceAccumulator::Reset(PerformanceElement elem)
void ShowFrametimeGraphWindow(PerformanceElement elem);
static const PerformanceElement DISPLAY_ORDER_PFE[PFE_MAX] = {
PFE_GAMELOOP,
PFE_GL_ECONOMY,
PFE_GL_TRAINS,
PFE_GL_ROADVEHS,
PFE_GL_SHIPS,
PFE_GL_AIRCRAFT,
PFE_GL_LANDSCAPE,
PFE_ALLSCRIPTS,
PFE_GAMESCRIPT,
PFE_AI0,
PFE_AI1,
PFE_AI2,
PFE_AI3,
PFE_AI4,
PFE_AI5,
PFE_AI6,
PFE_AI7,
PFE_AI8,
PFE_AI9,
PFE_AI10,
PFE_AI11,
PFE_AI12,
PFE_AI13,
PFE_AI14,
PFE_GL_LINKGRAPH,
PFE_DRAWING,
PFE_DRAWWORLD,
PFE_VIDEO,
PFE_SOUND,
};
static const char * GetAIName(int ai_index)
{
if (!Company::IsValidAiID(ai_index)) return "";
return Company::Get(ai_index)->ai_info->GetName();
}
/** @hideinitializer */
static const NWidgetPart _framerate_window_widgets[] = {
NWidget(NWID_HORIZONTAL),
@ -296,6 +373,7 @@ static const NWidgetPart _framerate_window_widgets[] = {
struct FramerateWindow : Window {
bool small;
GUITimer next_update;
int num_active;
struct CachedDecimal {
StringID strid;
@ -369,9 +447,16 @@ struct FramerateWindow : Window {
this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _pf_data[PFE_DRAWING].expected_rate);
int new_active = 0;
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
this->times_shortterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(8), MILLISECONDS_PER_TICK);
this->times_longterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(NUM_FRAMERATE_POINTS), MILLISECONDS_PER_TICK);
if (_pf_data[e].num_valid > 0) new_active++;
}
if (new_active != this->num_active) {
this->num_active = new_active;
this->ReInit();
}
}
@ -425,25 +510,32 @@ struct FramerateWindow : Window {
break;
case WID_FRW_TIMES_NAMES: {
int linecount = PFE_MAX - PFE_FIRST;
size->width = 0;
size->height = FONT_HEIGHT_NORMAL * (linecount + 1) + VSPACING;
for (int line = 0; line < linecount; line++) {
Dimension line_size = GetStringBoundingBox(STR_FRAMERATE_GAMELOOP + line);
size->height = FONT_HEIGHT_NORMAL + VSPACING;
for (PerformanceElement e : DISPLAY_ORDER_PFE) {
if (_pf_data[e].num_valid == 0) continue;
Dimension line_size;
if (e < PFE_AI0) {
line_size = GetStringBoundingBox(STR_FRAMERATE_GAMELOOP + e);
} else {
SetDParam(0, e - PFE_AI0 + 1);
SetDParamStr(1, GetAIName(e - PFE_AI0));
line_size = GetStringBoundingBox(STR_FRAMERATE_AI);
}
size->width = max(size->width, line_size.width);
size->height += FONT_HEIGHT_NORMAL;
}
break;
}
case WID_FRW_TIMES_CURRENT:
case WID_FRW_TIMES_AVERAGE: {
int linecount = PFE_MAX - PFE_FIRST;
*size = GetStringBoundingBox(STR_FRAMERATE_CURRENT + (widget - WID_FRW_TIMES_CURRENT));
SetDParam(0, 999999);
SetDParam(1, 2);
Dimension item_size = GetStringBoundingBox(STR_FRAMERATE_MS_GOOD);
size->width = max(size->width, item_size.width);
size->height += FONT_HEIGHT_NORMAL * linecount + VSPACING;
size->height += FONT_HEIGHT_NORMAL * this->num_active + VSPACING;
break;
}
}
@ -456,7 +548,8 @@ struct FramerateWindow : Window {
DrawString(r.left, r.right, y, heading_str, TC_FROMSTRING, SA_CENTER, true);
y += FONT_HEIGHT_NORMAL + VSPACING;
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
for (PerformanceElement e : DISPLAY_ORDER_PFE) {
if (_pf_data[e].num_valid == 0) continue;
values[e].InsertDParams(0);
DrawString(r.left, r.right, y, values[e].strid, TC_FROMSTRING, SA_RIGHT);
y += FONT_HEIGHT_NORMAL;
@ -468,10 +561,16 @@ struct FramerateWindow : Window {
switch (widget) {
case WID_FRW_TIMES_NAMES: {
/* Render a column of titles for performance element names */
int linecount = PFE_MAX - PFE_FIRST;
int y = r.top + FONT_HEIGHT_NORMAL + VSPACING; // first line contains headings in the value columns
for (int i = 0; i < linecount; i++) {
DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + i, TC_FROMSTRING, SA_LEFT);
for (PerformanceElement e : DISPLAY_ORDER_PFE) {
if (_pf_data[e].num_valid == 0) continue;
if (e < PFE_AI0) {
DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + e, TC_FROMSTRING, SA_LEFT);
} else {
SetDParam(0, e - PFE_AI0 + 1);
SetDParamStr(1, GetAIName(e - PFE_AI0));
DrawString(r.left, r.right, y, STR_FRAMERATE_AI, TC_FROMSTRING, SA_LEFT);
}
y += FONT_HEIGHT_NORMAL;
}
break;
@ -496,8 +595,14 @@ struct FramerateWindow : Window {
/* Open time graph windows when clicking detail measurement lines */
int line = this->GetRowFromWidget(pt.y, widget, VSPACING, FONT_HEIGHT_NORMAL);
if (line > 0) {
line -= 1;
ShowFrametimeGraphWindow((PerformanceElement)line);
/* Find the visible line that was clicked */
for (PerformanceElement e : DISPLAY_ORDER_PFE) {
if (_pf_data[e].num_valid > 0) line--;
if (line == 0) {
ShowFrametimeGraphWindow(e);
break;
}
}
}
break;
}
@ -549,7 +654,13 @@ struct FrametimeGraphWindow : Window {
{
switch (widget) {
case WID_FGW_CAPTION:
SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
if (this->element < PFE_AI0) {
SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
} else {
SetDParam(0, STR_FRAMETIME_CAPTION_AI);
SetDParam(1, this->element - PFE_AI0 + 1);
SetDParamStr(2, GetAIName(this->element - PFE_AI0));
}
break;
}
}
@ -829,7 +940,10 @@ void ConPrintFramerate()
" Viewport drawing",
"Video output",
"Sound mixing",
"AI/GS scripts total",
"Game script",
};
char ai_name_buf[128];
static const PerformanceElement rate_elements[] = { PFE_GAMELOOP, PFE_DRAWING, PFE_VIDEO };
@ -848,8 +962,15 @@ void ConPrintFramerate()
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
auto &pf = _pf_data[e];
if (pf.num_valid == 0) continue;
const char *name;
if (e < PFE_AI0) {
name = MEASUREMENT_NAMES[e];
} else {
seprintf(ai_name_buf, lastof(ai_name_buf), "AI %d %s", e - PFE_AI0 + 1, GetAIName(e - PFE_AI0)),
name = ai_name_buf;
}
IConsolePrintF(TC_LIGHT_BLUE, "%s times: %.2fms %.2fms %.2fms",
MEASUREMENT_NAMES[e],
name,
pf.GetAverageDurationMilliseconds(count1),
pf.GetAverageDurationMilliseconds(count2),
pf.GetAverageDurationMilliseconds(count3));

@ -60,6 +60,23 @@ enum PerformanceElement {
PFE_DRAWWORLD, ///< Time spent drawing world viewports in GUI
PFE_VIDEO, ///< Speed of painting drawn video buffer.
PFE_SOUND, ///< Speed of mixing audio samples
PFE_ALLSCRIPTS, ///< Sum of all GS/AI scripts
PFE_GAMESCRIPT, ///< Game script execution
PFE_AI0, ///< AI execution for player slot 1
PFE_AI1, ///< AI execution for player slot 2
PFE_AI2, ///< AI execution for player slot 3
PFE_AI3, ///< AI execution for player slot 4
PFE_AI4, ///< AI execution for player slot 5
PFE_AI5, ///< AI execution for player slot 6
PFE_AI6, ///< AI execution for player slot 7
PFE_AI7, ///< AI execution for player slot 8
PFE_AI8, ///< AI execution for player slot 9
PFE_AI9, ///< AI execution for player slot 10
PFE_AI10, ///< AI execution for player slot 11
PFE_AI11, ///< AI execution for player slot 12
PFE_AI12, ///< AI execution for player slot 13
PFE_AI13, ///< AI execution for player slot 14
PFE_AI14, ///< AI execution for player slot 15
PFE_MAX, ///< End of enum, must be last.
};
DECLARE_POSTFIX_INCREMENT(PerformanceElement)
@ -81,6 +98,7 @@ public:
PerformanceMeasurer(PerformanceElement elem);
~PerformanceMeasurer();
void SetExpectedRate(double rate);
static void SetInactive(PerformanceElement elem);
static void Paused(PerformanceElement elem);
};

@ -15,6 +15,7 @@
#include "../company_func.h"
#include "../network/network.h"
#include "../window_func.h"
#include "../framerate_type.h"
#include "game.hpp"
#include "game_scanner.hpp"
#include "game_config.hpp"
@ -31,8 +32,16 @@
/* static */ void Game::GameLoop()
{
if (_networking && !_network_server) return;
if (Game::instance == NULL) return;
if (_networking && !_network_server) {
PerformanceMeasurer::SetInactive(PFE_GAMESCRIPT);
return;
}
if (Game::instance == NULL) {
PerformanceMeasurer::SetInactive(PFE_GAMESCRIPT);
return;
}
PerformanceMeasurer framerate(PFE_GAMESCRIPT);
Game::frame_counter++;

@ -2747,6 +2747,9 @@ STR_FRAMERATE_DRAWING :{BLACK}Graphics
STR_FRAMERATE_DRAWING_VIEWPORTS :{BLACK} World viewports:
STR_FRAMERATE_VIDEO :{BLACK}Video output:
STR_FRAMERATE_SOUND :{BLACK}Sound mixing:
STR_FRAMERATE_ALLSCRIPTS :{BLACK} GS/AI total:
STR_FRAMERATE_GAMESCRIPT :{BLACK} Game script:
STR_FRAMERATE_AI :{BLACK} AI {NUM} {RAW_STRING}
############ End of leave-in-this-order
############ Leave those lines in this order!!
STR_FRAMETIME_CAPTION_GAMELOOP :Game loop
@ -2761,6 +2764,9 @@ STR_FRAMETIME_CAPTION_DRAWING :Graphics render
STR_FRAMETIME_CAPTION_DRAWING_VIEWPORTS :World viewport rendering
STR_FRAMETIME_CAPTION_VIDEO :Video output
STR_FRAMETIME_CAPTION_SOUND :Sound mixing
STR_FRAMETIME_CAPTION_ALLSCRIPTS :GS/AI scripts total
STR_FRAMETIME_CAPTION_GAMESCRIPT :Game script
STR_FRAMETIME_CAPTION_AI :AI {NUM} {RAW_STRING}
############ End of leave-in-this-order

@ -1407,8 +1407,11 @@ void StateGameLoop()
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
#ifndef DEBUG_DUMP_COMMANDS
AI::GameLoop();
Game::GameLoop();
{
PerformanceMeasurer framerate(PFE_ALLSCRIPTS);
AI::GameLoop();
Game::GameLoop();
}
#endif
UpdateLandscapingLimits();

Loading…
Cancel
Save