summaryrefslogtreecommitdiff
path: root/src/common/profiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/profiler.cpp')
-rw-r--r--src/common/profiler.cpp182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp
new file mode 100644
index 000000000..65c3df167
--- /dev/null
+++ b/src/common/profiler.cpp
@@ -0,0 +1,182 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/profiler.h"
6#include "common/profiler_reporting.h"
7#include "common/assert.h"
8
9#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013.
10#define NOMINMAX
11#define WIN32_LEAN_AND_MEAN
12#include <Windows.h> // For QueryPerformanceCounter/Frequency
13#endif
14
15namespace Common {
16namespace Profiling {
17
18#if ENABLE_PROFILING
19thread_local Timer* Timer::current_timer = nullptr;
20#endif
21
22#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
23QPCClock::time_point QPCClock::now() {
24 static LARGE_INTEGER freq;
25 // Use this dummy local static to ensure this gets initialized once.
26 static BOOL dummy = QueryPerformanceFrequency(&freq);
27
28 LARGE_INTEGER ticks;
29 QueryPerformanceCounter(&ticks);
30
31 // This is prone to overflow when multiplying, which is why I'm using micro instead of nano. The
32 // correct way to approach this would be to just return ticks as a time_point and then subtract
33 // and do this conversion when creating a duration from two time_points, however, as far as I
34 // could tell the C++ requirements for these types are incompatible with this approach.
35 return time_point(duration(ticks.QuadPart * std::micro::den / freq.QuadPart));
36}
37#endif
38
39TimingCategory::TimingCategory(const char* name, TimingCategory* parent)
40 : accumulated_duration(0) {
41
42 ProfilingManager& manager = GetProfilingManager();
43 category_id = manager.RegisterTimingCategory(this, name);
44 if (parent != nullptr)
45 manager.SetTimingCategoryParent(category_id, parent->category_id);
46}
47
48ProfilingManager::ProfilingManager()
49 : last_frame_end(Clock::now()), this_frame_start(Clock::now()) {
50}
51
52unsigned int ProfilingManager::RegisterTimingCategory(TimingCategory* category, const char* name) {
53 TimingCategoryInfo info;
54 info.category = category;
55 info.name = name;
56 info.parent = TimingCategoryInfo::NO_PARENT;
57
58 unsigned int id = (unsigned int)timing_categories.size();
59 timing_categories.push_back(std::move(info));
60
61 return id;
62}
63
64void ProfilingManager::SetTimingCategoryParent(unsigned int category, unsigned int parent) {
65 ASSERT(category < timing_categories.size());
66 ASSERT(parent < timing_categories.size());
67
68 timing_categories[category].parent = parent;
69}
70
71void ProfilingManager::BeginFrame() {
72 this_frame_start = Clock::now();
73}
74
75void ProfilingManager::FinishFrame() {
76 Clock::time_point now = Clock::now();
77
78 results.interframe_time = now - last_frame_end;
79 results.frame_time = now - this_frame_start;
80
81 results.time_per_category.resize(timing_categories.size());
82 for (size_t i = 0; i < timing_categories.size(); ++i) {
83 results.time_per_category[i] = timing_categories[i].category->GetAccumulatedTime();
84 }
85
86 last_frame_end = now;
87}
88
89TimingResultsAggregator::TimingResultsAggregator(size_t window_size)
90 : max_window_size(window_size), window_size(0) {
91 interframe_times.resize(window_size, Duration::zero());
92 frame_times.resize(window_size, Duration::zero());
93}
94
95void TimingResultsAggregator::Clear() {
96 window_size = cursor = 0;
97}
98
99void TimingResultsAggregator::SetNumberOfCategories(size_t n) {
100 size_t old_size = times_per_category.size();
101 if (n == old_size)
102 return;
103
104 times_per_category.resize(n);
105
106 for (size_t i = old_size; i < n; ++i) {
107 times_per_category[i].resize(max_window_size, Duration::zero());
108 }
109}
110
111void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) {
112 SetNumberOfCategories(frame_result.time_per_category.size());
113
114 interframe_times[cursor] = frame_result.interframe_time;
115 frame_times[cursor] = frame_result.frame_time;
116 for (size_t i = 0; i < frame_result.time_per_category.size(); ++i) {
117 times_per_category[i][cursor] = frame_result.time_per_category[i];
118 }
119
120 ++cursor;
121 if (cursor == max_window_size)
122 cursor = 0;
123 if (window_size < max_window_size)
124 ++window_size;
125}
126
127static AggregatedDuration AggregateField(const std::vector<Duration>& v, size_t len) {
128 AggregatedDuration result;
129 result.avg = Duration::zero();
130
131 result.min = result.max = (len == 0 ? Duration::zero() : v[0]);
132
133 for (size_t i = 1; i < len; ++i) {
134 Duration value = v[i];
135 result.avg += value;
136 result.min = std::min(result.min, value);
137 result.max = std::max(result.max, value);
138 }
139 if (len != 0)
140 result.avg /= len;
141
142 return result;
143}
144
145static float tof(Common::Profiling::Duration dur) {
146 using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
147 return std::chrono::duration_cast<FloatMs>(dur).count();
148}
149
150AggregatedFrameResult TimingResultsAggregator::GetAggregatedResults() const {
151 AggregatedFrameResult result;
152
153 result.interframe_time = AggregateField(interframe_times, window_size);
154 result.frame_time = AggregateField(frame_times, window_size);
155
156 if (result.interframe_time.avg != Duration::zero()) {
157 result.fps = 1000.0f / tof(result.interframe_time.avg);
158 } else {
159 result.fps = 0.0f;
160 }
161
162 result.time_per_category.resize(times_per_category.size());
163 for (size_t i = 0; i < times_per_category.size(); ++i) {
164 result.time_per_category[i] = AggregateField(times_per_category[i], window_size);
165 }
166
167 return result;
168}
169
170ProfilingManager& GetProfilingManager() {
171 // Takes advantage of "magic" static initialization for race-free initialization.
172 static ProfilingManager manager;
173 return manager;
174}
175
176SynchronizedRef<TimingResultsAggregator> GetTimingResultsAggregator() {
177 static SynchronizedWrapper<TimingResultsAggregator> aggregator(30);
178 return SynchronizedRef<TimingResultsAggregator>(aggregator);
179}
180
181} // namespace Profiling
182} // namespace Common