overlays: Performance Overlay

This commit is contained in:
VelocityRa 2018-05-30 00:38:21 +03:00 committed by kd-11
parent c2e17d04e1
commit c8d8a81ccd
7 changed files with 330 additions and 21 deletions

View file

@ -0,0 +1,233 @@
#include "stdafx.h"
#include "overlays.h"
#include "../GSRender.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/RawSPUThread.h"
#include "Emu/Cell/PPUThread.h"
#include "Utilities/sysinfo.h"
namespace rsx
{
namespace overlays
{
void perf_metrics_overlay::reset_body()
{
m_body.set_font("n023055ms.ttf", m_font_size);
m_body.set_pos(50, 50);
m_body.fore_color = {0xFF / 255.f, 0xe1 / 255.f, 0x38 / 255.f, 1.0f};
m_body.back_color = {0x00 / 255.f, 0x23 / 255.f, 0x39 / 255.f, 0.7f};
m_body.padding_top = 0;
m_body.padding_bottom = -14;
m_body.set_margin(5, 5, 0, 0);
}
void perf_metrics_overlay::reset_titles()
{
m_titles.set_font("n023055ms.ttf", m_font_size);
m_titles.set_pos(50, 50);
m_titles.fore_color = {0xf2 / 256.0, 0x6C / 256.0, 0x24 / 256.0, 1.0f};
m_titles.back_color = {0.0f, 0.0f, 0.0f, 0.0f};
m_titles.padding_top = 0;
m_titles.padding_bottom = -14;
m_titles.set_margin(5, 5, 0, 0);
switch (m_detail)
{
case detail_level::minimal:
case detail_level::low: m_titles.text = ""; break;
case detail_level::medium: m_titles.text = fmt::format("\n\n%s", title1_medium); break;
case detail_level::high: m_titles.text = fmt::format("\n\n%s\n\n\n\n\n\n%s", title1_high, title2); break;
}
m_titles.auto_resize();
m_titles.refresh();
}
void perf_metrics_overlay::init()
{
reset_titles();
reset_body();
force_next_update();
update();
m_update_timer.Start();
}
perf_metrics_overlay::perf_metrics_overlay(bool initialize)
{
// Default values, will change based on config options
m_update_interval = 350;
m_detail = detail_level::high;
m_font_size = 10;
if (initialize)
init();
}
// In ms
void perf_metrics_overlay::set_update_interval(u32 update_interval)
{
m_update_interval = update_interval;
}
void perf_metrics_overlay::set_detail_level(detail_level level)
{
m_detail = level;
reset_titles();
}
void perf_metrics_overlay::set_font_size(u32 font_size)
{
m_font_size = font_size;
reset_titles();
reset_body();
}
void perf_metrics_overlay::force_next_update()
{
m_force_update = true;
}
void perf_metrics_overlay::update()
{
const auto elapsed = m_update_timer.GetElapsedTimeInMilliSec();
if (!m_force_update)
{
++m_frames;
}
if (elapsed >= m_update_interval || m_force_update)
{
f32 fps{0};
f32 frametime{0};
u64 ppu_cycles{0};
u64 spu_cycles{0};
u64 rsx_cycles{0};
u64 total_cycles{0};
u32 ppus{0};
u32 spus{0};
u32 rawspus{0};
f32 cpu_usage{-1.f};
u32 total_threads{0};
f32 ppu_usage{0};
f32 spu_usage{0};
f32 rsx_usage{0};
u32 rsx_load{0};
std::shared_ptr<GSRender> rsx_thread;
std::string perf_text;
// 1. Fetch/calculate metrics we'll need
switch (m_detail)
{
case detail_level::high:
{
frametime = m_force_update ? 0 : std::max(0.0, elapsed / m_frames);
rsx_thread = fxm::get<GSRender>();
rsx_load = rsx_thread->get_load();
total_threads = CPUStats::get_thread_count();
// fallthrough
}
case detail_level::medium:
{
ppus = idm::select<ppu_thread>([&ppu_cycles](u32, ppu_thread& ppu) { ppu_cycles += ppu.get()->get_cycles(); });
spus = idm::select<SPUThread>([&spu_cycles](u32, SPUThread& spu) { spu_cycles += spu.get()->get_cycles(); });
rawspus = idm::select<RawSPUThread>([&spu_cycles](u32, RawSPUThread& rawspu) { spu_cycles += rawspu.get()->get_cycles(); });
if (!rsx_thread)
rsx_thread = fxm::get<GSRender>();
rsx_cycles += rsx_thread->get()->get_cycles();
total_cycles = ppu_cycles + spu_cycles + rsx_cycles;
cpu_usage = m_cpu_stats.get_usage();
ppu_usage = std::clamp(cpu_usage * ppu_cycles / total_cycles, 0.f, 100.f);
spu_usage = std::clamp(cpu_usage * spu_cycles / total_cycles, 0.f, 100.f);
rsx_usage = std::clamp(cpu_usage * rsx_cycles / total_cycles, 0.f, 100.f);
// fallthrough
}
case detail_level::low:
{
if (cpu_usage == -1.f)
cpu_usage = m_cpu_stats.get_usage();
// fallthrough
}
case detail_level::minimal:
{
fps = m_force_update ? 0 : std::max(0.0, static_cast<f32>(m_frames) / (elapsed / 1000));
}
}
// 2. Format output string
switch (m_detail)
{
case detail_level::minimal:
{
perf_text += fmt::format("FPS : %05.2f", fps);
break;
}
case detail_level::low:
{
perf_text += fmt::format("FPS : %05.2f\n"
"CPU : %04.1f %%",
fps, cpu_usage);
break;
}
case detail_level::medium:
{
perf_text += fmt::format("FPS : %05.2f\n\n"
"%s\n"
" PPU : %04.1f %%\n"
" SPU : %04.1f %%\n"
" RSX : %04.1f %%\n"
" Total : %04.1f %%",
fps, std::string(title1_medium.size(), ' '), ppu_usage, spu_usage, rsx_usage, cpu_usage, std::string(title2.size(), ' '));
break;
}
case detail_level::high:
{
perf_text += fmt::format("FPS : %05.2f (%03.1fms)\n\n"
"%s\n"
" PPU : %04.1f %% (%2u)\n"
" SPU : %04.1f %% (%2u)\n"
" RSX : %04.1f %% ( 1)\n"
" Total : %04.1f %% (%2u)\n\n"
"%s\n"
" RSX : %02u %%",
fps, frametime, std::string(title1_high.size(), ' '), ppu_usage, ppus, spu_usage, spus + rawspus, rsx_usage, cpu_usage, total_threads, std::string(title2.size(), ' '), rsx_load);
break;
}
}
m_body.text = perf_text;
m_body.auto_resize();
m_body.refresh();
if (!m_force_update)
{
m_frames = 0;
m_update_timer.Start();
}
else
{
// Only force once
m_force_update = false;
}
}
}
} // namespace overlays
} // namespace rsx

