1 /*
2  * Copyright 2024 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 #define LOG_TAG "powerhal-libperfmgr"
18 
19 #include "SessionRecords.h"
20 
21 #include <android-base/logging.h>
22 
23 namespace aidl {
24 namespace google {
25 namespace hardware {
26 namespace power {
27 namespace impl {
28 namespace pixel {
29 
30 static constexpr int32_t kTotalFramesForFPSCheck = 3;
31 
SessionRecords(const int32_t maxNumOfRecords,const double jankCheckTimeFactor)32 SessionRecords::SessionRecords(const int32_t maxNumOfRecords, const double jankCheckTimeFactor)
33     : kMaxNumOfRecords(maxNumOfRecords), kJankCheckTimeFactor(jankCheckTimeFactor) {
34     mRecords.resize(maxNumOfRecords);
35 }
36 
addReportedDurations(const std::vector<WorkDuration> & actualDurationsNs,int64_t targetDurationNs,FrameBuckets & newFramesInBuckets,bool computeFPSJitters)37 void SessionRecords::addReportedDurations(const std::vector<WorkDuration> &actualDurationsNs,
38                                           int64_t targetDurationNs,
39                                           FrameBuckets &newFramesInBuckets,
40                                           bool computeFPSJitters) {
41     for (auto &duration : actualDurationsNs) {
42         int32_t totalDurationUs = duration.durationNanos / 1000;
43 
44         if (mNumOfFrames >= kMaxNumOfRecords) {
45             // Remove the oldest record when the number of records is greater
46             // than allowed.
47             int32_t indexOfRecordToRemove = (mLatestRecordIndex + 1) % kMaxNumOfRecords;
48             mSumOfDurationsUs -= mRecords[indexOfRecordToRemove].totalDurationUs;
49             if (mRecords[indexOfRecordToRemove].isMissedCycle) {
50                 mNumOfMissedCycles--;
51                 if (mNumOfMissedCycles < 0) {
52                     LOG(ERROR) << "Invalid number of missed cycles: " << mNumOfMissedCycles;
53                 }
54             }
55             if (mRecords[indexOfRecordToRemove].isFPSJitter) {
56                 mNumOfFrameFPSJitters--;
57                 if (mNumOfFrameFPSJitters < 0) {
58                     LOG(ERROR) << "Invalid number of FPS jitter frames: " << mNumOfFrameFPSJitters;
59                 }
60             }
61             mNumOfFrames--;
62 
63             // If the record to be removed is the max duration, pop it out of the
64             // descending dequeue of record indexes.
65             if (mRecordsIndQueue.front() == indexOfRecordToRemove) {
66                 mRecordsIndQueue.pop_front();
67             }
68         }
69 
70         mLatestRecordIndex = (mLatestRecordIndex + 1) % kMaxNumOfRecords;
71 
72         // Track start delay
73         auto startTimeNs = duration.timeStampNanos - duration.durationNanos;
74         int32_t startIntervalUs = 0;
75         if (mNumOfFrames > 0) {
76             startIntervalUs = (startTimeNs - mLastStartTimeNs) / 1000;
77         }
78         mLastStartTimeNs = startTimeNs;
79 
80         // Track the number of frame FPS jitters.
81         // A frame is evaluated as FPS jitter if its startInterval is not less
82         // than previous three frames' average startIntervals.
83         bool FPSJitter = false;
84         if (computeFPSJitters) {
85             if (mAddedFramesForFPSCheck < kTotalFramesForFPSCheck) {
86                 if (startIntervalUs > 0) {
87                     mLatestStartIntervalSumUs += startIntervalUs;
88                     mAddedFramesForFPSCheck++;
89                 }
90             } else {
91                 if (startIntervalUs > (1.4 * mLatestStartIntervalSumUs / kTotalFramesForFPSCheck)) {
92                     FPSJitter = true;
93                     mNumOfFrameFPSJitters++;
94                 }
95                 int32_t oldRecordIndex = mLatestRecordIndex - kTotalFramesForFPSCheck;
96                 if (oldRecordIndex < 0) {
97                     oldRecordIndex += kMaxNumOfRecords;
98                 }
99                 mLatestStartIntervalSumUs +=
100                         startIntervalUs - mRecords[oldRecordIndex].startIntervalUs;
101             }
102         } else {
103             mLatestStartIntervalSumUs = 0;
104             mAddedFramesForFPSCheck = 0;
105         }
106 
107         bool cycleMissed = totalDurationUs > (targetDurationNs / 1000) * kJankCheckTimeFactor;
108         mRecords[mLatestRecordIndex] =
109                 CycleRecord{startIntervalUs, totalDurationUs, cycleMissed, FPSJitter};
110         mNumOfFrames++;
111         if (cycleMissed) {
112             mNumOfMissedCycles++;
113         }
114         updateFrameBuckets(totalDurationUs, cycleMissed, newFramesInBuckets);
115 
116         // Pop out the indexes that their related values are not greater than the
117         // latest one.
118         while (!mRecordsIndQueue.empty() &&
119                (mRecords[mRecordsIndQueue.back()].totalDurationUs <= totalDurationUs)) {
120             mRecordsIndQueue.pop_back();
121         }
122         mRecordsIndQueue.push_back(mLatestRecordIndex);
123 
124         mSumOfDurationsUs += totalDurationUs;
125         mAvgDurationUs = mSumOfDurationsUs / mNumOfFrames;
126     }
127 }
128 
getMaxDuration()129 std::optional<int32_t> SessionRecords::getMaxDuration() {
130     if (mRecordsIndQueue.empty()) {
131         return std::nullopt;
132     }
133     return mRecords[mRecordsIndQueue.front()].totalDurationUs;
134 }
135 
getAvgDuration()136 std::optional<int32_t> SessionRecords::getAvgDuration() {
137     if (mNumOfFrames <= 0) {
138         return std::nullopt;
139     }
140     return mAvgDurationUs;
141 }
142 
getNumOfRecords()143 int32_t SessionRecords::getNumOfRecords() {
144     return mNumOfFrames;
145 }
146 
getNumOfMissedCycles()147 int32_t SessionRecords::getNumOfMissedCycles() {
148     return mNumOfMissedCycles;
149 }
150 
isLowFrameRate(int32_t fpsLowRateThreshold)151 bool SessionRecords::isLowFrameRate(int32_t fpsLowRateThreshold) {
152     // Check the last three records. If all of their start delays are larger
153     // than the cycle duration threshold, return "true".
154     auto cycleDurationThresholdUs = 1000000.0 / fpsLowRateThreshold;
155     if (mNumOfFrames >= 3) {  // Todo: make this number as a tunable config
156         int32_t ind1 = mLatestRecordIndex;
157         int32_t ind2 = ind1 == 0 ? (kMaxNumOfRecords - 1) : (ind1 - 1);
158         int32_t ind3 = ind2 == 0 ? (kMaxNumOfRecords - 1) : (ind2 - 1);
159         return (mRecords[ind1].startIntervalUs >= cycleDurationThresholdUs) &&
160                (mRecords[ind2].startIntervalUs >= cycleDurationThresholdUs) &&
161                (mRecords[ind3].startIntervalUs >= cycleDurationThresholdUs);
162     }
163 
164     return false;
165 }
166 
resetRecords()167 void SessionRecords::resetRecords() {
168     mAvgDurationUs = 0;
169     mLastStartTimeNs = 0;
170     mLatestRecordIndex = -1;
171     mNumOfMissedCycles = 0;
172     mNumOfFrames = 0;
173     mSumOfDurationsUs = 0;
174     mRecordsIndQueue.clear();
175 }
176 
getLatestFPS() const177 int32_t SessionRecords::getLatestFPS() const {
178     return 1000000 * kTotalFramesForFPSCheck / mLatestStartIntervalSumUs;
179 }
180 
getNumOfFPSJitters() const181 int32_t SessionRecords::getNumOfFPSJitters() const {
182     return mNumOfFrameFPSJitters;
183 }
184 
updateFrameBuckets(int32_t frameDurationUs,bool isJankFrame,FrameBuckets & framesInBuckets)185 void SessionRecords::updateFrameBuckets(int32_t frameDurationUs, bool isJankFrame,
186                                         FrameBuckets &framesInBuckets) {
187     framesInBuckets.totalNumOfFrames++;
188     if (!isJankFrame || frameDurationUs < 17000) {
189         return;
190     }
191 
192     if (frameDurationUs < 25000) {
193         framesInBuckets.numOfFrames17to25ms++;
194     } else if (frameDurationUs < 34000) {
195         framesInBuckets.numOfFrames25to34ms++;
196     } else if (frameDurationUs < 67000) {
197         framesInBuckets.numOfFrames34to67ms++;
198     } else if (frameDurationUs < 100000) {
199         framesInBuckets.numOfFrames67to100ms++;
200     } else if (frameDurationUs >= 100000) {
201         framesInBuckets.numOfFramesOver100ms++;
202     }
203 }
204 
205 }  // namespace pixel
206 }  // namespace impl
207 }  // namespace power
208 }  // namespace hardware
209 }  // namespace google
210 }  // namespace aidl
211