| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| }; |