View file

@ -11,6 +11,8 @@
#include "Emu/Cell/Modules/cellSaveData.h"
#include "Emu/Cell/Modules/cellMsgDialog.h"
#include "Emu/Cell/Modules/sceNpTrophy.h"
#include "Utilities/CPUStats.h"
#include "Utilities/Timer.h"
#include <time.h>
@ -32,10 +34,10 @@ namespace rsx
virtual ~overlay() = default;
void refresh();
virtual void update() {}
virtual compiled_resource get_compiled() = 0;
void refresh();
};
// Interactable UI element
@ -67,14 +69,15 @@ namespace rsx
s32 return_code = CELL_OK;
std::function<void(s32 status)> on_close;
void close();
virtual void update() override {}
virtual compiled_resource get_compiled() override = 0;
virtual void on_button_pressed(pad_button /*button_press*/)
{
close();
};
}
void close();
s32 run_input_loop()
{
@ -345,7 +348,7 @@ namespace rsx
m_dirty_list.erase
(
std::remove_if(m_dirty_list.begin(), m_dirty_list.end(), [&uids](std::unique_ptr<user_interface>& e)
std::remove_if(m_dirty_list.begin(), m_dirty_list.end(), [&uids](std::unique_ptr<overlay>& e)
{
return std::find(uids.begin(), uids.end(), e->uid) != uids.end();
})
@ -403,27 +406,50 @@ namespace rsx
}
};
struct fps_display : user_interface
struct perf_metrics_overlay : overlay
{
label m_display;
private:
/*
minimal - fps
low - fps, total cpu usage
medium - fps, detailed cpu usage
high - fps, frametime, detailed cpu usage, thread number, rsx load
*/
detail_level m_detail;
fps_display()
{
m_display.w = 150;
m_display.h = 30;
m_display.set_font("Arial", 16);
m_display.set_pos(1100, 20);
}
label m_body;
label m_titles;
void update(std::string current_fps)
{
m_display.text = current_fps;
m_display.refresh();
}
CPUStats m_cpu_stats;
Timer m_update_timer;
u32 m_update_interval; // in ms
u32 m_frames{ 0 };
u32 m_font_size;
bool m_force_update;
const std::string title1_medium{"CPU Utilization:"};
const std::string title1_high{"Host Utilization (CPU):"};
const std::string title2{"Guest Utilization (PS3):"};
void reset_body();
void reset_titles();
public:
perf_metrics_overlay(bool initialize = true);
void init();
void set_detail_level(detail_level level);
void set_update_interval(u32 update_interval);
void set_font_size(u32 font_size);
void force_next_update();
void update() override;
compiled_resource get_compiled() override
{
return m_display.get_compiled();
m_body.get_compiled().add(m_titles.get_compiled());
return m_body.get_compiled();
}
};

View file

@ -355,6 +355,16 @@ namespace rsx
if (supports_native_ui)
{
m_overlay_manager = fxm::make_always<rsx::overlays::display_manager>();
if (g_cfg.video.perf_overlay.perf_overlay_enabled)
{
auto perf_overlay = m_overlay_manager->create<rsx::overlays::perf_metrics_overlay>(false);
perf_overlay->set_detail_level(g_cfg.video.perf_overlay.level);
perf_overlay->set_update_interval(g_cfg.video.perf_overlay.update_interval);
perf_overlay->set_font_size(g_cfg.video.perf_overlay.font_size);
perf_overlay->init();
}
}
on_init_thread();

View file

@ -198,6 +198,23 @@ void fmt_class_string<audio_renderer>::format(std::string& out, u64 arg)
});
}
template <>
inline void fmt_class_string<detail_level>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](detail_level value)
{
switch (value)
{
case detail_level::minimal: return "Minimal";
case detail_level::low: return "Low";
case detail_level::medium: return "Medium";
case detail_level::high: return "High";
}
return unknown;
});
}
void Emulator::Init()
{
if (!g_tty)

View file

@ -147,6 +147,14 @@ enum class frame_limit_type
_auto,
};
enum class detail_level
{
minimal,
low,
medium,
high,
};
enum CellNetCtlState : s32;
enum CellSysutilLang : s32;
@ -393,6 +401,17 @@ struct cfg_root : cfg::node
} vk{this};
struct node_perf_overlay : cfg::node
{
node_perf_overlay(cfg::node* _this) : cfg::node(_this, "Perfomance Overlay") {}
cfg::_bool perf_overlay_enabled{this, "Enabled", false};
cfg::_enum<detail_level> level{this, "Detail level", detail_level::high};
cfg::_int<30, 5000> update_interval{this, "Metrics update interval (ms)", 350};
cfg::_int<4, 36> font_size{this, "Font size (px)", 10};
} perf_overlay{this};
} video{this};
struct node_audio : cfg::node

View file

@ -295,6 +295,7 @@
</ClCompile>
<ClCompile Include="Emu\RSX\Null\NullGSRender.cpp" />
<ClCompile Include="Emu\RSX\Overlays\overlays.cpp" />
<ClCompile Include="Emu\RSX\Overlays\overlay_perf_metrics.cpp" />
<ClCompile Include="Emu\RSX\rsx_methods.cpp" />
<ClCompile Include="Emu\RSX\rsx_utils.cpp" />
<ClCompile Include="Crypto\aes.cpp">

View file

@ -749,6 +749,9 @@
<ClCompile Include="Emu\RSX\Overlays\overlays.cpp">
<Filter>Emu\GPU\RSX\Overlays</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\Overlays\overlay_perf_metrics.cpp">
<Filter>Emu\GPU\RSX\Overlays</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">