/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include namespace android { namespace { struct CallbackData { CallbackData() = default; CallbackData(nsecs_t time, const sp& fence, const std::vector& stats) : latchTime(time), presentFence(fence), surfaceControlStats(stats) {} nsecs_t latchTime; sp presentFence; std::vector surfaceControlStats; }; class ExpectedResult { public: enum Transaction { NOT_PRESENTED = 0, PRESENTED, }; enum Buffer { NOT_ACQUIRED = 0, ACQUIRED, }; enum PreviousBuffer { NOT_RELEASED = 0, RELEASED, UNKNOWN, }; void reset() { mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; mExpectedSurfaceResults.clear(); } void addSurface(ExpectedResult::Transaction transactionResult, const sp& layer, ExpectedResult::Buffer bufferResult = ACQUIRED, ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { mTransactionResult = transactionResult; mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer), std::forward_as_tuple(bufferResult, previousBufferResult)); } void addSurfaces(ExpectedResult::Transaction transactionResult, const std::vector>& layers, ExpectedResult::Buffer bufferResult = ACQUIRED, ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { for (const auto& layer : layers) { addSurface(transactionResult, layer, bufferResult, previousBufferResult); } } void addExpectedPresentTime(nsecs_t expectedPresentTime) { mExpectedPresentTime = expectedPresentTime; } void addExpectedPresentTimeForVsyncId(nsecs_t expectedPresentTime) { mExpectedPresentTimeForVsyncId = expectedPresentTime; } void verifyCallbackData(const CallbackData& callbackData) const { const auto& [latchTime, presentFence, surfaceControlStats] = callbackData; if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) { ASSERT_GE(latchTime, 0) << "bad latch time"; ASSERT_NE(presentFence, nullptr); if (mExpectedPresentTime >= 0) { ASSERT_EQ(presentFence->wait(3000), NO_ERROR); ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6)); // if the panel is running at 30 hz, at the worst case, our expected time just // misses vsync and we have to wait another 33.3ms ASSERT_LE(presentFence->getSignalTime(), mExpectedPresentTime + nsecs_t(66.666666 * 1e6)); } else if (mExpectedPresentTimeForVsyncId >= 0) { ASSERT_EQ(presentFence->wait(3000), NO_ERROR); // We give 4ms for prediction error ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTimeForVsyncId - 4'000'000); } } else { ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented"; ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched"; } ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size()) << "wrong number of surfaces"; for (const auto& stats : surfaceControlStats) { ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control"; const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl); ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end()) << "unexpected surface control"; expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime); } } private: class ExpectedSurfaceResult { public: ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult, ExpectedResult::PreviousBuffer previousBufferResult) : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {} void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, nsecs_t latchTime) const { const auto& [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence, transformHint, frameEvents] = surfaceControlStats; ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) << "bad acquire time"; ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time"; if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) { ASSERT_NE(previousReleaseFence, nullptr) << "failed to set release prev buffer fence"; } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) { ASSERT_EQ(previousReleaseFence, nullptr) << "should not have set released prev buffer fence"; } } private: ExpectedResult::Buffer mBufferResult; ExpectedResult::PreviousBuffer mPreviousBufferResult; }; struct SCHash { std::size_t operator()(const sp& sc) const { return std::hash{}(sc->getHandle().get()); } }; ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; nsecs_t mExpectedPresentTime = -1; nsecs_t mExpectedPresentTimeForVsyncId = -1; std::unordered_map, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults; }; class CallbackHelper { public: static void function(void* callbackContext, nsecs_t latchTime, const sp& presentFence, const std::vector& stats) { if (!callbackContext) { ALOGE("failed to get callback context"); } CallbackHelper* helper = static_cast(callbackContext); std::lock_guard lock(helper->mMutex); helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats); helper->mConditionVariable.notify_all(); } void getCallbackData(CallbackData* outData) { std::unique_lock lock(mMutex); if (mCallbackDataQueue.empty()) { ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)), std::cv_status::timeout) << "did not receive callback"; } *outData = std::move(mCallbackDataQueue.front()); mCallbackDataQueue.pop(); } void verifyFinalState() { // Wait to see if there are extra callbacks std::this_thread::sleep_for(500ms); std::lock_guard lock(mMutex); EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; mCallbackDataQueue = {}; } void* getContext() { return static_cast(this); } std::mutex mMutex; std::condition_variable mConditionVariable; std::queue mCallbackDataQueue; }; } } // namespace android