New FrameTime/VBlank Analyzer + Graph
This commit is contained in:
parent
2345ba178d
commit
673f81c18a
|
@ -175,7 +175,10 @@ public enum BooleanSetting implements AbstractBooleanSetting
|
|||
GFX_WIDESCREEN_HACK(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "wideScreenHack", false),
|
||||
GFX_CROP(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "Crop", false),
|
||||
GFX_SHOW_FPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowFPS", false),
|
||||
GFX_SHOW_FTIMES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowFTimes", false),
|
||||
GFX_SHOW_VPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowVPS", false),
|
||||
GFX_SHOW_VTIMES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowVTimes", false),
|
||||
GFX_SHOW_GRAPHS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowGraphs", false),
|
||||
GFX_SHOW_SPEED(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowSpeed", false),
|
||||
GFX_SHOW_SPEED_COLORS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowSpeedColors", true),
|
||||
GFX_OVERLAY_STATS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "OverlayStats", false),
|
||||
|
|
|
@ -770,8 +770,14 @@ public final class SettingsFragmentPresenter
|
|||
R.string.video_backend, 0, R.array.videoBackendEntries, R.array.videoBackendValues));
|
||||
sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_FPS, R.string.show_fps,
|
||||
R.string.show_fps_description));
|
||||
sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_FTIMES, R.string.show_ftimes,
|
||||
R.string.show_ftimes_description));
|
||||
sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_VPS, R.string.show_vps,
|
||||
R.string.show_vps_description));
|
||||
sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_VTIMES, R.string.show_vtimes,
|
||||
R.string.show_vtimes_description));
|
||||
sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_GRAPHS, R.string.show_graphs,
|
||||
R.string.show_graphs_description));
|
||||
sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_SPEED, R.string.show_speed,
|
||||
R.string.show_speed_description));
|
||||
sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_SPEED_COLORS,
|
||||
|
|
|
@ -245,8 +245,14 @@
|
|||
<string name="video_backend_description">Select the API used for graphics rendering.</string>
|
||||
<string name="show_fps">Show FPS</string>
|
||||
<string name="show_fps_description">Shows the number of distinct frames rendered per second as a measure of visual smoothness.</string>
|
||||
<string name="show_ftimes">Show Frame Times</string>
|
||||
<string name="show_ftimes_description">Shows the average time in ms between each distinct rendered frame alongside the standard deviation.</string>
|
||||
<string name="show_vps">Show VPS</string>
|
||||
<string name="show_vps_description">Show the number of frames rendered per second as a measure of emulation speed.</string>
|
||||
<string name="show_vtimes">Show VBlank Times</string>
|
||||
<string name="show_vtimes_description">Shows the average time in ms between each rendered frame alongside the standard deviation.</string>
|
||||
<string name="show_graphs">Show Performance Graphs</string>
|
||||
<string name="show_graphs_description">Shows frametime graph along with statistics as a representation of emulation performance.</string>
|
||||
<string name="show_speed">Show % Speed</string>
|
||||
<string name="show_speed_description">Shows the % speed of emulation compared to full speed.</string>
|
||||
<string name="show_speed_colors">Show Speed Color</string>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "Common/Swap.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "VideoCommon/PerformanceMetrics.h"
|
||||
|
||||
static u32 DPL2QualityToFrameBlockSize(AudioCommon::DPL2Quality quality)
|
||||
{
|
||||
|
@ -160,6 +161,8 @@ unsigned int Mixer::Mix(short* samples, unsigned int num_samples)
|
|||
|
||||
memset(samples, 0, num_samples * 2 * sizeof(short));
|
||||
|
||||
// TODO: Determine how emulation speed will be used in audio
|
||||
// const float emulation_speed = std::roundf(g_perf_metrics.GetSpeed()) / 100.f;
|
||||
const float emulation_speed = m_config_emulation_speed;
|
||||
const int timing_variance = m_config_timing_variance;
|
||||
if (m_config_audio_stretch)
|
||||
|
|
|
@ -48,9 +48,6 @@ public:
|
|||
void StartLogDSPAudio(const std::string& filename);
|
||||
void StopLogDSPAudio();
|
||||
|
||||
float GetCurrentSpeed() const { return m_speed.load(); }
|
||||
void UpdateSpeed(float val) { m_speed.store(val); }
|
||||
|
||||
// 54000000 doesn't work here as it doesn't evenly divide with 32000, but 108000000 does
|
||||
static constexpr u64 FIXED_SAMPLE_RATE_DIVIDEND = 54000000 * 2;
|
||||
|
||||
|
@ -117,9 +114,6 @@ private:
|
|||
bool m_log_dtk_audio = false;
|
||||
bool m_log_dsp_audio = false;
|
||||
|
||||
// Current rate of emulation (1.0 = 100% speed)
|
||||
std::atomic<float> m_speed{0.0f};
|
||||
|
||||
float m_config_emulation_speed;
|
||||
int m_config_timing_variance;
|
||||
bool m_config_audio_stretch;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -26,3 +27,10 @@ using s8 = std::int8_t;
|
|||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
using DT = Clock::duration;
|
||||
using DT_us = std::chrono::duration<double, std::micro>;
|
||||
using DT_ms = std::chrono::duration<double, std::milli>;
|
||||
using DT_s = std::chrono::duration<double, std::ratio<1>>;
|
||||
|
|
|
@ -27,7 +27,10 @@ const Info<bool> GFX_CROP{{System::GFX, "Settings", "Crop"}, false};
|
|||
const Info<int> GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES{
|
||||
{System::GFX, "Settings", "SafeTextureCacheColorSamples"}, 128};
|
||||
const Info<bool> GFX_SHOW_FPS{{System::GFX, "Settings", "ShowFPS"}, false};
|
||||
const Info<bool> GFX_SHOW_FTIMES{{System::GFX, "Settings", "ShowFTimes"}, false};
|
||||
const Info<bool> GFX_SHOW_VPS{{System::GFX, "Settings", "ShowVPS"}, false};
|
||||
const Info<bool> GFX_SHOW_VTIMES{{System::GFX, "Settings", "ShowVTimes"}, false};
|
||||
const Info<bool> GFX_SHOW_GRAPHS{{System::GFX, "Settings", "ShowGraphs"}, false};
|
||||
const Info<bool> GFX_SHOW_SPEED{{System::GFX, "Settings", "ShowSpeed"}, false};
|
||||
const Info<bool> GFX_SHOW_SPEED_COLORS{{System::GFX, "Settings", "ShowSpeedColors"}, true};
|
||||
const Info<int> GFX_PERF_SAMP_WINDOW{{System::GFX, "Settings", "PerfSampWindowMS"}, 1000};
|
||||
|
|
|
@ -30,7 +30,10 @@ extern const Info<AspectMode> GFX_SUGGESTED_ASPECT_RATIO;
|
|||
extern const Info<bool> GFX_CROP;
|
||||
extern const Info<int> GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES;
|
||||
extern const Info<bool> GFX_SHOW_FPS;
|
||||
extern const Info<bool> GFX_SHOW_FTIMES;
|
||||
extern const Info<bool> GFX_SHOW_VPS;
|
||||
extern const Info<bool> GFX_SHOW_VTIMES;
|
||||
extern const Info<bool> GFX_SHOW_GRAPHS;
|
||||
extern const Info<bool> GFX_SHOW_SPEED;
|
||||
extern const Info<bool> GFX_SHOW_SPEED_COLORS;
|
||||
extern const Info<int> GFX_PERF_SAMP_WINDOW;
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
#include "VideoCommon/Fifo.h"
|
||||
#include "VideoCommon/HiresTextures.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/PerformanceMetrics.h"
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
|
||||
|
@ -100,8 +101,6 @@ static bool s_wants_determinism;
|
|||
// Declarations and definitions
|
||||
static Common::Timer s_timer;
|
||||
static u64 s_timer_offset;
|
||||
static std::atomic<u32> s_drawn_frame;
|
||||
static std::atomic<u32> s_drawn_video;
|
||||
|
||||
static bool s_is_stopping = false;
|
||||
static bool s_hardware_initialized = false;
|
||||
|
@ -347,6 +346,9 @@ static void CpuThread(const std::optional<std::string>& savestate_path, bool del
|
|||
// This needs to be delayed until after the video backend is ready.
|
||||
DolphinAnalytics::Instance().ReportGameStart();
|
||||
|
||||
// Clear performance data collected from previous threads.
|
||||
g_perf_metrics.Reset();
|
||||
|
||||
#ifdef ANDROID
|
||||
// For some reason, calling the JNI function AttachCurrentThread from the CPU thread after a
|
||||
// certain point causes a crash if fastmem is enabled. Let's call it early to avoid that problem.
|
||||
|
@ -843,19 +845,15 @@ void RunOnCPUThread(std::function<void()> function, bool wait_for_completion)
|
|||
// This should only be called from VI
|
||||
void VideoThrottle()
|
||||
{
|
||||
g_perf_metrics.CountVBlank();
|
||||
|
||||
// Update info per second
|
||||
u64 elapsed_ms = s_timer.ElapsedMs();
|
||||
if ((elapsed_ms >= 1000 && s_drawn_video.load() > 0) || s_frame_step)
|
||||
if ((elapsed_ms >= 500) || s_frame_step)
|
||||
{
|
||||
s_timer.Start();
|
||||
|
||||
UpdateTitle(elapsed_ms);
|
||||
|
||||
s_drawn_frame.store(0);
|
||||
s_drawn_video.store(0);
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
s_drawn_video++;
|
||||
}
|
||||
|
||||
// --- Callbacks for backends / engine ---
|
||||
|
@ -864,9 +862,9 @@ void VideoThrottle()
|
|||
// frame is presented to the host screen
|
||||
void Callback_FramePresented(double actual_emulation_speed)
|
||||
{
|
||||
s_last_actual_emulation_speed = actual_emulation_speed;
|
||||
g_perf_metrics.CountFrame();
|
||||
|
||||
s_drawn_frame++;
|
||||
s_last_actual_emulation_speed = actual_emulation_speed;
|
||||
s_stop_frame_step.store(true);
|
||||
}
|
||||
|
||||
|
@ -891,15 +889,11 @@ void Callback_NewField()
|
|||
}
|
||||
}
|
||||
|
||||
void UpdateTitle(u64 elapsed_ms)
|
||||
void UpdateTitle()
|
||||
{
|
||||
if (elapsed_ms == 0)
|
||||
elapsed_ms = 1;
|
||||
|
||||
float FPS = (float)(s_drawn_frame.load() * 1000.0 / elapsed_ms);
|
||||
float VPS = (float)(s_drawn_video.load() * 1000.0 / elapsed_ms);
|
||||
float Speed = (float)(s_drawn_video.load() * (100 * 1000.0) /
|
||||
(VideoInterface::GetTargetRefreshRate() * elapsed_ms));
|
||||
float FPS = g_perf_metrics.GetFPS();
|
||||
float VPS = g_perf_metrics.GetVPS();
|
||||
float Speed = g_perf_metrics.GetSpeed();
|
||||
|
||||
// Settings are shown the same for both extended and summary info
|
||||
const std::string SSettings = fmt::format(
|
||||
|
@ -956,15 +950,6 @@ void UpdateTitle(u64 elapsed_ms)
|
|||
message += " | " + title;
|
||||
}
|
||||
|
||||
// Update the audio timestretcher with the current speed
|
||||
auto& system = Core::System::GetInstance();
|
||||
SoundStream* sound_stream = system.GetSoundStream();
|
||||
if (sound_stream)
|
||||
{
|
||||
Mixer* mixer = sound_stream->GetMixer();
|
||||
mixer->UpdateSpeed((float)Speed / 100);
|
||||
}
|
||||
|
||||
Host_UpdateTitle(message);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ void OnFrameEnd();
|
|||
|
||||
void VideoThrottle();
|
||||
|
||||
void UpdateTitle(u64 elapsed_ms);
|
||||
void UpdateTitle();
|
||||
|
||||
// Run a function as the CPU thread.
|
||||
//
|
||||
|
|
|
@ -637,7 +637,6 @@
|
|||
<ClInclude Include="VideoCommon\DataReader.h" />
|
||||
<ClInclude Include="VideoCommon\DriverDetails.h" />
|
||||
<ClInclude Include="VideoCommon\Fifo.h" />
|
||||
<ClInclude Include="VideoCommon\FPSCounter.h" />
|
||||
<ClInclude Include="VideoCommon\FramebufferManager.h" />
|
||||
<ClInclude Include="VideoCommon\FramebufferShaderGen.h" />
|
||||
<ClInclude Include="VideoCommon\FrameDump.h" />
|
||||
|
@ -672,6 +671,8 @@
|
|||
<ClInclude Include="VideoCommon\OnScreenDisplay.h" />
|
||||
<ClInclude Include="VideoCommon\OpcodeDecoding.h" />
|
||||
<ClInclude Include="VideoCommon\PerfQueryBase.h" />
|
||||
<ClInclude Include="VideoCommon\PerformanceMetrics.h" />
|
||||
<ClInclude Include="VideoCommon\PerformanceTracker.h" />
|
||||
<ClInclude Include="VideoCommon\PixelEngine.h" />
|
||||
<ClInclude Include="VideoCommon\PixelShaderGen.h" />
|
||||
<ClInclude Include="VideoCommon\PixelShaderManager.h" />
|
||||
|
@ -1244,7 +1245,6 @@
|
|||
<ClCompile Include="VideoCommon\CPMemory.cpp" />
|
||||
<ClCompile Include="VideoCommon\DriverDetails.cpp" />
|
||||
<ClCompile Include="VideoCommon\Fifo.cpp" />
|
||||
<ClCompile Include="VideoCommon\FPSCounter.cpp" />
|
||||
<ClCompile Include="VideoCommon\FramebufferManager.cpp" />
|
||||
<ClCompile Include="VideoCommon\FramebufferShaderGen.cpp" />
|
||||
<ClCompile Include="VideoCommon\FrameDump.cpp" />
|
||||
|
@ -1272,6 +1272,8 @@
|
|||
<ClCompile Include="VideoCommon\OnScreenDisplay.cpp" />
|
||||
<ClCompile Include="VideoCommon\OpcodeDecoding.cpp" />
|
||||
<ClCompile Include="VideoCommon\PerfQueryBase.cpp" />
|
||||
<ClCompile Include="VideoCommon\PerformanceMetrics.cpp" />
|
||||
<ClCompile Include="VideoCommon\PerformanceTracker.cpp" />
|
||||
<ClCompile Include="VideoCommon\PixelEngine.cpp" />
|
||||
<ClCompile Include="VideoCommon\PixelShaderGen.cpp" />
|
||||
<ClCompile Include="VideoCommon\PixelShaderManager.cpp" />
|
||||
|
|
|
@ -377,6 +377,7 @@ PRIVATE
|
|||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
uicommon
|
||||
imgui
|
||||
implot
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
|
|
|
@ -51,7 +51,10 @@ void AdvancedWidget::CreateWidgets()
|
|||
performance_box->setLayout(performance_layout);
|
||||
|
||||
m_show_fps = new GraphicsBool(tr("Show FPS"), Config::GFX_SHOW_FPS);
|
||||
m_show_ftimes = new GraphicsBool(tr("Show Frame Times"), Config::GFX_SHOW_FTIMES);
|
||||
m_show_vps = new GraphicsBool(tr("Show VPS"), Config::GFX_SHOW_VPS);
|
||||
m_show_vtimes = new GraphicsBool(tr("Show VBlank Times"), Config::GFX_SHOW_VTIMES);
|
||||
m_show_graphs = new GraphicsBool(tr("Show Performance Graphs"), Config::GFX_SHOW_GRAPHS);
|
||||
m_show_speed = new GraphicsBool(tr("Show % Speed"), Config::GFX_SHOW_SPEED);
|
||||
m_show_speed_colors = new GraphicsBool(tr("Show Speed Colors"), Config::GFX_SHOW_SPEED_COLORS);
|
||||
m_perf_samp_window = new GraphicsInteger(0, 10000, Config::GFX_PERF_SAMP_WINDOW, 100);
|
||||
|
@ -59,12 +62,15 @@ void AdvancedWidget::CreateWidgets()
|
|||
new GraphicsBool(tr("Log Render Time to File"), Config::GFX_LOG_RENDER_TIME_TO_FILE);
|
||||
|
||||
performance_layout->addWidget(m_show_fps, 0, 0);
|
||||
performance_layout->addWidget(m_show_ftimes, 0, 1);
|
||||
performance_layout->addWidget(m_show_vps, 1, 0);
|
||||
performance_layout->addWidget(m_show_speed, 0, 1);
|
||||
performance_layout->addWidget(new QLabel(tr("Performance Sample Window (ms):")), 2, 0);
|
||||
performance_layout->addWidget(m_perf_samp_window, 2, 1);
|
||||
performance_layout->addWidget(m_log_render_time, 3, 0);
|
||||
performance_layout->addWidget(m_show_speed_colors, 3, 1);
|
||||
performance_layout->addWidget(m_show_vtimes, 1, 1);
|
||||
performance_layout->addWidget(m_show_speed, 2, 0);
|
||||
performance_layout->addWidget(m_show_graphs, 2, 1);
|
||||
performance_layout->addWidget(new QLabel(tr("Performance Sample Window (ms):")), 3, 0);
|
||||
performance_layout->addWidget(m_perf_samp_window, 3, 1);
|
||||
performance_layout->addWidget(m_log_render_time, 4, 0);
|
||||
performance_layout->addWidget(m_show_speed_colors, 4, 1);
|
||||
|
||||
// Debugging
|
||||
auto* debugging_box = new QGroupBox(tr("Debugging"));
|
||||
|
@ -240,10 +246,22 @@ void AdvancedWidget::AddDescriptions()
|
|||
QT_TR_NOOP("Shows the number of distinct frames rendered per second as a measure of "
|
||||
"visual smoothness.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
static const char TR_SHOW_FTIMES_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the average time in ms between each distinct rendered frame alongside "
|
||||
"the standard deviation.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
static const char TR_SHOW_VPS_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the number of frames rendered per second as a measure of "
|
||||
"emulation speed.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
static const char TR_SHOW_VTIMES_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the average time in ms between each rendered frame alongside "
|
||||
"the standard deviation.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
static const char TR_SHOW_GRAPHS_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows frametime graph along with statistics as a representation of "
|
||||
"emulation performance.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
static const char TR_SHOW_SPEED_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the % speed of emulation compared to full speed."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
|
@ -380,7 +398,10 @@ void AdvancedWidget::AddDescriptions()
|
|||
QT_TR_NOOP("<dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
|
||||
m_show_fps->SetDescription(tr(TR_SHOW_FPS_DESCRIPTION));
|
||||
m_show_ftimes->SetDescription(tr(TR_SHOW_FTIMES_DESCRIPTION));
|
||||
m_show_vps->SetDescription(tr(TR_SHOW_VPS_DESCRIPTION));
|
||||
m_show_vtimes->SetDescription(tr(TR_SHOW_VTIMES_DESCRIPTION));
|
||||
m_show_graphs->SetDescription(tr(TR_SHOW_GRAPHS_DESCRIPTION));
|
||||
m_show_speed->SetDescription(tr(TR_SHOW_SPEED_DESCRIPTION));
|
||||
m_log_render_time->SetDescription(tr(TR_LOG_RENDERTIME_DESCRIPTION));
|
||||
m_show_speed_colors->SetDescription(tr(TR_SHOW_SPEED_COLORS_DESCRIPTION));
|
||||
|
|
|
@ -36,7 +36,10 @@ private:
|
|||
GraphicsBool* m_enable_format_overlay;
|
||||
GraphicsBool* m_enable_api_validation;
|
||||
GraphicsBool* m_show_fps;
|
||||
GraphicsBool* m_show_ftimes;
|
||||
GraphicsBool* m_show_vps;
|
||||
GraphicsBool* m_show_vtimes;
|
||||
GraphicsBool* m_show_graphs;
|
||||
GraphicsBool* m_show_speed;
|
||||
GraphicsBool* m_show_speed_colors;
|
||||
GraphicsInteger* m_perf_samp_window;
|
||||
|
|
|
@ -425,6 +425,7 @@
|
|||
<Import Project="$(ExternalsDir)enet\exports.props" />
|
||||
<Import Project="$(ExternalsDir)fmt\exports.props" />
|
||||
<Import Project="$(ExternalsDir)imgui\exports.props" />
|
||||
<Import Project="$(ExternalsDir)implot\exports.props" />
|
||||
<Import Project="$(ExternalsDir)liblzma\exports.props" />
|
||||
<Import Project="$(ExternalsDir)mbedtls\exports.props" />
|
||||
<Import Project="$(ExternalsDir)mGBA\exports.props" />
|
||||
|
|
|
@ -27,8 +27,6 @@ add_library(videocommon
|
|||
DriverDetails.h
|
||||
Fifo.cpp
|
||||
Fifo.h
|
||||
FPSCounter.cpp
|
||||
FPSCounter.h
|
||||
FramebufferManager.cpp
|
||||
FramebufferManager.h
|
||||
FramebufferShaderGen.cpp
|
||||
|
@ -85,6 +83,10 @@ add_library(videocommon
|
|||
OpcodeDecoding.h
|
||||
PerfQueryBase.cpp
|
||||
PerfQueryBase.h
|
||||
PerformanceMetrics.cpp
|
||||
PerformanceMetrics.h
|
||||
PerformanceTracker.cpp
|
||||
PerformanceTracker.h
|
||||
PixelEngine.cpp
|
||||
PixelEngine.h
|
||||
PixelShaderGen.cpp
|
||||
|
@ -168,6 +170,7 @@ PRIVATE
|
|||
spng
|
||||
xxhash
|
||||
imgui
|
||||
implot
|
||||
glslang
|
||||
)
|
||||
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
// Copyright 2012 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/FPSCounter.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Timer.h"
|
||||
#include "Core/Core.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
static constexpr double US_TO_MS = 1000.0;
|
||||
static constexpr double US_TO_S = 1000000.0;
|
||||
|
||||
static constexpr double FPS_SAMPLE_RC_RATIO = 0.25;
|
||||
|
||||
FPSCounter::FPSCounter(const char* log_name)
|
||||
{
|
||||
m_last_time = Common::Timer::NowUs();
|
||||
m_log_name = log_name;
|
||||
|
||||
m_on_state_changed_handle = Core::AddOnStateChangedCallback([this](Core::State state) {
|
||||
if (state == Core::State::Paused)
|
||||
SetPaused(true);
|
||||
else if (state == Core::State::Running)
|
||||
SetPaused(false);
|
||||
});
|
||||
}
|
||||
|
||||
FPSCounter::~FPSCounter()
|
||||
{
|
||||
Core::RemoveOnStateChangedCallback(&m_on_state_changed_handle);
|
||||
}
|
||||
|
||||
void FPSCounter::LogRenderTimeToFile(s64 val)
|
||||
{
|
||||
if (!m_bench_file.is_open())
|
||||
{
|
||||
File::OpenFStream(m_bench_file, File::GetUserPath(D_LOGS_IDX) + m_log_name, std::ios_base::out);
|
||||
}
|
||||
|
||||
m_bench_file << std::fixed << std::setprecision(8) << (val / US_TO_MS) << std::endl;
|
||||
}
|
||||
|
||||
void FPSCounter::Update()
|
||||
{
|
||||
if (m_paused)
|
||||
return;
|
||||
|
||||
const s64 time = Common::Timer::NowUs();
|
||||
const s64 diff = std::max<s64>(0, time - m_last_time);
|
||||
const s64 window = std::max(1, g_ActiveConfig.iPerfSampleUSec);
|
||||
|
||||
m_raw_dt = diff / US_TO_S;
|
||||
m_last_time = time;
|
||||
|
||||
m_dt_total += diff;
|
||||
m_dt_queue.push_back(diff);
|
||||
|
||||
while (window <= m_dt_total - m_dt_queue.front())
|
||||
{
|
||||
m_dt_total -= m_dt_queue.front();
|
||||
m_dt_queue.pop_front();
|
||||
}
|
||||
|
||||
// This frame count takes into account frames that are partially in the sample window
|
||||
const double fps = (m_dt_queue.size() * US_TO_S) / m_dt_total;
|
||||
const double rc = FPS_SAMPLE_RC_RATIO * std::min(window, m_dt_total) / US_TO_S;
|
||||
const double a = std::max(0.0, 1.0 - std::exp(-m_raw_dt / rc));
|
||||
|
||||
// Sometimes euler averages can break when the average is inf/nan
|
||||
// This small check makes sure that if it does break, it gets fixed
|
||||
if (std::isfinite(m_avg_fps))
|
||||
m_avg_fps += a * (fps - m_avg_fps);
|
||||
else
|
||||
m_avg_fps = fps;
|
||||
|
||||
if (g_ActiveConfig.bLogRenderTimeToFile)
|
||||
LogRenderTimeToFile(diff);
|
||||
}
|
||||
|
||||
void FPSCounter::SetPaused(bool paused)
|
||||
{
|
||||
m_paused = paused;
|
||||
if (m_paused)
|
||||
{
|
||||
m_last_time_pause = Common::Timer::NowUs();
|
||||
}
|
||||
else
|
||||
{
|
||||
const s64 time = Common::Timer::NowUs();
|
||||
const s64 diff = time - m_last_time_pause;
|
||||
m_last_time += diff;
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class FPSCounter
|
||||
{
|
||||
public:
|
||||
explicit FPSCounter(const char* log_name = "log.txt");
|
||||
~FPSCounter();
|
||||
FPSCounter(const FPSCounter&) = delete;
|
||||
FPSCounter& operator=(const FPSCounter&) = delete;
|
||||
FPSCounter(FPSCounter&&) = delete;
|
||||
FPSCounter& operator=(FPSCounter&&) = delete;
|
||||
|
||||
// Called when a frame is rendered (updated every second).
|
||||
void Update();
|
||||
|
||||
double GetFPS() const { return m_avg_fps; }
|
||||
|
||||
double GetDeltaTime() const { return m_raw_dt; }
|
||||
|
||||
private:
|
||||
void LogRenderTimeToFile(s64 val);
|
||||
void SetPaused(bool paused);
|
||||
|
||||
const char* m_log_name;
|
||||
std::ofstream m_bench_file;
|
||||
|
||||
bool m_paused = false;
|
||||
s64 m_last_time = 0;
|
||||
|
||||
double m_avg_fps = 0.0;
|
||||
double m_raw_dt = 0.0;
|
||||
|
||||
s64 m_dt_total = 0;
|
||||
std::deque<s64> m_dt_queue;
|
||||
|
||||
int m_on_state_changed_handle = -1;
|
||||
s64 m_last_time_pause = 0;
|
||||
};
|
|
@ -0,0 +1,221 @@
|
|||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/PerformanceMetrics.h"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <implot.h>
|
||||
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
PerformanceMetrics g_perf_metrics;
|
||||
|
||||
void PerformanceMetrics::Reset()
|
||||
{
|
||||
m_fps_counter.Reset();
|
||||
m_vps_counter.Reset();
|
||||
m_speed_counter.Reset();
|
||||
}
|
||||
|
||||
void PerformanceMetrics::CountFrame()
|
||||
{
|
||||
m_fps_counter.Count();
|
||||
}
|
||||
|
||||
void PerformanceMetrics::CountVBlank()
|
||||
{
|
||||
m_vps_counter.Count();
|
||||
m_speed_counter.Count();
|
||||
}
|
||||
|
||||
double PerformanceMetrics::GetFPS() const
|
||||
{
|
||||
return m_fps_counter.GetHzAvg();
|
||||
}
|
||||
|
||||
double PerformanceMetrics::GetVPS() const
|
||||
{
|
||||
return m_vps_counter.GetHzAvg();
|
||||
}
|
||||
|
||||
double PerformanceMetrics::GetSpeed() const
|
||||
{
|
||||
return 100.0 * m_speed_counter.GetHzAvg() / VideoInterface::GetTargetRefreshRate();
|
||||
}
|
||||
|
||||
double PerformanceMetrics::GetLastSpeedDenominator() const
|
||||
{
|
||||
return DT_s(m_speed_counter.GetLastRawDt()).count() * VideoInterface::GetTargetRefreshRate();
|
||||
}
|
||||
|
||||
void PerformanceMetrics::DrawImGuiStats(const float backbuffer_scale) const
|
||||
{
|
||||
const float bg_alpha = 0.7f;
|
||||
const auto imgui_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing;
|
||||
|
||||
const double fps = GetFPS();
|
||||
const double vps = GetVPS();
|
||||
const double speed = GetSpeed();
|
||||
|
||||
// Change Color based on % Speed
|
||||
float r = 0.0f, g = 1.0f, b = 1.0f;
|
||||
if (g_ActiveConfig.bShowSpeedColors)
|
||||
{
|
||||
r = 1.0 - (speed - 80.0) / 20.0;
|
||||
g = speed / 80.0;
|
||||
b = (speed - 90.0) / 10.0;
|
||||
}
|
||||
|
||||
const float window_padding = 8.f * backbuffer_scale;
|
||||
const float window_width = 93.f * backbuffer_scale;
|
||||
float window_y = window_padding;
|
||||
float window_x = ImGui::GetIO().DisplaySize.x - window_padding;
|
||||
|
||||
const float graph_width = 50.f * backbuffer_scale + 3.f * window_width + 2.f * window_padding;
|
||||
const float graph_height =
|
||||
std::min(200.f * backbuffer_scale, ImGui::GetIO().DisplaySize.y - 85.f * backbuffer_scale);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 14.f * backbuffer_scale);
|
||||
if (g_ActiveConfig.bShowGraphs)
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 4.f * backbuffer_scale));
|
||||
|
||||
// Position in the top-right corner of the screen.
|
||||
ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
ImGui::SetNextWindowSize(ImVec2(graph_width, graph_height));
|
||||
ImGui::SetNextWindowBgAlpha(bg_alpha);
|
||||
window_y += graph_height + window_padding;
|
||||
|
||||
if (ImGui::Begin("PerformanceGraphs", nullptr, imgui_flags))
|
||||
{
|
||||
const static int num_ticks = 17;
|
||||
const static double tick_marks[num_ticks] = {0.0,
|
||||
1000.0 / 360.0,
|
||||
1000.0 / 240.0,
|
||||
1000.0 / 180.0,
|
||||
1000.0 / 120.0,
|
||||
1000.0 / 90.00,
|
||||
1000.0 / 59.94,
|
||||
1000.0 / 40.00,
|
||||
1000.0 / 29.97,
|
||||
1000.0 / 24.00,
|
||||
1000.0 / 20.00,
|
||||
1000.0 / 15.00,
|
||||
1000.0 / 10.00,
|
||||
1000.0 / 5.000,
|
||||
1000.0 / 2.000,
|
||||
1000.0,
|
||||
2000.0};
|
||||
|
||||
const DT vblank_time = m_vps_counter.GetDtAvg() + m_vps_counter.GetDtStd();
|
||||
const DT frame_time = m_fps_counter.GetDtAvg() + m_fps_counter.GetDtStd();
|
||||
const double target_max_time = DT_ms(vblank_time + frame_time).count();
|
||||
const double a =
|
||||
std::max(0.0, 1.0 - std::exp(-4000.0 * m_vps_counter.GetLastRawDt().count() /
|
||||
DT_ms(m_vps_counter.GetSampleWindow()).count()));
|
||||
|
||||
static double max_time = 0.0;
|
||||
if (std::isfinite(max_time))
|
||||
max_time += a * (target_max_time - max_time);
|
||||
else
|
||||
max_time = target_max_time;
|
||||
|
||||
const double total_frame_time = std::max(DT_ms(m_fps_counter.GetSampleWindow()).count(),
|
||||
DT_ms(m_vps_counter.GetSampleWindow()).count());
|
||||
|
||||
if (ImPlot::BeginPlot("PerformanceGraphs", ImVec2(-1.0, -1.0),
|
||||
ImPlotFlags_NoFrame | ImPlotFlags_NoTitle | ImPlotFlags_NoMenus))
|
||||
{
|
||||
ImPlot::PushStyleColor(ImPlotCol_PlotBg, {0, 0, 0, 0});
|
||||
ImPlot::PushStyleColor(ImPlotCol_LegendBg, {0, 0, 0, 0.2f});
|
||||
ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, ImVec2(0.f, 0.f));
|
||||
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 3.f);
|
||||
ImPlot::SetupAxes(nullptr, nullptr,
|
||||
ImPlotAxisFlags_Lock | ImPlotAxisFlags_Invert |
|
||||
ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight,
|
||||
ImPlotAxisFlags_Lock | ImPlotAxisFlags_Invert | ImPlotAxisFlags_NoLabel |
|
||||
ImPlotAxisFlags_NoHighlight);
|
||||
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.1f");
|
||||
ImPlot::SetupAxisTicks(ImAxis_Y1, tick_marks, num_ticks);
|
||||
ImPlot::SetupAxesLimits(0, total_frame_time, 0, max_time, ImGuiCond_Always);
|
||||
ImPlot::SetupLegend(ImPlotLocation_SouthEast, ImPlotLegendFlags_None);
|
||||
m_vps_counter.ImPlotPlotLines("V-Blank (ms)");
|
||||
m_fps_counter.ImPlotPlotLines("Frame (ms)");
|
||||
ImPlot::EndPlot();
|
||||
ImPlot::PopStyleVar(2);
|
||||
ImPlot::PopStyleColor(2);
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShowFPS || g_ActiveConfig.bShowFTimes)
|
||||
{
|
||||
// Position in the top-right corner of the screen.
|
||||
int count = g_ActiveConfig.bShowFPS + 2 * g_ActiveConfig.bShowFTimes;
|
||||
ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, (12.f + 17.f * count) * backbuffer_scale));
|
||||
ImGui::SetNextWindowBgAlpha(bg_alpha);
|
||||
window_x -= window_width + window_padding;
|
||||
|
||||
if (ImGui::Begin("FPSStats", nullptr, imgui_flags))
|
||||
{
|
||||
if (g_ActiveConfig.bShowFPS)
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "FPS:%7.2lf", fps);
|
||||
if (g_ActiveConfig.bShowFTimes)
|
||||
{
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "dt:%6.2lfms",
|
||||
DT_ms(m_fps_counter.GetDtAvg()).count());
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), " ±:%6.2lfms",
|
||||
DT_ms(m_fps_counter.GetDtStd()).count());
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShowVPS || g_ActiveConfig.bShowVTimes)
|
||||
{
|
||||
// Position in the top-right corner of the screen.
|
||||
int count = g_ActiveConfig.bShowVPS + 2 * g_ActiveConfig.bShowVTimes;
|
||||
ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, (12.f + 17.f * count) * backbuffer_scale));
|
||||
ImGui::SetNextWindowBgAlpha(bg_alpha);
|
||||
window_x -= window_width + window_padding;
|
||||
|
||||
if (ImGui::Begin("VPSStats", nullptr, imgui_flags))
|
||||
{
|
||||
if (g_ActiveConfig.bShowVPS)
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "VPS:%7.2lf", vps);
|
||||
if (g_ActiveConfig.bShowVTimes)
|
||||
{
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "dt:%6.2lfms",
|
||||
DT_ms(m_vps_counter.GetDtAvg()).count());
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), " ±:%6.2lfms",
|
||||
DT_ms(m_vps_counter.GetDtStd()).count());
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShowSpeed)
|
||||
{
|
||||
// Position in the top-right corner of the screen.
|
||||
ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, 29.f * backbuffer_scale));
|
||||
ImGui::SetNextWindowBgAlpha(bg_alpha);
|
||||
|
||||
if (ImGui::Begin("SpeedStats", nullptr, imgui_flags))
|
||||
{
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "Speed:%4.0lf%%", speed);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VideoCommon/PerformanceTracker.h"
|
||||
|
||||
class PerformanceMetrics
|
||||
{
|
||||
public:
|
||||
PerformanceMetrics() = default;
|
||||
~PerformanceMetrics() = default;
|
||||
|
||||
PerformanceMetrics(const PerformanceMetrics&) = delete;
|
||||
PerformanceMetrics& operator=(const PerformanceMetrics&) = delete;
|
||||
PerformanceMetrics(PerformanceMetrics&&) = delete;
|
||||
PerformanceMetrics& operator=(PerformanceMetrics&&) = delete;
|
||||
|
||||
public: // Count Functions
|
||||
void Reset();
|
||||
void CountFrame();
|
||||
void CountVBlank();
|
||||
|
||||
public: // Getter Functions
|
||||
double GetFPS() const;
|
||||
double GetVPS() const;
|
||||
double GetSpeed() const;
|
||||
|
||||
double GetLastSpeedDenominator() const;
|
||||
|
||||
public: // ImGui Functions
|
||||
void DrawImGuiStats(const float backbuffer_scale) const;
|
||||
|
||||
private: // Member Variables
|
||||
PerformanceTracker m_fps_counter{"render_times.txt"};
|
||||
PerformanceTracker m_vps_counter{"vblank_times.txt"};
|
||||
PerformanceTracker m_speed_counter{nullptr, 6000000};
|
||||
};
|
||||
|
||||
extern PerformanceMetrics g_perf_metrics;
|
|
@ -0,0 +1,252 @@
|
|||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/PerformanceTracker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <implot.h>
|
||||
#include <iomanip>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Timer.h"
|
||||
#include "Core/Core.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
static constexpr double SAMPLE_RC_RATIO = 0.25;
|
||||
|
||||
PerformanceTracker::PerformanceTracker(const char* log_name,
|
||||
const std::optional<s64> sample_window_us)
|
||||
: m_on_state_changed_handle{Core::AddOnStateChangedCallback([this](Core::State state) {
|
||||
if (state == Core::State::Paused)
|
||||
SetPaused(true);
|
||||
else if (state == Core::State::Running)
|
||||
SetPaused(false);
|
||||
})},
|
||||
m_log_name{log_name}, m_sample_window_us{sample_window_us}
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
PerformanceTracker::~PerformanceTracker()
|
||||
{
|
||||
Core::RemoveOnStateChangedCallback(&m_on_state_changed_handle);
|
||||
}
|
||||
|
||||
void PerformanceTracker::Reset()
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
|
||||
QueueClear();
|
||||
m_last_time = Clock::now();
|
||||
m_hz_avg = 0.0;
|
||||
m_dt_avg = DT::zero();
|
||||
m_dt_std = std::nullopt;
|
||||
}
|
||||
|
||||
void PerformanceTracker::Count()
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
|
||||
if (m_paused)
|
||||
return;
|
||||
|
||||
const DT window{GetSampleWindow()};
|
||||
|
||||
const TimePoint time{Clock::now()};
|
||||
const DT diff{time - m_last_time};
|
||||
|
||||
m_last_time = time;
|
||||
|
||||
QueuePush(diff);
|
||||
m_dt_total += diff;
|
||||
|
||||
if (m_dt_queue_begin == m_dt_queue_end)
|
||||
m_dt_total -= QueuePop();
|
||||
|
||||
while (window <= m_dt_total - QueueTop())
|
||||
m_dt_total -= QueuePop();
|
||||
|
||||
// Simple Moving Average Throughout the Window
|
||||
m_dt_avg = m_dt_total / QueueSize();
|
||||
const double hz = DT_s(1.0) / m_dt_avg;
|
||||
|
||||
// Exponential Moving Average
|
||||
const DT_s rc = SAMPLE_RC_RATIO * std::min(window, m_dt_total);
|
||||
const double a = 1.0 - std::exp(-(DT_s(diff) / rc));
|
||||
|
||||
// Sometimes euler averages can break when the average is inf/nan
|
||||
if (std::isfinite(m_hz_avg))
|
||||
m_hz_avg += a * (hz - m_hz_avg);
|
||||
else
|
||||
m_hz_avg = hz;
|
||||
|
||||
m_dt_std = std::nullopt;
|
||||
|
||||
if (m_log_name && g_ActiveConfig.bLogRenderTimeToFile)
|
||||
LogRenderTimeToFile(diff);
|
||||
}
|
||||
|
||||
DT PerformanceTracker::GetSampleWindow() const
|
||||
{
|
||||
// This reads a constant value and thus does not need a mutex
|
||||
return std::chrono::duration_cast<DT>(
|
||||
DT_us(m_sample_window_us.value_or(std::max(1, g_ActiveConfig.iPerfSampleUSec))));
|
||||
}
|
||||
|
||||
double PerformanceTracker::GetHzAvg() const
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
return m_hz_avg;
|
||||
}
|
||||
|
||||
DT PerformanceTracker::GetDtAvg() const
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
return m_dt_avg;
|
||||
}
|
||||
|
||||
DT PerformanceTracker::GetDtStd() const
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
|
||||
if (m_dt_std)
|
||||
return *m_dt_std;
|
||||
|
||||
if (QueueEmpty())
|
||||
return *(m_dt_std = DT::zero());
|
||||
|
||||
double total = 0.0;
|
||||
for (std::size_t i = m_dt_queue_begin; i != m_dt_queue_end; i = IncrementIndex(i))
|
||||
{
|
||||
double diff = DT_s(m_dt_queue[i] - m_dt_avg).count();
|
||||
total += diff * diff;
|
||||
}
|
||||
|
||||
// This is a weighted standard deviation
|
||||
return *(m_dt_std = std::chrono::duration_cast<DT>(DT_s(std::sqrt(total / QueueSize()))));
|
||||
}
|
||||
|
||||
DT PerformanceTracker::GetLastRawDt() const
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
|
||||
if (QueueEmpty())
|
||||
return DT::zero();
|
||||
|
||||
return QueueBottom();
|
||||
}
|
||||
|
||||
void PerformanceTracker::ImPlotPlotLines(const char* label) const
|
||||
{
|
||||
static std::array<float, MAX_DT_QUEUE_SIZE + 2> x, y;
|
||||
|
||||
std::lock_guard lock{m_mutex};
|
||||
|
||||
if (QueueEmpty())
|
||||
return;
|
||||
|
||||
// Decides if there are too many points to plot using rectangles
|
||||
const bool quality = QueueSize() < MAX_QUALITY_GRAPH_SIZE;
|
||||
|
||||
const DT update_time = Clock::now() - m_last_time;
|
||||
const float predicted_frame_time = DT_ms(std::max(update_time, QueueBottom())).count();
|
||||
|
||||
std::size_t points = 0;
|
||||
if (quality)
|
||||
{
|
||||
x[points] = 0.f;
|
||||
y[points] = predicted_frame_time;
|
||||
++points;
|
||||
}
|
||||
|
||||
x[points] = DT_ms(update_time).count();
|
||||
y[points] = predicted_frame_time;
|
||||
++points;
|
||||
|
||||
const std::size_t begin = DecrementIndex(m_dt_queue_end);
|
||||
const std::size_t end = DecrementIndex(m_dt_queue_begin);
|
||||
for (std::size_t i = begin; i != end; i = DecrementIndex(i))
|
||||
{
|
||||
const float frame_time_ms = DT_ms(m_dt_queue[i]).count();
|
||||
|
||||
if (quality)
|
||||
{
|
||||
x[points] = x[points - 1];
|
||||
y[points] = frame_time_ms;
|
||||
++points;
|
||||
}
|
||||
|
||||
x[points] = x[points - 1] + frame_time_ms;
|
||||
y[points] = frame_time_ms;
|
||||
++points;
|
||||
}
|
||||
|
||||
ImPlot::PlotLine(label, x.data(), y.data(), static_cast<int>(points));
|
||||
}
|
||||
|
||||
void PerformanceTracker::QueueClear()
|
||||
{
|
||||
m_dt_total = DT::zero();
|
||||
m_dt_queue_begin = 0;
|
||||
m_dt_queue_end = 0;
|
||||
}
|
||||
|
||||
void PerformanceTracker::QueuePush(DT dt)
|
||||
{
|
||||
m_dt_queue[m_dt_queue_end] = dt;
|
||||
m_dt_queue_end = IncrementIndex(m_dt_queue_end);
|
||||
}
|
||||
|
||||
const DT& PerformanceTracker::QueuePop()
|
||||
{
|
||||
const std::size_t top = m_dt_queue_begin;
|
||||
m_dt_queue_begin = IncrementIndex(m_dt_queue_begin);
|
||||
return m_dt_queue[top];
|
||||
}
|
||||
|
||||
const DT& PerformanceTracker::QueueTop() const
|
||||
{
|
||||
return m_dt_queue[m_dt_queue_begin];
|
||||
}
|
||||
|
||||
const DT& PerformanceTracker::QueueBottom() const
|
||||
{
|
||||
return m_dt_queue[DecrementIndex(m_dt_queue_end)];
|
||||
}
|
||||
|
||||
std::size_t PerformanceTracker::QueueSize() const
|
||||
{
|
||||
return GetDifference(m_dt_queue_begin, m_dt_queue_end);
|
||||
}
|
||||
|
||||
bool PerformanceTracker::QueueEmpty() const
|
||||
{
|
||||
return m_dt_queue_begin == m_dt_queue_end;
|
||||
}
|
||||
|
||||
void PerformanceTracker::LogRenderTimeToFile(DT val)
|
||||
{
|
||||
if (!m_bench_file.is_open())
|
||||
{
|
||||
File::OpenFStream(m_bench_file, File::GetUserPath(D_LOGS_IDX) + m_log_name, std::ios_base::out);
|
||||
}
|
||||
|
||||
m_bench_file << std::fixed << std::setprecision(8) << DT_ms(val).count() << std::endl;
|
||||
}
|
||||
|
||||
void PerformanceTracker::SetPaused(bool paused)
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
|
||||
m_paused = paused;
|
||||
if (m_paused)
|
||||
{
|
||||
m_last_time = TimePoint::max();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_last_time = Clock::now();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class PerformanceTracker
|
||||
{
|
||||
private:
|
||||
// Must be powers of 2 for masking to work
|
||||
static constexpr u64 MAX_DT_QUEUE_SIZE = 1UL << 12;
|
||||
static constexpr u64 MAX_QUALITY_GRAPH_SIZE = 1UL << 8;
|
||||
|
||||
static inline std::size_t IncrementIndex(const std::size_t index)
|
||||
{
|
||||
return (index + 1) & (MAX_DT_QUEUE_SIZE - 1);
|
||||
}
|
||||
|
||||
static inline std::size_t DecrementIndex(const std::size_t index)
|
||||
{
|
||||
return (index - 1) & (MAX_DT_QUEUE_SIZE - 1);
|
||||
}
|
||||
|
||||
static inline std::size_t GetDifference(const std::size_t begin, const std::size_t end)
|
||||
{
|
||||
return (end - begin) & (MAX_DT_QUEUE_SIZE - 1);
|
||||
}
|
||||
|
||||
public:
|
||||
PerformanceTracker(const char* log_name = nullptr,
|
||||
const std::optional<s64> sample_window_us = {});
|
||||
~PerformanceTracker();
|
||||
|
||||
PerformanceTracker(const PerformanceTracker&) = delete;
|
||||
PerformanceTracker& operator=(const PerformanceTracker&) = delete;
|
||||
PerformanceTracker(PerformanceTracker&&) = delete;
|
||||
PerformanceTracker& operator=(PerformanceTracker&&) = delete;
|
||||
|
||||
public: // Functions for recording and accessing information
|
||||
void Reset();
|
||||
void Count();
|
||||
|
||||
DT GetSampleWindow() const;
|
||||
|
||||
double GetHzAvg() const;
|
||||
|
||||
DT GetDtAvg() const;
|
||||
DT GetDtStd() const;
|
||||
|
||||
DT GetLastRawDt() const;
|
||||
|
||||
void ImPlotPlotLines(const char* label) const;
|
||||
|
||||
private: // Functions for managing dt queue
|
||||
inline void QueueClear();
|
||||
inline void QueuePush(DT dt);
|
||||
inline const DT& QueuePop();
|
||||
inline const DT& QueueTop() const;
|
||||
inline const DT& QueueBottom() const;
|
||||
|
||||
std::size_t inline QueueSize() const;
|
||||
bool inline QueueEmpty() const;
|
||||
|
||||
private: // Handle pausing and logging
|
||||
void LogRenderTimeToFile(DT val);
|
||||
void SetPaused(bool paused);
|
||||
|
||||
private:
|
||||
// Handle not counting time during pauses
|
||||
bool m_paused = false;
|
||||
int m_on_state_changed_handle;
|
||||
|
||||
// Name of log file and file stream
|
||||
const char* m_log_name;
|
||||
std::ofstream m_bench_file;
|
||||
|
||||
// Last time Count() was called
|
||||
TimePoint m_last_time;
|
||||
|
||||
// Amount of time to sample dt's over (defaults to config)
|
||||
const std::optional<s64> m_sample_window_us;
|
||||
|
||||
// Queue + Running Total used to calculate average dt
|
||||
DT m_dt_total = DT::zero();
|
||||
std::array<DT, MAX_DT_QUEUE_SIZE> m_dt_queue;
|
||||
std::size_t m_dt_queue_begin = 0;
|
||||
std::size_t m_dt_queue_end = 0;
|
||||
|
||||
// Average rate/time throughout the window
|
||||
DT m_dt_avg = DT::zero(); // Uses Moving Average
|
||||
double m_hz_avg = 0.0; // Uses Moving Average + Euler Average
|
||||
|
||||
// Used to initialize this on demand instead of on every Count()
|
||||
mutable std::optional<DT> m_dt_std = std::nullopt;
|
||||
|
||||
// Used to enable thread safety with the performance tracker
|
||||
mutable std::mutex m_mutex;
|
||||
};
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <fmt/format.h>
|
||||
#include <imgui.h>
|
||||
#include <implot.h>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
|
@ -63,7 +64,6 @@
|
|||
#include "VideoCommon/BoundingBox.h"
|
||||
#include "VideoCommon/CPMemory.h"
|
||||
#include "VideoCommon/CommandProcessor.h"
|
||||
#include "VideoCommon/FPSCounter.h"
|
||||
#include "VideoCommon/FrameDump.h"
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/FramebufferShaderGen.h"
|
||||
|
@ -592,46 +592,6 @@ void Renderer::CheckForConfigChanges()
|
|||
// Create On-Screen-Messages
|
||||
void Renderer::DrawDebugText()
|
||||
{
|
||||
if (g_ActiveConfig.bShowFPS || g_ActiveConfig.bShowVPS || g_ActiveConfig.bShowSpeed)
|
||||
{
|
||||
// Position in the top-right corner of the screen.
|
||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - (10.0f * m_backbuffer_scale),
|
||||
10.f * m_backbuffer_scale),
|
||||
ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
|
||||
int count = g_ActiveConfig.bShowFPS + g_ActiveConfig.bShowVPS + g_ActiveConfig.bShowSpeed;
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(94.f * m_backbuffer_scale, (12.f + 17.f * count) * m_backbuffer_scale));
|
||||
|
||||
if (ImGui::Begin("Performance", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing))
|
||||
{
|
||||
const double fps = m_fps_counter.GetFPS();
|
||||
const double vps = m_vps_counter.GetFPS();
|
||||
const double speed = 100.0 * vps / VideoInterface::GetTargetRefreshRate();
|
||||
|
||||
// Change Color based on % Speed
|
||||
float r = 0.0f, g = 1.0f, b = 1.0f;
|
||||
if (g_ActiveConfig.bShowSpeedColors)
|
||||
{
|
||||
r = 1.0 - (speed - 80.0) / 20.0;
|
||||
g = speed / 80.0;
|
||||
b = (speed - 90.0) / 10.0;
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShowFPS)
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "FPS:%7.2lf", fps);
|
||||
if (g_ActiveConfig.bShowVPS)
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "VPS:%7.2lf", vps);
|
||||
if (g_ActiveConfig.bShowSpeed)
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "Speed:%4.0lf%%", speed);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
const bool show_movie_window =
|
||||
Config::Get(Config::MAIN_SHOW_FRAME_COUNT) || Config::Get(Config::MAIN_SHOW_LAG) ||
|
||||
Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY) ||
|
||||
|
@ -1046,6 +1006,11 @@ bool Renderer::InitializeImGui()
|
|||
PanicAlertFmt("Creating ImGui context failed");
|
||||
return false;
|
||||
}
|
||||
if (!ImPlot::CreateContext())
|
||||
{
|
||||
PanicAlertFmt("Creating ImPlot context failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't create an ini file. TODO: Do we want this in the future?
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
|
@ -1159,6 +1124,7 @@ void Renderer::ShutdownImGui()
|
|||
std::unique_lock<std::mutex> imgui_lock(m_imgui_mutex);
|
||||
|
||||
ImGui::EndFrame();
|
||||
ImPlot::DestroyContext();
|
||||
ImGui::DestroyContext();
|
||||
m_imgui_pipeline.reset();
|
||||
m_imgui_vertex_format.reset();
|
||||
|
@ -1390,10 +1356,6 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
|
|||
g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect);
|
||||
const bool is_duplicate_frame = xfb_entry->id == m_last_xfb_id;
|
||||
|
||||
m_vps_counter.Update();
|
||||
if (!is_duplicate_frame)
|
||||
m_fps_counter.Update();
|
||||
|
||||
if (xfb_entry && (!g_ActiveConfig.bSkipPresentingDuplicateXFBs || !is_duplicate_frame))
|
||||
{
|
||||
m_last_xfb_id = xfb_entry->id;
|
||||
|
@ -1407,6 +1369,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
|
|||
{
|
||||
auto lock = GetImGuiLock();
|
||||
|
||||
g_perf_metrics.DrawImGuiStats(m_backbuffer_scale);
|
||||
DrawDebugText();
|
||||
OSD::DrawMessages();
|
||||
ImGui::Render();
|
||||
|
@ -1485,8 +1448,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
|
|||
{
|
||||
// Remove stale EFB/XFB copies.
|
||||
g_texture_cache->Cleanup(m_frame_count);
|
||||
const double last_speed_denominator =
|
||||
m_fps_counter.GetDeltaTime() * VideoInterface::GetTargetRefreshRate();
|
||||
const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator();
|
||||
// The denominator should always be > 0 but if it's not, just return 1
|
||||
const double last_speed =
|
||||
last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0;
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
#include "Common/MathUtil.h"
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
#include "VideoCommon/FPSCounter.h"
|
||||
#include "VideoCommon/FrameDump.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
|
||||
#include "VideoCommon/PerformanceMetrics.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
|
||||
|
@ -338,9 +338,6 @@ protected:
|
|||
MathUtil::Rectangle<int> m_target_rectangle = {};
|
||||
int m_frame_count = 0;
|
||||
|
||||
FPSCounter m_fps_counter = FPSCounter("render_times.txt");
|
||||
FPSCounter m_vps_counter = FPSCounter("v_blank_times.txt");
|
||||
|
||||
std::unique_ptr<VideoCommon::PostProcessing> m_post_processor;
|
||||
|
||||
void* m_new_surface_handle = nullptr;
|
||||
|
|
|
@ -64,7 +64,10 @@ void VideoConfig::Refresh()
|
|||
bCrop = Config::Get(Config::GFX_CROP);
|
||||
iSafeTextureCache_ColorSamples = Config::Get(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES);
|
||||
bShowFPS = Config::Get(Config::GFX_SHOW_FPS);
|
||||
bShowFTimes = Config::Get(Config::GFX_SHOW_FTIMES);
|
||||
bShowVPS = Config::Get(Config::GFX_SHOW_VPS);
|
||||
bShowVTimes = Config::Get(Config::GFX_SHOW_VTIMES);
|
||||
bShowGraphs = Config::Get(Config::GFX_SHOW_GRAPHS);
|
||||
bShowSpeed = Config::Get(Config::GFX_SHOW_SPEED);
|
||||
bShowSpeedColors = Config::Get(Config::GFX_SHOW_SPEED_COLORS);
|
||||
iPerfSampleUSec = Config::Get(Config::GFX_PERF_SAMP_WINDOW) * 1000;
|
||||
|
|
|
@ -89,7 +89,10 @@ struct VideoConfig final
|
|||
|
||||
// Information
|
||||
bool bShowFPS = false;
|
||||
bool bShowFTimes = false;
|
||||
bool bShowVPS = false;
|
||||
bool bShowVTimes = false;
|
||||
bool bShowGraphs = false;
|
||||
bool bShowSpeed = false;
|
||||
bool bShowSpeedColors = false;
|
||||
int iPerfSampleUSec = 0;
|
||||
|
|
Loading…
Reference in New Issue