mirror of https://github.com/PCSX2/pcsx2.git
ImGuiOverlays: Add frame time graph
This commit is contained in:
parent
bb7ab5690c
commit
4bf6b1df5e
|
@ -144,6 +144,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowIndicators, "EmuCore/GS", "OsdShowIndicators", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowSettings, "EmuCore/GS", "OsdShowSettings", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowInputs, "EmuCore/GS", "OsdShowInputs", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowFrameTimes, "EmuCore/GS", "OsdShowFrameTimes", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.warnAboutUnsafeSettings, "EmuCore", "WarnAboutUnsafeSettings", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fxaa, "EmuCore/GS", "fxaa", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.shadeBoost, "EmuCore/GS", "ShadeBoost", false);
|
||||
|
|
|
@ -1552,13 +1552,20 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="warnAboutUnsafeSettings">
|
||||
<property name="text">
|
||||
<string>Warn About Unsafe Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="osdShowFrameTimes">
|
||||
<property name="text">
|
||||
<string>Show Frame Times</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -508,7 +508,8 @@ struct Pcsx2Config
|
|||
OsdShowGSStats : 1,
|
||||
OsdShowIndicators : 1,
|
||||
OsdShowSettings : 1,
|
||||
OsdShowInputs : 1;
|
||||
OsdShowInputs : 1,
|
||||
OsdShowFrameTimes : 1;
|
||||
|
||||
bool
|
||||
HWSpinGPUForReadbacks : 1,
|
||||
|
|
|
@ -2495,6 +2495,8 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
|||
"EmuCore/GS", "OsdShowSettings", false);
|
||||
DrawToggleSetting(bsi, ICON_FA_GAMEPAD " Show Inputs",
|
||||
"Shows the current controller state of the system in the bottom-left corner of the display.", "EmuCore/GS", "OsdShowInputs", false);
|
||||
DrawToggleSetting(bsi, ICON_FA_RULER_HORIZONTAL " Show Frame Times",
|
||||
"Shows a visual history of frame times in the upper-left corner of the display.", "EmuCore/GS", "OsdShowFrameTimes", false);
|
||||
DrawToggleSetting(bsi, ICON_FA_EXCLAMATION_CIRCLE " Warn About Unsafe Settings",
|
||||
"Displays warnings when settings are enabled which may break games.", "EmuCore", "WarnAboutUnsafeSettings", true);
|
||||
|
||||
|
|
|
@ -15,14 +15,18 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "gsl/span"
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include "common/Align.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
#include "imgui.h"
|
||||
|
@ -33,6 +37,7 @@
|
|||
#include "Frontend/ImGuiOverlays.h"
|
||||
#include "GS.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/GSVector.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
|
@ -60,6 +65,32 @@ namespace ImGuiManager
|
|||
#endif
|
||||
} // namespace ImGuiManager
|
||||
|
||||
static std::tuple<float, float> GetMinMax(gsl::span<const float> values)
|
||||
{
|
||||
GSVector4 vmin(GSVector4::load<false>(values.data()));
|
||||
GSVector4 vmax(vmin);
|
||||
|
||||
const u32 count = static_cast<u32>(values.size());
|
||||
const u32 aligned_count = Common::AlignDownPow2(count, 4);
|
||||
u32 i = 4;
|
||||
for (; i < aligned_count; i += 4)
|
||||
{
|
||||
const GSVector4 v(GSVector4::load<false>(&values[i]));
|
||||
vmin = vmin.min(v);
|
||||
vmax = vmax.max(v);
|
||||
}
|
||||
|
||||
float min = std::min(vmin.x, std::min(vmin.y, std::min(vmin.z, vmin.w)));
|
||||
float max = std::max(vmax.x, std::max(vmax.y, std::max(vmax.z, vmax.w)));
|
||||
for (; i < count; i++)
|
||||
{
|
||||
min = std::min(min, values[i]);
|
||||
max = std::max(max, values[i]);
|
||||
}
|
||||
|
||||
return std::tie(min, max);
|
||||
}
|
||||
|
||||
void ImGuiManager::FormatProcessorStat(std::string& text, double usage, double time)
|
||||
{
|
||||
// Some values, such as GPU (and even CPU to some extent) can be out of phase with the wall clock,
|
||||
|
@ -224,6 +255,69 @@ void ImGuiManager::DrawPerformanceOverlay()
|
|||
DRAW_LINE(standard_font, is_slowmo ? ICON_FA_FORWARD : ICON_FA_FAST_FORWARD, IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
if (GSConfig.OsdShowFrameTimes)
|
||||
{
|
||||
const ImVec2 history_size(200.0f * scale, 50.0f * scale);
|
||||
ImGui::SetNextWindowSize(ImVec2(history_size.x, history_size.y));
|
||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - margin - history_size.x, position_y));
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.25f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotLines, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
|
||||
ImGui::PushFont(fixed_font);
|
||||
if (ImGui::Begin("##frame_times", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs))
|
||||
{
|
||||
auto [min, max] = GetMinMax(PerformanceMetrics::GetFrameTimeHistory());
|
||||
|
||||
// add a little bit of space either side, so we're not constantly resizing
|
||||
if ((max - min) < 4.0f)
|
||||
{
|
||||
min = min - std::fmod(min, 1.0f);
|
||||
max = max - std::fmod(max, 1.0f) + 1.0f;
|
||||
min = std::max(min - 2.0f, 0.0f);
|
||||
max += 2.0f;
|
||||
}
|
||||
|
||||
ImGui::PlotEx(
|
||||
ImGuiPlotType_Lines, "##frame_times",
|
||||
[](void*, int idx) -> float {
|
||||
return PerformanceMetrics::GetFrameTimeHistory()[(
|
||||
(PerformanceMetrics::GetFrameTimeHistoryPos() + idx) % PerformanceMetrics::NUM_FRAME_TIME_SAMPLES)];
|
||||
},
|
||||
nullptr, PerformanceMetrics::NUM_FRAME_TIME_SAMPLES, 0, nullptr, min, max, history_size);
|
||||
|
||||
ImDrawList* win_dl = ImGui::GetCurrentWindow()->DrawList;
|
||||
const ImVec2 wpos(ImGui::GetCurrentWindow()->Pos);
|
||||
|
||||
text.clear();
|
||||
fmt::format_to(std::back_inserter(text), "{:.1f} ms", max);
|
||||
text_size = fixed_font->CalcTextSizeA(fixed_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.c_str() + text.length());
|
||||
win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing + shadow_offset, wpos.y + shadow_offset),
|
||||
IM_COL32(0, 0, 0, 100), text.c_str(), text.c_str() + text.length());
|
||||
win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing, wpos.y), IM_COL32(255, 255, 255, 255), text.c_str(),
|
||||
text.c_str() + text.length());
|
||||
|
||||
text.clear();
|
||||
fmt::format_to(std::back_inserter(text), "{:.1f} ms", min);
|
||||
text_size = fixed_font->CalcTextSizeA(fixed_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.c_str() + text.length());
|
||||
win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing + shadow_offset,
|
||||
wpos.y + history_size.y - fixed_font->FontSize + shadow_offset),
|
||||
IM_COL32(0, 0, 0, 100), text.c_str(), text.c_str() + text.length());
|
||||
win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing, wpos.y + history_size.y - fixed_font->FontSize),
|
||||
IM_COL32(255, 255, 255, 255), text.c_str(), text.c_str() + text.length());
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleVar(5);
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (!fsui_active)
|
||||
{
|
||||
|
|
|
@ -605,7 +605,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
if (Host::BeginPresentFrame(true))
|
||||
Host::EndPresentFrame();
|
||||
g_gs_device->RestoreAPIState();
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame);
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -655,7 +655,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
PerformanceMetrics::OnGPUPresent(g_host_display->GetAndResetAccumulatedGPUTime());
|
||||
}
|
||||
g_gs_device->RestoreAPIState();
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame);
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame, false);
|
||||
|
||||
// snapshot
|
||||
// wx is dumb and call this from the UI thread...
|
||||
|
|
|
@ -321,6 +321,7 @@ Pcsx2Config::GSOptions::GSOptions()
|
|||
OsdShowIndicators = true;
|
||||
OsdShowSettings = false;
|
||||
OsdShowInputs = false;
|
||||
OsdShowFrameTimes = false;
|
||||
|
||||
HWDownloadMode = GSHardwareDownloadMode::Enabled;
|
||||
HWSpinGPUForReadbacks = false;
|
||||
|
@ -544,6 +545,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
|
|||
GSSettingBool(OsdShowIndicators);
|
||||
GSSettingBool(OsdShowSettings);
|
||||
GSSettingBool(OsdShowInputs);
|
||||
GSSettingBool(OsdShowFrameTimes);
|
||||
|
||||
GSSettingBool(HWSpinGPUForReadbacks);
|
||||
GSSettingBool(HWSpinCPUForReadbacks);
|
||||
|
|
|
@ -41,6 +41,7 @@ static float s_average_frame_time = 0.0f;
|
|||
static float s_average_frame_time_accumulator = 0.0f;
|
||||
static float s_worst_frame_time_accumulator = 0.0f;
|
||||
static u32 s_frames_since_last_update = 0;
|
||||
static u32 s_unskipped_frames_since_last_update = 0;
|
||||
static Common::Timer s_last_update_time;
|
||||
static Common::Timer s_last_frame_time;
|
||||
|
||||
|
@ -65,6 +66,9 @@ static float s_gs_thread_time = 0.0f;
|
|||
static float s_vu_thread_usage = 0.0f;
|
||||
static float s_vu_thread_time = 0.0f;
|
||||
|
||||
static PerformanceMetrics::FrameTimeHistory s_frame_time_history;
|
||||
static u32 s_frame_time_history_pos = 0;
|
||||
|
||||
struct GSSWThreadStats
|
||||
{
|
||||
Threading::ThreadHandle handle;
|
||||
|
@ -100,11 +104,15 @@ void PerformanceMetrics::Clear()
|
|||
s_gpu_usage = 0.0f;
|
||||
|
||||
s_frame_number = 0;
|
||||
|
||||
s_frame_time_history.fill(0.0f);
|
||||
s_frame_time_history_pos = 0;
|
||||
}
|
||||
|
||||
void PerformanceMetrics::Reset()
|
||||
{
|
||||
s_frames_since_last_update = 0;
|
||||
s_unskipped_frames_since_last_update = 0;
|
||||
s_gs_framebuffer_blits_since_last_update = 0;
|
||||
s_gs_privileged_register_writes_since_last_update = 0;
|
||||
s_average_frame_time_accumulator = 0.0f;
|
||||
|
@ -125,11 +133,18 @@ void PerformanceMetrics::Reset()
|
|||
stat.last_cpu_time = stat.handle.GetCPUTime();
|
||||
}
|
||||
|
||||
void PerformanceMetrics::Update(bool gs_register_write, bool fb_blit)
|
||||
void PerformanceMetrics::Update(bool gs_register_write, bool fb_blit, bool is_skipping_present)
|
||||
{
|
||||
const float frame_time = s_last_frame_time.GetTimeMillisecondsAndReset();
|
||||
s_average_frame_time_accumulator += frame_time;
|
||||
s_worst_frame_time_accumulator = std::max(s_worst_frame_time_accumulator, frame_time);
|
||||
if (!is_skipping_present)
|
||||
{
|
||||
const float frame_time = s_last_frame_time.GetTimeMillisecondsAndReset();
|
||||
s_average_frame_time_accumulator += frame_time;
|
||||
s_worst_frame_time_accumulator = std::max(s_worst_frame_time_accumulator, frame_time);
|
||||
s_frame_time_history[s_frame_time_history_pos] = frame_time;
|
||||
s_frame_time_history_pos = (s_frame_time_history_pos + 1) % NUM_FRAME_TIME_SAMPLES;
|
||||
s_unskipped_frames_since_last_update++;
|
||||
}
|
||||
|
||||
s_frames_since_last_update++;
|
||||
s_gs_privileged_register_writes_since_last_update += static_cast<u32>(gs_register_write);
|
||||
s_gs_framebuffer_blits_since_last_update += static_cast<u32>(fb_blit);
|
||||
|
@ -144,10 +159,10 @@ void PerformanceMetrics::Update(bool gs_register_write, bool fb_blit)
|
|||
s_last_update_time.ResetTo(now_ticks);
|
||||
s_worst_frame_time = s_worst_frame_time_accumulator;
|
||||
s_worst_frame_time_accumulator = 0.0f;
|
||||
s_average_frame_time = s_average_frame_time_accumulator / static_cast<float>(s_frames_since_last_update);
|
||||
s_average_frame_time = s_average_frame_time_accumulator / static_cast<float>(s_unskipped_frames_since_last_update);
|
||||
s_average_frame_time_accumulator = 0.0f;
|
||||
s_fps = static_cast<float>(s_frames_since_last_update) / time;
|
||||
s_average_gpu_time = s_accumulated_gpu_time / static_cast<float>(s_frames_since_last_update);
|
||||
s_average_gpu_time = s_accumulated_gpu_time / static_cast<float>(s_unskipped_frames_since_last_update);
|
||||
s_gpu_usage = s_accumulated_gpu_time / (time * 10.0f);
|
||||
s_accumulated_gpu_time = 0.0f;
|
||||
|
||||
|
@ -209,6 +224,7 @@ void PerformanceMetrics::Update(bool gs_register_write, bool fb_blit)
|
|||
}
|
||||
|
||||
s_frames_since_last_update = 0;
|
||||
s_unskipped_frames_since_last_update = 0;
|
||||
s_presents_since_last_update = 0;
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
|
@ -339,3 +355,13 @@ float PerformanceMetrics::GetGPUAverageTime()
|
|||
{
|
||||
return s_average_gpu_time;
|
||||
}
|
||||
|
||||
const PerformanceMetrics::FrameTimeHistory& PerformanceMetrics::GetFrameTimeHistory()
|
||||
{
|
||||
return s_frame_time_history;
|
||||
}
|
||||
|
||||
u32 PerformanceMetrics::GetFrameTimeHistoryPos()
|
||||
{
|
||||
return s_frame_time_history_pos;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/Threading.h"
|
||||
|
||||
namespace PerformanceMetrics
|
||||
|
@ -25,9 +27,12 @@ namespace PerformanceMetrics
|
|||
DISPFBBlit
|
||||
};
|
||||
|
||||
static constexpr u32 NUM_FRAME_TIME_SAMPLES = 150;
|
||||
using FrameTimeHistory = std::array<float, NUM_FRAME_TIME_SAMPLES>;
|
||||
|
||||
void Clear();
|
||||
void Reset();
|
||||
void Update(bool gs_register_write, bool fb_blit);
|
||||
void Update(bool gs_register_write, bool fb_blit, bool is_skipping_present);
|
||||
void OnGPUPresent(float gpu_time);
|
||||
|
||||
/// Sets the EE thread for CPU usage calculations.
|
||||
|
@ -64,4 +69,7 @@ namespace PerformanceMetrics
|
|||
|
||||
float GetGPUUsage();
|
||||
float GetGPUAverageTime();
|
||||
|
||||
const FrameTimeHistory& GetFrameTimeHistory();
|
||||
u32 GetFrameTimeHistoryPos();
|
||||
} // namespace PerformanceMetrics
|
||||
|
|
Loading…
Reference in New Issue