| Valerie Hau | e9137b7 | 2019-08-27 13:22:18 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2019 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 | #pragma once | 
|  | 17 |  | 
|  | 18 | #include <gtest/gtest.h> | 
|  | 19 | #include <gui/SurfaceComposerClient.h> | 
|  | 20 | #include <gui/SurfaceControl.h> | 
|  | 21 | #include <ui/Fence.h> | 
|  | 22 | #include <utils/Timers.h> | 
|  | 23 | #include <thread> | 
|  | 24 |  | 
|  | 25 | namespace android { | 
|  | 26 |  | 
|  | 27 | namespace { | 
|  | 28 |  | 
|  | 29 | struct CallbackData { | 
|  | 30 | CallbackData() = default; | 
|  | 31 | CallbackData(nsecs_t time, const sp<Fence>& fence, | 
|  | 32 | const std::vector<SurfaceControlStats>& stats) | 
|  | 33 | : latchTime(time), presentFence(fence), surfaceControlStats(stats) {} | 
|  | 34 |  | 
|  | 35 | nsecs_t latchTime; | 
|  | 36 | sp<Fence> presentFence; | 
|  | 37 | std::vector<SurfaceControlStats> surfaceControlStats; | 
|  | 38 | }; | 
|  | 39 |  | 
|  | 40 | class ExpectedResult { | 
|  | 41 | public: | 
|  | 42 | enum Transaction { | 
|  | 43 | NOT_PRESENTED = 0, | 
|  | 44 | PRESENTED, | 
|  | 45 | }; | 
|  | 46 |  | 
|  | 47 | enum Buffer { | 
|  | 48 | NOT_ACQUIRED = 0, | 
|  | 49 | ACQUIRED, | 
|  | 50 | }; | 
|  | 51 |  | 
|  | 52 | enum PreviousBuffer { | 
|  | 53 | NOT_RELEASED = 0, | 
|  | 54 | RELEASED, | 
|  | 55 | UNKNOWN, | 
|  | 56 | }; | 
|  | 57 |  | 
|  | 58 | void reset() { | 
|  | 59 | mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; | 
|  | 60 | mExpectedSurfaceResults.clear(); | 
|  | 61 | } | 
|  | 62 |  | 
|  | 63 | void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer, | 
|  | 64 | ExpectedResult::Buffer bufferResult = ACQUIRED, | 
|  | 65 | ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { | 
|  | 66 | mTransactionResult = transactionResult; | 
|  | 67 | mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer), | 
|  | 68 | std::forward_as_tuple(bufferResult, previousBufferResult)); | 
|  | 69 | } | 
|  | 70 |  | 
|  | 71 | void addSurfaces(ExpectedResult::Transaction transactionResult, | 
|  | 72 | const std::vector<sp<SurfaceControl>>& layers, | 
|  | 73 | ExpectedResult::Buffer bufferResult = ACQUIRED, | 
|  | 74 | ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { | 
|  | 75 | for (const auto& layer : layers) { | 
|  | 76 | addSurface(transactionResult, layer, bufferResult, previousBufferResult); | 
|  | 77 | } | 
|  | 78 | } | 
|  | 79 |  | 
|  | 80 | void addExpectedPresentTime(nsecs_t expectedPresentTime) { | 
|  | 81 | mExpectedPresentTime = expectedPresentTime; | 
|  | 82 | } | 
|  | 83 |  | 
| Ady Abraham | 29d16cb | 2021-03-08 13:19:21 -0800 | [diff] [blame] | 84 | void addExpectedPresentTimeForVsyncId(nsecs_t expectedPresentTime) { | 
|  | 85 | mExpectedPresentTimeForVsyncId = expectedPresentTime; | 
|  | 86 | } | 
|  | 87 |  | 
| Valerie Hau | e9137b7 | 2019-08-27 13:22:18 -0700 | [diff] [blame] | 88 | void verifyCallbackData(const CallbackData& callbackData) const { | 
|  | 89 | const auto& [latchTime, presentFence, surfaceControlStats] = callbackData; | 
|  | 90 | if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) { | 
|  | 91 | ASSERT_GE(latchTime, 0) << "bad latch time"; | 
|  | 92 | ASSERT_NE(presentFence, nullptr); | 
|  | 93 | if (mExpectedPresentTime >= 0) { | 
|  | 94 | ASSERT_EQ(presentFence->wait(3000), NO_ERROR); | 
|  | 95 | ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6)); | 
|  | 96 | // if the panel is running at 30 hz, at the worst case, our expected time just | 
|  | 97 | // misses vsync and we have to wait another 33.3ms | 
|  | 98 | ASSERT_LE(presentFence->getSignalTime(), | 
|  | 99 | mExpectedPresentTime + nsecs_t(66.666666 * 1e6)); | 
| Ady Abraham | 29d16cb | 2021-03-08 13:19:21 -0800 | [diff] [blame] | 100 | } else if (mExpectedPresentTimeForVsyncId >= 0) { | 
|  | 101 | ASSERT_EQ(presentFence->wait(3000), NO_ERROR); | 
|  | 102 | // We give 4ms for prediction error | 
|  | 103 | ASSERT_GE(presentFence->getSignalTime(), | 
|  | 104 | mExpectedPresentTimeForVsyncId - 4'000'000); | 
| Valerie Hau | e9137b7 | 2019-08-27 13:22:18 -0700 | [diff] [blame] | 105 | } | 
|  | 106 | } else { | 
|  | 107 | ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented"; | 
|  | 108 | ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched"; | 
|  | 109 | } | 
|  | 110 |  | 
|  | 111 | ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size()) | 
|  | 112 | << "wrong number of surfaces"; | 
|  | 113 |  | 
|  | 114 | for (const auto& stats : surfaceControlStats) { | 
|  | 115 | ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control"; | 
|  | 116 |  | 
|  | 117 | const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl); | 
|  | 118 | ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end()) | 
|  | 119 | << "unexpected surface control"; | 
|  | 120 | expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime); | 
|  | 121 | } | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 | private: | 
|  | 125 | class ExpectedSurfaceResult { | 
|  | 126 | public: | 
|  | 127 | ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult, | 
|  | 128 | ExpectedResult::PreviousBuffer previousBufferResult) | 
|  | 129 | : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {} | 
|  | 130 |  | 
|  | 131 | void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, | 
|  | 132 | nsecs_t latchTime) const { | 
| Valerie Hau | 871d635 | 2020-01-29 08:44:02 -0800 | [diff] [blame] | 133 | const auto& | 
|  | 134 | [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence, | 
|  | 135 | transformHint, | 
|  | 136 | frameEvents] = surfaceControlStats; | 
| Valerie Hau | e9137b7 | 2019-08-27 13:22:18 -0700 | [diff] [blame] | 137 |  | 
|  | 138 | ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) | 
|  | 139 | << "bad acquire time"; | 
|  | 140 | ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time"; | 
|  | 141 |  | 
|  | 142 | if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) { | 
|  | 143 | ASSERT_NE(previousReleaseFence, nullptr) | 
|  | 144 | << "failed to set release prev buffer fence"; | 
|  | 145 | } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) { | 
|  | 146 | ASSERT_EQ(previousReleaseFence, nullptr) | 
|  | 147 | << "should not have set released prev buffer fence"; | 
|  | 148 | } | 
|  | 149 | } | 
|  | 150 |  | 
|  | 151 | private: | 
|  | 152 | ExpectedResult::Buffer mBufferResult; | 
|  | 153 | ExpectedResult::PreviousBuffer mPreviousBufferResult; | 
|  | 154 | }; | 
|  | 155 |  | 
|  | 156 | struct SCHash { | 
|  | 157 | std::size_t operator()(const sp<SurfaceControl>& sc) const { | 
|  | 158 | return std::hash<IBinder*>{}(sc->getHandle().get()); | 
|  | 159 | } | 
|  | 160 | }; | 
|  | 161 | ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; | 
|  | 162 | nsecs_t mExpectedPresentTime = -1; | 
| Ady Abraham | 29d16cb | 2021-03-08 13:19:21 -0800 | [diff] [blame] | 163 | nsecs_t mExpectedPresentTimeForVsyncId = -1; | 
| Valerie Hau | e9137b7 | 2019-08-27 13:22:18 -0700 | [diff] [blame] | 164 | std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults; | 
|  | 165 | }; | 
|  | 166 |  | 
|  | 167 | class CallbackHelper { | 
|  | 168 | public: | 
|  | 169 | static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence, | 
|  | 170 | const std::vector<SurfaceControlStats>& stats) { | 
|  | 171 | if (!callbackContext) { | 
|  | 172 | ALOGE("failed to get callback context"); | 
|  | 173 | } | 
|  | 174 | CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext); | 
|  | 175 | std::lock_guard lock(helper->mMutex); | 
|  | 176 | helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats); | 
|  | 177 | helper->mConditionVariable.notify_all(); | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 | void getCallbackData(CallbackData* outData) { | 
|  | 181 | std::unique_lock lock(mMutex); | 
|  | 182 |  | 
|  | 183 | if (mCallbackDataQueue.empty()) { | 
|  | 184 | ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)), | 
|  | 185 | std::cv_status::timeout) | 
|  | 186 | << "did not receive callback"; | 
|  | 187 | } | 
|  | 188 |  | 
|  | 189 | *outData = std::move(mCallbackDataQueue.front()); | 
|  | 190 | mCallbackDataQueue.pop(); | 
|  | 191 | } | 
|  | 192 |  | 
|  | 193 | void verifyFinalState() { | 
|  | 194 | // Wait to see if there are extra callbacks | 
|  | 195 | std::this_thread::sleep_for(500ms); | 
|  | 196 |  | 
|  | 197 | std::lock_guard lock(mMutex); | 
|  | 198 | EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; | 
|  | 199 | mCallbackDataQueue = {}; | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | void* getContext() { return static_cast<void*>(this); } | 
|  | 203 |  | 
|  | 204 | std::mutex mMutex; | 
|  | 205 | std::condition_variable mConditionVariable; | 
|  | 206 | std::queue<CallbackData> mCallbackDataQueue; | 
|  | 207 | }; | 
|  | 208 | } | 
|  | 209 | } // namespace android |