1/*
2 * Copyright (C) 2012 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// This is needed for stdint.h to define INT64_MAX in C++
18#define __STDC_LIMIT_MACROS
19
20#include <inttypes.h>
21
22#include <cutils/log.h>
23
24#include <ui/Fence.h>
25#include <ui/FrameStats.h>
26
27#include <utils/String8.h>
28
29#include "FrameTracker.h"
30#include "EventLog/EventLog.h"
31
32namespace android {
33
34FrameTracker::FrameTracker() :
35        mOffset(0),
36        mNumFences(0),
37        mDisplayPeriod(0) {
38    resetFrameCountersLocked();
39}
40
41void FrameTracker::setDesiredPresentTime(nsecs_t presentTime) {
42    Mutex::Autolock lock(mMutex);
43    mFrameRecords[mOffset].desiredPresentTime = presentTime;
44}
45
46void FrameTracker::setFrameReadyTime(nsecs_t readyTime) {
47    Mutex::Autolock lock(mMutex);
48    mFrameRecords[mOffset].frameReadyTime = readyTime;
49}
50
51void FrameTracker::setFrameReadyFence(const sp<Fence>& readyFence) {
52    Mutex::Autolock lock(mMutex);
53    mFrameRecords[mOffset].frameReadyFence = readyFence;
54    mNumFences++;
55}
56
57void FrameTracker::setActualPresentTime(nsecs_t presentTime) {
58    Mutex::Autolock lock(mMutex);
59    mFrameRecords[mOffset].actualPresentTime = presentTime;
60}
61
62void FrameTracker::setActualPresentFence(const sp<Fence>& readyFence) {
63    Mutex::Autolock lock(mMutex);
64    mFrameRecords[mOffset].actualPresentFence = readyFence;
65    mNumFences++;
66}
67
68void FrameTracker::setDisplayRefreshPeriod(nsecs_t displayPeriod) {
69    Mutex::Autolock lock(mMutex);
70    mDisplayPeriod = displayPeriod;
71}
72
73void FrameTracker::advanceFrame() {
74    Mutex::Autolock lock(mMutex);
75
76    // Update the statistic to include the frame we just finished.
77    updateStatsLocked(mOffset);
78
79    // Advance to the next frame.
80    mOffset = (mOffset+1) % NUM_FRAME_RECORDS;
81    mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
82    mFrameRecords[mOffset].frameReadyTime = INT64_MAX;
83    mFrameRecords[mOffset].actualPresentTime = INT64_MAX;
84
85    if (mFrameRecords[mOffset].frameReadyFence != NULL) {
86        // We're clobbering an unsignaled fence, so we need to decrement the
87        // fence count.
88        mFrameRecords[mOffset].frameReadyFence = NULL;
89        mNumFences--;
90    }
91
92    if (mFrameRecords[mOffset].actualPresentFence != NULL) {
93        // We're clobbering an unsignaled fence, so we need to decrement the
94        // fence count.
95        mFrameRecords[mOffset].actualPresentFence = NULL;
96        mNumFences--;
97    }
98
99    // Clean up the signaled fences to keep the number of open fence FDs in
100    // this process reasonable.
101    processFencesLocked();
102}
103
104void FrameTracker::clearStats() {
105    Mutex::Autolock lock(mMutex);
106    for (size_t i = 0; i < NUM_FRAME_RECORDS; i++) {
107        mFrameRecords[i].desiredPresentTime = 0;
108        mFrameRecords[i].frameReadyTime = 0;
109        mFrameRecords[i].actualPresentTime = 0;
110        mFrameRecords[i].frameReadyFence.clear();
111        mFrameRecords[i].actualPresentFence.clear();
112    }
113    mNumFences = 0;
114    mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
115    mFrameRecords[mOffset].frameReadyTime = INT64_MAX;
116    mFrameRecords[mOffset].actualPresentTime = INT64_MAX;
117}
118
119void FrameTracker::getStats(FrameStats* outStats) const {
120    Mutex::Autolock lock(mMutex);
121    processFencesLocked();
122
123    outStats->refreshPeriodNano = mDisplayPeriod;
124
125    const size_t offset = mOffset;
126    for (size_t i = 1; i < NUM_FRAME_RECORDS; i++) {
127        const size_t index = (offset + i) % NUM_FRAME_RECORDS;
128
129        // Skip frame records with no data (if buffer not yet full).
130        if (mFrameRecords[index].desiredPresentTime == 0) {
131            continue;
132        }
133
134        nsecs_t desiredPresentTimeNano = mFrameRecords[index].desiredPresentTime;
135        outStats->desiredPresentTimesNano.push_back(desiredPresentTimeNano);
136
137        nsecs_t actualPresentTimeNano = mFrameRecords[index].actualPresentTime;
138        outStats->actualPresentTimesNano.push_back(actualPresentTimeNano);
139
140        nsecs_t frameReadyTimeNano = mFrameRecords[index].frameReadyTime;
141        outStats->frameReadyTimesNano.push_back(frameReadyTimeNano);
142    }
143}
144
145void FrameTracker::logAndResetStats(const String8& name) {
146    Mutex::Autolock lock(mMutex);
147    logStatsLocked(name);
148    resetFrameCountersLocked();
149}
150
151void FrameTracker::processFencesLocked() const {
152    FrameRecord* records = const_cast<FrameRecord*>(mFrameRecords);
153    int& numFences = const_cast<int&>(mNumFences);
154
155    for (int i = 1; i < NUM_FRAME_RECORDS && numFences > 0; i++) {
156        size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;
157        bool updated = false;
158
159        const sp<Fence>& rfence = records[idx].frameReadyFence;
160        if (rfence != NULL) {
161            records[idx].frameReadyTime = rfence->getSignalTime();
162            if (records[idx].frameReadyTime < INT64_MAX) {
163                records[idx].frameReadyFence = NULL;
164                numFences--;
165                updated = true;
166            }
167        }
168
169        const sp<Fence>& pfence = records[idx].actualPresentFence;
170        if (pfence != NULL) {
171            records[idx].actualPresentTime = pfence->getSignalTime();
172            if (records[idx].actualPresentTime < INT64_MAX) {
173                records[idx].actualPresentFence = NULL;
174                numFences--;
175                updated = true;
176            }
177        }
178
179        if (updated) {
180            updateStatsLocked(idx);
181        }
182    }
183}
184
185void FrameTracker::updateStatsLocked(size_t newFrameIdx) const {
186    int* numFrames = const_cast<int*>(mNumFrames);
187
188    if (mDisplayPeriod > 0 && isFrameValidLocked(newFrameIdx)) {
189        size_t prevFrameIdx = (newFrameIdx+NUM_FRAME_RECORDS-1) %
190                NUM_FRAME_RECORDS;
191
192        if (isFrameValidLocked(prevFrameIdx)) {
193            nsecs_t newPresentTime =
194                    mFrameRecords[newFrameIdx].actualPresentTime;
195            nsecs_t prevPresentTime =
196                    mFrameRecords[prevFrameIdx].actualPresentTime;
197
198            nsecs_t duration = newPresentTime - prevPresentTime;
199            int numPeriods = int((duration + mDisplayPeriod/2) /
200                    mDisplayPeriod);
201
202            for (int i = 0; i < NUM_FRAME_BUCKETS-1; i++) {
203                int nextBucket = 1 << (i+1);
204                if (numPeriods < nextBucket) {
205                    numFrames[i]++;
206                    return;
207                }
208            }
209
210            // The last duration bucket is a catch-all.
211            numFrames[NUM_FRAME_BUCKETS-1]++;
212        }
213    }
214}
215
216void FrameTracker::resetFrameCountersLocked() {
217    for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
218        mNumFrames[i] = 0;
219    }
220}
221
222void FrameTracker::logStatsLocked(const String8& name) const {
223    for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
224        if (mNumFrames[i] > 0) {
225            EventLog::logFrameDurations(name, mNumFrames, NUM_FRAME_BUCKETS);
226            return;
227        }
228    }
229}
230
231bool FrameTracker::isFrameValidLocked(size_t idx) const {
232    return mFrameRecords[idx].actualPresentTime > 0 &&
233            mFrameRecords[idx].actualPresentTime < INT64_MAX;
234}
235
236void FrameTracker::dumpStats(String8& result) const {
237    Mutex::Autolock lock(mMutex);
238    processFencesLocked();
239
240    const size_t o = mOffset;
241    for (size_t i = 1; i < NUM_FRAME_RECORDS; i++) {
242        const size_t index = (o+i) % NUM_FRAME_RECORDS;
243        result.appendFormat("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
244            mFrameRecords[index].desiredPresentTime,
245            mFrameRecords[index].actualPresentTime,
246            mFrameRecords[index].frameReadyTime);
247    }
248    result.append("\n");
249}
250
251} // namespace android
252