1
0
mirror of https://github.com/luanti-org/luanti.git synced 2025-12-13 10:45:21 +01:00

Profiler and profiler graph improvements

This commit is contained in:
sfan5
2025-12-03 19:32:23 +01:00
parent 607531ea6b
commit cf696239e4
13 changed files with 130 additions and 118 deletions

View File

@@ -263,7 +263,6 @@ void ClientEnvironment::step(float dtime)
/*
Step and handle simple objects
*/
g_profiler->avg("ClientEnv: CSO count [#]", m_simple_objects.size());
for (auto i = m_simple_objects.begin(); i != m_simple_objects.end();) {
ClientSimpleObject *simple = *i;

View File

@@ -717,7 +717,6 @@ void ClientMap::updateDrawList()
g_profiler->avg("MapBlock sides skipped [#]", sides_skipped);
g_profiler->avg("MapBlocks examined [#]", blocks_visited);
}
g_profiler->avg("MapBlocks shortlist [#]", shortlist.size());
// must populate either only to avoid duplicates
assert(m_drawlist.empty() || shortlist.empty());

View File

@@ -108,7 +108,6 @@ void Clouds::updateMesh()
return;
}
ScopeProfiler sp(g_profiler, "Clouds::updateMesh()", SPT_AVG);
m_mesh_origin = m_origin;
m_last_noise_center = center_of_drawing_in_noise_i;
m_mesh_valid = true;
@@ -374,7 +373,7 @@ void Clouds::render()
return;
#if 0
{
{ // proof that m_box is correct
video::SMaterial tmp;
tmp.Thickness = 1.f;
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);

View File

@@ -1257,16 +1257,6 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
profiler_print_interval = 3;
}
if (profiler_interval.step(dtime, profiler_print_interval)) {
if (print_to_log) {
infostream << "Profiler:" << std::endl;
g_profiler->print(infostream);
}
m_game_ui->updateProfiler();
g_profiler->clear();
}
// Update graphs
g_profiler->graphAdd("Time non-rendering [us]",
draw_times.busy_time - stats.drawtime);
@@ -1281,6 +1271,16 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
stats2.PrimitivesDrawn / float(stats2.Drawcalls));
g_profiler->avg("Irr: HW buffers uploaded", stats2.HWBuffersUploaded);
g_profiler->avg("Irr: HW buffers active", stats2.HWBuffersActive);
if (profiler_interval.step(dtime, profiler_print_interval)) {
if (print_to_log) {
infostream << "Profiler:" << std::endl;
g_profiler->print(infostream);
}
m_game_ui->updateProfiler();
g_profiler->clear();
}
}
void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
@@ -3644,8 +3644,11 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
*/
v2u32 screensize = this->driver->getScreenSize();
if (this->m_game_ui->m_flags.show_profiler_graph)
graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
if (this->m_game_ui->m_flags.show_profiler_graph) {
auto font = g_fontengine->getFont(
g_fontengine->getDefaultFontSize() * 0.9f, FM_Mono);
graph->draw(10, screensize.Y - 10, driver, font);
}
/*
Damage flash

View File

@@ -44,15 +44,14 @@ void GameUI::init()
{
// First line of debug text
m_guitext = gui::StaticText::add(guienv, utf8_to_wide(PROJECT_NAME_C).c_str(),
core::rect<s32>(0, 0, 0, 0), false, true, guiroot);
core::recti(), false, true, guiroot);
// Second line of debug text
m_guitext2 = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false,
m_guitext2 = gui::StaticText::add(guienv, L"", core::recti(), false,
true, guiroot);
// Chat text
m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0),
//false, false); // Disable word wrap as of now
m_guitext_chat = gui::StaticText::add(guienv, L"", core::recti(),
false, true, guiroot);
u16 chat_font_size = g_settings->getU16("chat_font_size");
if (chat_font_size != 0) {
@@ -74,12 +73,12 @@ void GameUI::init()
// Status text (displays info when showing and hiding GUI stuff, etc.)
m_guitext_status = gui::StaticText::add(guienv, L"<Status>",
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
core::recti(), false, false, guiroot);
m_guitext_status->setVisible(false);
// Profiler text (size is updated when text is updated)
m_guitext_profiler = gui::StaticText::add(guienv, L"<Profiler>",
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
core::recti(), false, false, guiroot);
m_guitext_profiler->setOverrideFont(g_fontengine->getFont(
g_fontengine->getDefaultFontSize() * 0.9f, FM_Mono));
m_guitext_profiler->setVisible(false);
@@ -97,7 +96,7 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
// Minimal debug text must only contain info that can't give a gameplay advantage
if (m_flags.show_minimal_debug) {
const u16 fps = 1.0 / stats.dtime_jitter.avg;
const u16 fps = 1.0f / stats.dtime_jitter.avg;
m_drawtime_avg *= 0.95f;
m_drawtime_avg += 0.05f * (stats.drawtime / 1000);
@@ -105,7 +104,7 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
os << std::fixed
<< PROJECT_NAME_C " " << g_version_hash
<< " | FPS: " << fps
<< std::setprecision(fps >= 100 ? 1 : 0)
<< std::setprecision(m_drawtime_avg < 10 ? 1 : 0)
<< " | drawtime: " << m_drawtime_avg << "ms"
<< std::setprecision(1)
<< " | dtime jitter: "
@@ -118,7 +117,7 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X, screensize.Y));
setStaticText(m_guitext, utf8_to_wide(os.str()).c_str());
setStaticText(m_guitext, utf8_to_wide(os.str()));
minimal_debug_height = m_guitext->getTextHeight();
}
@@ -240,7 +239,7 @@ void GameUI::updateChatSize()
if (m_flags.show_basic_debug)
chat_y += m_guitext2->getTextHeight();
const v2u32 &window_size = RenderingEngine::getWindowSize();
const v2u32 window_size = RenderingEngine::getWindowSize();
core::rect<s32> chat_size(10, chat_y, window_size.X - 20, 0);
chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y,
@@ -255,28 +254,41 @@ void GameUI::updateChatSize()
void GameUI::updateProfiler()
{
if (m_profiler_current_page != 0) {
std::ostringstream os(std::ios_base::binary);
os << " Profiler page " << (int)m_profiler_current_page <<
", elapsed: " << g_profiler->getElapsedMs() << " ms)" << std::endl;
g_profiler->print(os, m_profiler_current_page, m_profiler_max_page);
EnrichedString str(utf8_to_wide(os.str()));
str.setBackground(video::SColor(120, 0, 0, 0));
setStaticText(m_guitext_profiler, str);
core::dimension2d<u32> size = m_guitext_profiler->getOverrideFont()->
getDimension(str.c_str());
core::position2di upper_left(6, m_guitext->getTextHeight() * 2.5f);
core::position2di lower_right = upper_left;
lower_right.X += size.Width + 10;
lower_right.Y += size.Height;
m_guitext_profiler->setRelativePosition(core::rect<s32>(upper_left, lower_right));
}
m_guitext_profiler->setVisible(m_profiler_current_page != 0);
if (m_profiler_current_page == 0)
return;
std::ostringstream oss(std::ios_base::binary);
oss << "Profiler page " << (int)m_profiler_current_page
<< "/" << (int)m_profiler_max_page
<< ", elapsed: " << g_profiler->getElapsedMs() << " ms" << std::endl;
g_profiler->print(oss, m_profiler_current_page, m_profiler_max_page);
EnrichedString str(utf8_to_wide(oss.str()));
str.setBackground(video::SColor(120, 0, 0, 0));
setStaticText(m_guitext_profiler, str);
v2s32 upper_left(5, 10);
if (m_flags.show_minimal_debug)
upper_left.Y += m_guitext->getTextHeight();
if (m_flags.show_basic_debug)
upper_left.Y += m_guitext2->getTextHeight();
v2s32 lower_right = upper_left;
lower_right.X += m_guitext_profiler->getTextWidth() + 5;
lower_right.Y += m_guitext_profiler->getTextHeight();
m_guitext_profiler->setRelativePosition(core::recti(upper_left, lower_right));
// Really dumb heuristic (we have a fixed number of pages, not a fixed page size)
const v2u32 window_size = RenderingEngine::getWindowSize();
if (upper_left.Y + m_guitext_profiler->getTextHeight()
> window_size.Y * 0.7f) {
if (m_profiler_max_page < 5) {
m_profiler_max_page++;
updateProfiler(); // do it again
}
}
}
void GameUI::toggleChat(Client *client)

View File

@@ -102,5 +102,5 @@ private:
gui::IGUIStaticText *m_guitext_profiler = nullptr; // Profiler text
u8 m_profiler_current_page = 0;
const u8 m_profiler_max_page = 3;
u8 m_profiler_max_page = 1;
};

View File

@@ -7,6 +7,7 @@
#include "profilergraph.h"
#include "IVideoDriver.h"
#include "util/string.h"
#include "util/basic_macros.h"
void ProfilerGraph::put(const Profiler::GraphValues &values)
{
@@ -34,35 +35,33 @@ void ProfilerGraph::draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
continue;
}
if (value < j->second.min)
j->second.min = value;
if (value > j->second.max)
j->second.max = value;
j->second.min = std::min(j->second.min, value);
j->second.max = std::max(j->second.max, value);
}
}
if (m_meta.empty())
return;
// Assign colors
static const video::SColor usable_colors[] = {video::SColor(255, 255, 100, 100),
video::SColor(255, 90, 225, 90),
video::SColor(255, 100, 100, 255),
video::SColor(255, 255, 150, 50),
video::SColor(255, 220, 220, 100)};
static const u32 usable_colors_count =
sizeof(usable_colors) / sizeof(*usable_colors);
static const video::SColor usable_colors[] = {
0xffc5000b, 0xffff950e, 0xffaecf00, 0xffffd320,
0xffff420e, 0xffff8080, 0xff729fcf, 0xffff99cc,
};
u32 next_color_i = 0;
for (auto &i : m_meta) {
Meta &meta = i.second;
video::SColor color(255, 200, 200, 200);
if (next_color_i < usable_colors_count)
if (next_color_i < ARRLEN(usable_colors))
color = usable_colors[next_color_i++];
meta.color = color;
}
s32 graphh = 50;
const s32 texth = font->getDimension(L"Ay").Height;
const s32 graphh = 52;
s32 textx = x_left + m_log_max_size + 15;
s32 textx2 = textx + 200 - 15;
s32 meta_i = 0;
@@ -71,23 +70,22 @@ void ProfilerGraph::draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
const std::string &id = p.first;
const Meta &meta = p.second;
s32 x = x_left;
s32 y = y_bottom - meta_i * 50;
s32 y = y_bottom - meta_i * graphh;
float show_min = meta.min;
float show_max = meta.max;
if (show_min >= -0.0001 && show_max >= -0.0001) {
if (show_min <= show_max * 0.5)
if (show_min >= -0.0001f && show_max >= -0.0001f) {
if (show_min <= show_max * 0.5f || show_max <= graphh)
show_min = 0;
}
const s32 texth = 15;
char buf[20];
if (floorf(show_max) == show_max)
porting::mt_snprintf(buf, sizeof(buf), "%.5g", show_max);
else
porting::mt_snprintf(buf, sizeof(buf), "%.3g", show_max);
font->draw(utf8_to_wide(buf).c_str(),
core::rect<s32>(textx, y - graphh, textx2,
core::recti(textx, y - graphh, textx2,
y - graphh + texth),
meta.color);
@@ -96,23 +94,23 @@ void ProfilerGraph::draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
else
porting::mt_snprintf(buf, sizeof(buf), "%.3g", show_min);
font->draw(utf8_to_wide(buf).c_str(),
core::rect<s32>(textx, y - texth, textx2, y), meta.color);
core::recti(textx, y - texth, textx2, y), meta.color);
font->draw(utf8_to_wide(id).c_str(),
core::rect<s32>(textx, y - graphh / 2 - texth / 2, textx2,
core::recti(textx, y - graphh / 2 - texth / 2, textx2,
y - graphh / 2 + texth / 2),
meta.color);
s32 graph1y = y;
s32 graph1h = graphh;
bool relativegraph = (show_min != 0 && show_min != show_max);
float lastscaledvalue = 0.0;
float lastscaledvalue = 0;
bool lastscaledvalue_exists = false;
for (const Piece &piece : m_log) {
float value = 0;
bool value_exists = false;
Profiler::GraphValues::const_iterator k = piece.values.find(id);
auto k = piece.values.find(id);
if (k != piece.values.end()) {
value = k->second;
@@ -125,12 +123,12 @@ void ProfilerGraph::draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
continue;
}
float scaledvalue = 1.0;
float scaledvalue = 1.0f;
if (show_max != show_min)
scaledvalue = (value - show_min) / (show_max - show_min);
if (scaledvalue == 1.0 && value == 0) {
if (scaledvalue == 1.0f && value == 0) {
x++;
lastscaledvalue_exists = false;
continue;

View File

@@ -18,9 +18,10 @@ namespace video {
class ProfilerGraph
{
private:
// FIXME: this data structure is awfully inefficient
struct Piece
{
Piece(Profiler::GraphValues v) : values(std::move(v)) {}
Piece(const Profiler::GraphValues &v) : values(v) {}
Profiler::GraphValues values;
};
struct Meta

View File

@@ -897,7 +897,7 @@ void Peer::RTTStatistics(float rtt, const std::string &profiler_id,
m_rtt.max_rtt = rtt;
/* do average calculation */
if (m_rtt.avg_rtt < 0.0)
if (m_rtt.avg_rtt < 0)
m_rtt.avg_rtt = rtt;
else
m_rtt.avg_rtt = m_rtt.avg_rtt * (num_samples/(num_samples-1)) +
@@ -919,7 +919,7 @@ void Peer::RTTStatistics(float rtt, const std::string &profiler_id,
if (jitter >= m_rtt.jitter_max)
m_rtt.jitter_max = jitter;
if (m_rtt.jitter_avg < 0.0)
if (m_rtt.jitter_avg < 0)
m_rtt.jitter_avg = jitter;
else
m_rtt.jitter_avg = m_rtt.jitter_avg * (num_samples/(num_samples-1)) +
@@ -1002,7 +1002,7 @@ void UDPPeer::reportRTT(float rtt)
{
if (rtt < 0)
return;
RTTStatistics(rtt,"rudp",MAX_RELIABLE_WINDOW_SIZE*10);
RTTStatistics(rtt, "network", MAX_RELIABLE_WINDOW_SIZE*10);
// use this value to decide the resend timeout
const float rtt_stat = getStat(AVG_RTT);

View File

@@ -220,7 +220,8 @@ void ConnectionSendThread::runTimeouts(float dtime, u32 peer_packet_quota)
resend_timeout, peer_packet_quota);
channel.UpdatePacketLossCounter(timed_outs.size());
g_profiler->graphAdd("packets_lost", timed_outs.size());
if (timed_outs.size() > 0)
g_profiler->graphAdd("packets_lost", timed_outs.size());
// Note that this only happens during connection setup, it would
// break badly otherwise.

View File

@@ -11,6 +11,42 @@
static Profiler main_profiler;
Profiler *g_profiler = &main_profiler;
/*
Splits a list into "pages". For example, the list [1,2,3,4,5] split
into two pages would be [1,2,3],[4,5]. This function computes the
minimum and maximum indices of a single page.
length: Length of the list that should be split
page: Page number, 1 <= page <= pagecount
pagecount: The number of pages, >= 1
minindex: Receives the minimum index (inclusive).
maxindex: Receives the maximum index (exclusive).
Ensures 0 <= minindex <= maxindex <= length.
*/
static void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxindex)
{
if (length < 1 || pagecount < 1 || page < 1 || page > pagecount) {
// Special cases or invalid parameters
minindex = maxindex = 0;
} else if(pagecount <= length) {
// Less pages than entries in the list:
// Each page contains at least one entry
minindex = (length * (page-1) + (pagecount-1)) / pagecount;
maxindex = (length * page + (pagecount-1)) / pagecount;
} else {
// More pages than entries in the list:
// Make sure the empty pages are at the end
if (page < length) {
minindex = page-1;
maxindex = page;
} else {
minindex = 0;
maxindex = 0;
}
}
}
ScopeProfiler::ScopeProfiler(Profiler *profiler, const std::string &name,
ScopeProfilerType type, TimePrecision prec) :
m_profiler(profiler),
@@ -137,14 +173,15 @@ int Profiler::print(std::ostream &o, u32 page, u32 pagecount)
{
// Padding
s32 space = std::max(0, 46 - (s32)i.first.size());
s32 space = std::max(0, 48 - (s32)i.first.size());
memset(buffer, '_', space);
buffer[space] = '\0';
o << buffer;
}
porting::mt_snprintf(buffer, sizeof(buffer), "% 5ix % 7g",
getAvgCount(i.first), floor(i.second * 1000.0) / 1000.0);
float rounded = floorf(i.second * 1000.0f) / 1000.0f;
porting::mt_snprintf(buffer, sizeof(buffer), "% 5ix % 8g",
getAvgCount(i.first), rounded);
o << buffer << std::endl;
}
return values.size();

