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