| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2018 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 | #pragma once | 
 | 18 |  | 
 | 19 | #include <utils/Errors.h> | 
 | 20 |  | 
 | 21 | #include <mutex> | 
 | 22 |  | 
 | 23 | using namespace android::surfaceflinger; | 
 | 24 |  | 
 | 25 | namespace android { | 
 | 26 |  | 
 | 27 | /* | 
 | 28 |  * Modulates the vsync-offsets depending on current SurfaceFlinger state. | 
 | 29 |  */ | 
 | 30 | class VSyncModulator { | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 31 | private: | 
 | 32 |  | 
 | 33 |     // Number of frames we'll keep the early phase offsets once they are activated. This acts as a | 
 | 34 |     // low-pass filter in case the client isn't quick enough in sending new transactions. | 
 | 35 |     const int MIN_EARLY_FRAME_COUNT = 2; | 
 | 36 |  | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 37 | public: | 
 | 38 |  | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 39 |     struct Offsets { | 
 | 40 |         nsecs_t sf; | 
 | 41 |         nsecs_t app; | 
 | 42 |     }; | 
 | 43 |  | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 44 |     enum TransactionStart { | 
 | 45 |         EARLY, | 
 | 46 |         NORMAL | 
 | 47 |     }; | 
 | 48 |  | 
 | 49 |     // Sets the phase offsets | 
 | 50 |     // | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 51 |     // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction | 
 | 52 |     //          as early. May be the same as late, in which case we don't shift offsets. | 
 | 53 |     // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition | 
 | 54 |     //            and the transaction was marked as early, we'll use sfEarly. | 
 | 55 |     // sfLate: The regular SF vsync phase offset. | 
 | 56 |     // appEarly: Like sfEarly, but for the app-vsync | 
 | 57 |     // appEarlyGl: Like sfEarlyGl, but for the app-vsync. | 
 | 58 |     // appLate: The regular app vsync phase offset. | 
 | 59 |     void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) { | 
 | 60 |         mEarlyOffsets = early; | 
 | 61 |         mEarlyGlOffsets = earlyGl; | 
 | 62 |         mLateOffsets = late; | 
 | 63 |         mOffsets = late; | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 64 |     } | 
 | 65 |  | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 66 |     Offsets getEarlyOffsets() const { | 
 | 67 |         return mEarlyOffsets; | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 68 |     } | 
 | 69 |  | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 70 |     Offsets getEarlyGlOffsets() const { | 
 | 71 |         return mEarlyGlOffsets; | 
 | 72 |     } | 
 | 73 |  | 
 | 74 |     void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) { | 
 | 75 |         mSfEventThread = sfEventThread; | 
 | 76 |         mAppEventThread = appEventThread; | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 77 |     } | 
 | 78 |  | 
 | 79 |     void setTransactionStart(TransactionStart transactionStart) { | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 80 |  | 
 | 81 |         if (transactionStart == TransactionStart::EARLY) { | 
 | 82 |             mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT; | 
 | 83 |         } | 
 | 84 |  | 
| Jorim Jaggi | f15c3be | 2018-04-12 12:56:58 +0100 | [diff] [blame] | 85 |         // An early transaction stays an early transaction. | 
 | 86 |         if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) { | 
 | 87 |             return; | 
 | 88 |         } | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 89 |         mTransactionStart = transactionStart; | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 90 |         updateOffsets(); | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 91 |     } | 
 | 92 |  | 
| Jorim Jaggi | f15c3be | 2018-04-12 12:56:58 +0100 | [diff] [blame] | 93 |     void onTransactionHandled() { | 
 | 94 |         if (mTransactionStart == TransactionStart::NORMAL) return; | 
 | 95 |         mTransactionStart = TransactionStart::NORMAL; | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 96 |         updateOffsets(); | 
| Jorim Jaggi | f15c3be | 2018-04-12 12:56:58 +0100 | [diff] [blame] | 97 |     } | 
 | 98 |  | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 99 |     void onRefreshed(bool usedRenderEngine) { | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 100 |         bool updateOffsetsNeeded = false; | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 101 |         if (mRemainingEarlyFrameCount > 0) { | 
 | 102 |             mRemainingEarlyFrameCount--; | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 103 |             updateOffsetsNeeded = true; | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 104 |         } | 
 | 105 |         if (usedRenderEngine != mLastFrameUsedRenderEngine) { | 
 | 106 |             mLastFrameUsedRenderEngine = usedRenderEngine; | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 107 |             updateOffsetsNeeded = true; | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 108 |         } | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 109 |         if (updateOffsetsNeeded) { | 
 | 110 |             updateOffsets(); | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 111 |         } | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 112 |     } | 
 | 113 |  | 
 | 114 | private: | 
 | 115 |  | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 116 |     void updateOffsets() { | 
 | 117 |         const Offsets desired = getOffsets(); | 
 | 118 |         const Offsets current = mOffsets; | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 119 |  | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 120 |         bool changed = false; | 
 | 121 |         if (desired.sf != current.sf) { | 
 | 122 |             mSfEventThread->setPhaseOffset(desired.sf); | 
 | 123 |             changed = true; | 
 | 124 |         } | 
 | 125 |         if (desired.app != current.app) { | 
 | 126 |             mAppEventThread->setPhaseOffset(desired.app); | 
 | 127 |             changed = true; | 
 | 128 |         } | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 129 |  | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 130 |         if (changed) { | 
 | 131 |             mOffsets = desired; | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 132 |         } | 
 | 133 |     } | 
 | 134 |  | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 135 |     Offsets getOffsets() { | 
 | 136 |         if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) { | 
 | 137 |             return mEarlyOffsets; | 
 | 138 |         } else if (mLastFrameUsedRenderEngine) { | 
 | 139 |             return mEarlyGlOffsets; | 
 | 140 |         } else { | 
 | 141 |             return mLateOffsets; | 
 | 142 |         } | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 143 |     } | 
 | 144 |  | 
| Jorim Jaggi | e203e04 | 2018-06-19 15:57:08 +0200 | [diff] [blame] | 145 |     Offsets mLateOffsets; | 
 | 146 |     Offsets mEarlyOffsets; | 
 | 147 |     Offsets mEarlyGlOffsets; | 
 | 148 |  | 
 | 149 |     EventThread* mSfEventThread = nullptr; | 
 | 150 |     EventThread* mAppEventThread = nullptr; | 
 | 151 |  | 
 | 152 |     std::atomic<Offsets> mOffsets; | 
 | 153 |  | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 154 |     std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL; | 
 | 155 |     std::atomic<bool> mLastFrameUsedRenderEngine = false; | 
| Jorim Jaggi | 9053521 | 2018-05-23 23:44:06 +0200 | [diff] [blame] | 156 |     std::atomic<int> mRemainingEarlyFrameCount = 0; | 
| Dan Stoza | 2713c30 | 2018-03-28 17:07:36 -0700 | [diff] [blame] | 157 | }; | 
 | 158 |  | 
 | 159 | } // namespace android |