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