You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
8.0 KiB
210 lines
8.0 KiB
/*
|
|
* 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 <gtest/gtest.h>
|
|
#include <gui/SurfaceComposerClient.h>
|
|
#include <gui/SurfaceControl.h>
|
|
#include <ui/Fence.h>
|
|
#include <utils/Timers.h>
|
|
#include <thread>
|
|
|
|
namespace android {
|
|
|
|
namespace {
|
|
|
|
struct CallbackData {
|
|
CallbackData() = default;
|
|
CallbackData(nsecs_t time, const sp<Fence>& fence,
|
|
const std::vector<SurfaceControlStats>& stats)
|
|
: latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
|
|
|
|
nsecs_t latchTime;
|
|
sp<Fence> presentFence;
|
|
std::vector<SurfaceControlStats> 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<SurfaceControl>& 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<sp<SurfaceControl>>& 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<SurfaceControl>& sc) const {
|
|
return std::hash<IBinder*>{}(sc->getHandle().get());
|
|
}
|
|
};
|
|
ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
|
|
nsecs_t mExpectedPresentTime = -1;
|
|
nsecs_t mExpectedPresentTimeForVsyncId = -1;
|
|
std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
|
|
};
|
|
|
|
class CallbackHelper {
|
|
public:
|
|
static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
|
|
const std::vector<SurfaceControlStats>& stats) {
|
|
if (!callbackContext) {
|
|
ALOGE("failed to get callback context");
|
|
}
|
|
CallbackHelper* helper = static_cast<CallbackHelper*>(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<void*>(this); }
|
|
|
|
std::mutex mMutex;
|
|
std::condition_variable mConditionVariable;
|
|
std::queue<CallbackData> mCallbackDataQueue;
|
|
};
|
|
}
|
|
} // namespace android
|