1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "FrameStatistics.h"
18
19 #define LOG_TAG "FrameStatistics"
20
21 #include <cmath>
22 #include <inttypes.h>
23 #include <string>
24
25 #include "EGL.h"
26
27 #include "Log.h"
28
29
30 namespace swappy {
31
32 // NB This is only needed for C++14
33 constexpr std::chrono::nanoseconds FrameStatistics::LOG_EVERY_N_NS;
34
updateFrames(EGLnsecsANDROID start,EGLnsecsANDROID end,uint64_t stat[])35 void FrameStatistics::updateFrames(EGLnsecsANDROID start, EGLnsecsANDROID end, uint64_t stat[]) {
36 const uint64_t deltaTimeNano = end - start;
37
38 uint32_t numFrames = deltaTimeNano / mRefreshPeriod.count();
39 numFrames = std::min(numFrames, static_cast<uint32_t>(MAX_FRAME_BUCKETS));
40 stat[numFrames]++;
41 }
42
updateIdleFrames(EGL::FrameTimestamps & frameStats)43 void FrameStatistics::updateIdleFrames(EGL::FrameTimestamps& frameStats) {
44 updateFrames(frameStats.renderingCompleted,
45 frameStats.compositionLatched,
46 mStats.idleFrames);
47 }
48
updateLatencyFrames(swappy::EGL::FrameTimestamps & frameStats,TimePoint frameStartTime)49 void FrameStatistics::updateLatencyFrames(swappy::EGL::FrameTimestamps &frameStats,
50 TimePoint frameStartTime) {
51 updateFrames(frameStartTime.time_since_epoch().count(),
52 frameStats.presented,
53 mStats.latencyFrames);
54 }
55
updateLateFrames(EGL::FrameTimestamps & frameStats)56 void FrameStatistics::updateLateFrames(EGL::FrameTimestamps& frameStats) {
57 updateFrames(frameStats.requested,
58 frameStats.presented,
59 mStats.lateFrames);
60 }
61
updateOffsetFromPreviousFrame(swappy::EGL::FrameTimestamps & frameStats)62 void FrameStatistics::updateOffsetFromPreviousFrame(swappy::EGL::FrameTimestamps &frameStats) {
63 if (mPrevFrameTime != 0) {
64 updateFrames(mPrevFrameTime,
65 frameStats.presented,
66 mStats.offsetFromPreviousFrame);
67 }
68 mPrevFrameTime = frameStats.presented;
69 }
70
71 // called once per swap
capture(EGLDisplay dpy,EGLSurface surface)72 void FrameStatistics::capture(EGLDisplay dpy, EGLSurface surface) {
73 const TimePoint frameStartTime = std::chrono::steady_clock::now();
74
75 // first get the next frame id
76 std::pair<bool,EGLuint64KHR> nextFrameId = mEgl->getNextFrameId(dpy, surface);
77 if (nextFrameId.first) {
78 mPendingFrames.push_back({dpy, surface, nextFrameId.second, frameStartTime});
79 }
80
81 if (mPendingFrames.empty()) {
82 return;
83 }
84
85
86 EGLFrame frame = mPendingFrames.front();
87 // make sure we don't lag behind the stats too much
88 if (nextFrameId.first && nextFrameId.second - frame.id > MAX_FRAME_LAG) {
89 while (mPendingFrames.size() > 1)
90 mPendingFrames.erase(mPendingFrames.begin());
91 mPrevFrameTime = 0;
92 frame = mPendingFrames.front();
93 }
94
95 std::unique_ptr<EGL::FrameTimestamps> frameStats =
96 mEgl->getFrameTimestamps(frame.dpy, frame.surface, frame.id);
97
98 if (!frameStats) {
99 return;
100 }
101
102 mPendingFrames.erase(mPendingFrames.begin());
103
104 std::lock_guard<std::mutex> lock(mMutex);
105 mStats.totalFrames++;
106 updateIdleFrames(*frameStats);
107 updateLateFrames(*frameStats);
108 updateOffsetFromPreviousFrame(*frameStats);
109 updateLatencyFrames(*frameStats, frame.startFrameTime);
110
111 logFrames();
112 }
113
logFrames()114 void FrameStatistics::logFrames() {
115 static auto previousLogTime = std::chrono::steady_clock::now();
116
117 if (std::chrono::steady_clock::now() - previousLogTime < LOG_EVERY_N_NS) {
118 return;
119 }
120
121 std::string message;
122 ALOGI("== Frame statistics ==");
123 ALOGI("total frames: %" PRIu64, mStats.totalFrames);
124 message += "Buckets: ";
125 for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
126 message += "\t[" + swappy::to_string(i) + "]";
127 ALOGI("%s", message.c_str());
128
129 message = "";
130 message += "idle frames: ";
131 for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
132 message += "\t " + swappy::to_string(mStats.idleFrames[i]);
133 ALOGI("%s", message.c_str());
134
135 message = "";
136 message += "late frames: ";
137 for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
138 message += "\t " + swappy::to_string(mStats.lateFrames[i]);
139 ALOGI("%s", message.c_str());
140
141 message = "";
142 message += "offset from previous frame: ";
143 for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
144 message += "\t " + swappy::to_string(mStats.offsetFromPreviousFrame[i]);
145 ALOGI("%s", message.c_str());
146
147 message = "";
148 message += "frame latency: ";
149 for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
150 message += "\t " + swappy::to_string(mStats.latencyFrames[i]);
151 ALOGI("%s", message.c_str());
152
153 previousLogTime = std::chrono::steady_clock::now();
154 }
155
getStats()156 Swappy_Stats FrameStatistics::getStats() {
157 std::lock_guard<std::mutex> lock(mMutex);
158 return mStats;
159 }
160
161 } // namespace swappy
162