This file was deleted.

This file was deleted.

@@ -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;

// Count Functions
void Reset();
void CountFrame();
void CountVBlank();

// Getter Functions
double GetFPS() const;
double GetVPS() const;
double GetSpeed() const;

double GetLastSpeedDenominator() const;

// ImGui Functions
void DrawImGuiStats(const float backbuffer_scale) const;

private:
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,104 @@
// 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;

// Functions for recording performance information
void Reset();
void Count();

// Functions for reading performance information
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;

// Handle pausing and logging
void LogRenderTimeToFile(DT val);
void SetPaused(bool paused);

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 @@ class Renderer
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;