View File

@@ -136,7 +136,6 @@ void *ServerThread::run()
while (!stopRequested()) {
framemarker.start();
ScopeProfiler spm(g_profiler, "Server::RunStep() (max)", SPT_MAX);
u64 t0 = porting::getTimeUs();

View File

@@ -407,42 +407,6 @@ private:
};
/*
Splits a list into "pages". For example, the list [1,2,3,4,5] split
into two pages would be [1,2,3],[4,5]. This function computes the
minimum and maximum indices of a single page.
length: Length of the list that should be split
page: Page number, 1 <= page <= pagecount
pagecount: The number of pages, >= 1
minindex: Receives the minimum index (inclusive).
maxindex: Receives the maximum index (exclusive).
Ensures 0 <= minindex <= maxindex <= length.
*/
inline void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxindex)
{
if (length < 1 || pagecount < 1 || page < 1 || page > pagecount) {
// Special cases or invalid parameters
minindex = maxindex = 0;
} else if(pagecount <= length) {
// Less pages than entries in the list:
// Each page contains at least one entry
minindex = (length * (page-1) + (pagecount-1)) / pagecount;
maxindex = (length * page + (pagecount-1)) / pagecount;
} else {
// More pages than entries in the list:
// Make sure the empty pages are at the end
if (page < length) {
minindex = page-1;
maxindex = page;
} else {
minindex = 0;
maxindex = 0;
}
}
}
constexpr inline bool is_power_of_two(u32 n)
{
return n != 0 && (n & (n - 1)) == 0;