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.
337 lines
15 KiB
337 lines
15 KiB
/*
|
|
* Copyright (C) 2021 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.
|
|
*/
|
|
|
|
#include "LayerTransactionTest.h"
|
|
#include "utils/CallbackUtils.h"
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
namespace android {
|
|
|
|
using android::hardware::graphics::common::V1_1::BufferUsage;
|
|
|
|
::testing::Environment* const binderEnv =
|
|
::testing::AddGlobalTestEnvironment(new BinderEnvironment());
|
|
|
|
// b/181132765 - disabled until cuttlefish failures are investigated
|
|
class ReleaseBufferCallbackHelper {
|
|
public:
|
|
static void function(void* callbackContext, ReleaseCallbackId callbackId,
|
|
const sp<Fence>& releaseFence,
|
|
uint32_t /*currentMaxAcquiredBufferCount*/) {
|
|
if (!callbackContext) {
|
|
FAIL() << "failed to get callback context";
|
|
}
|
|
ReleaseBufferCallbackHelper* helper =
|
|
static_cast<ReleaseBufferCallbackHelper*>(callbackContext);
|
|
std::lock_guard lock(helper->mMutex);
|
|
helper->mCallbackDataQueue.emplace(callbackId, releaseFence);
|
|
helper->mConditionVariable.notify_all();
|
|
}
|
|
|
|
void getCallbackData(ReleaseCallbackId* callbackId) {
|
|
std::unique_lock lock(mMutex);
|
|
if (mCallbackDataQueue.empty()) {
|
|
if (!mConditionVariable.wait_for(lock, std::chrono::seconds(3),
|
|
[&] { return !mCallbackDataQueue.empty(); })) {
|
|
FAIL() << "failed to get releaseBuffer callback";
|
|
}
|
|
}
|
|
|
|
auto callbackData = mCallbackDataQueue.front();
|
|
mCallbackDataQueue.pop();
|
|
*callbackId = callbackData.first;
|
|
}
|
|
|
|
void verifyNoCallbacks() {
|
|
// Wait to see if there are extra callbacks
|
|
std::this_thread::sleep_for(300ms);
|
|
|
|
std::lock_guard lock(mMutex);
|
|
EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
|
|
mCallbackDataQueue = {};
|
|
}
|
|
|
|
android::ReleaseBufferCallback getCallback() {
|
|
return std::bind(function, static_cast<void*>(this) /* callbackContext */,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
|
}
|
|
|
|
std::mutex mMutex;
|
|
std::condition_variable mConditionVariable;
|
|
std::queue<std::pair<ReleaseCallbackId, sp<Fence>>> mCallbackDataQueue;
|
|
};
|
|
|
|
class ReleaseBufferCallbackTest : public LayerTransactionTest {
|
|
public:
|
|
virtual sp<SurfaceControl> createBufferStateLayer() {
|
|
return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
|
|
}
|
|
|
|
static void submitBuffer(const sp<SurfaceControl>& layer, sp<GraphicBuffer> buffer,
|
|
sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id,
|
|
ReleaseBufferCallbackHelper& releaseCallback) {
|
|
Transaction t;
|
|
t.setFrameNumber(layer, id.framenumber);
|
|
t.setBuffer(layer, buffer, id, releaseCallback.getCallback());
|
|
t.setAcquireFence(layer, fence);
|
|
t.addTransactionCompletedCallback(callback.function, callback.getContext());
|
|
t.apply();
|
|
}
|
|
|
|
static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult) {
|
|
CallbackData callbackData;
|
|
helper.getCallbackData(&callbackData);
|
|
expectedResult.verifyCallbackData(callbackData);
|
|
}
|
|
|
|
static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback,
|
|
const ReleaseCallbackId& expectedCallbackId) {
|
|
ReleaseCallbackId actualReleaseBufferId;
|
|
releaseCallback.getCallbackData(&actualReleaseBufferId);
|
|
EXPECT_EQ(expectedCallbackId, actualReleaseBufferId);
|
|
releaseCallback.verifyNoCallbacks();
|
|
}
|
|
static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() {
|
|
static std::vector<ReleaseBufferCallbackHelper*> sCallbacks;
|
|
sCallbacks.emplace_back(new ReleaseBufferCallbackHelper());
|
|
return sCallbacks.back();
|
|
}
|
|
|
|
static sp<GraphicBuffer> getBuffer() {
|
|
return new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
|
|
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
|
|
BufferUsage::COMPOSER_OVERLAY,
|
|
"test");
|
|
}
|
|
static uint64_t generateFrameNumber() {
|
|
static uint64_t sFrameNumber = 0;
|
|
return ++sFrameNumber;
|
|
}
|
|
};
|
|
|
|
TEST_F(ReleaseBufferCallbackTest, DISABLED_PresentBuffer) {
|
|
sp<SurfaceControl> layer = createBufferStateLayer();
|
|
CallbackHelper transactionCallback;
|
|
ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
|
|
|
|
// If a buffer is being presented, we should not emit a release callback.
|
|
sp<GraphicBuffer> firstBuffer = getBuffer();
|
|
ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
|
|
submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, firstBufferCallbackId,
|
|
*releaseCallback);
|
|
ExpectedResult expected;
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
|
|
EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
|
|
|
|
// if state doesn't change, no release callbacks are expected
|
|
Transaction t;
|
|
t.addTransactionCompletedCallback(transactionCallback.function,
|
|
transactionCallback.getContext());
|
|
t.apply();
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, ExpectedResult()));
|
|
EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
|
|
|
|
// If a presented buffer is replaced, we should emit a release callback for the
|
|
// previously presented buffer.
|
|
sp<GraphicBuffer> secondBuffer = getBuffer();
|
|
ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
|
|
submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, secondBufferCallbackId,
|
|
*releaseCallback);
|
|
expected = ExpectedResult();
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED,
|
|
ExpectedResult::PreviousBuffer::RELEASED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
|
|
ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
|
|
}
|
|
|
|
TEST_F(ReleaseBufferCallbackTest, DISABLED_OffScreenLayer) {
|
|
sp<SurfaceControl> layer = createBufferStateLayer();
|
|
|
|
CallbackHelper transactionCallback;
|
|
ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
|
|
|
|
// If a buffer is being presented, we should not emit a release callback.
|
|
sp<GraphicBuffer> firstBuffer = getBuffer();
|
|
ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
|
|
submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, firstBufferCallbackId,
|
|
*releaseCallback);
|
|
ExpectedResult expected;
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
|
|
releaseCallback->verifyNoCallbacks();
|
|
|
|
// If a layer is parented offscreen then it should not emit a callback since sf still owns
|
|
// the buffer and can render it again.
|
|
Transaction t;
|
|
t.reparent(layer, nullptr);
|
|
t.addTransactionCompletedCallback(transactionCallback.function,
|
|
transactionCallback.getContext());
|
|
t.apply();
|
|
expected = ExpectedResult();
|
|
expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED,
|
|
ExpectedResult::PreviousBuffer::NOT_RELEASED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
|
|
ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
|
|
|
|
// If a presented buffer is replaced, we should emit a release callback for the
|
|
// previously presented buffer.
|
|
sp<GraphicBuffer> secondBuffer = getBuffer();
|
|
ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
|
|
submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, secondBufferCallbackId,
|
|
*releaseCallback);
|
|
expected = ExpectedResult();
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED,
|
|
ExpectedResult::PreviousBuffer::NOT_RELEASED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
|
|
ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
|
|
|
|
// If continue to submit buffer we continue to get release callbacks
|
|
sp<GraphicBuffer> thirdBuffer = getBuffer();
|
|
ReleaseCallbackId thirdBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
|
|
submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, thirdBufferCallbackId,
|
|
*releaseCallback);
|
|
expected = ExpectedResult();
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED,
|
|
ExpectedResult::PreviousBuffer::NOT_RELEASED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
|
|
ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBufferCallbackId));
|
|
}
|
|
|
|
TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_layerdestroy) {
|
|
sp<SurfaceControl> layer = createBufferStateLayer();
|
|
CallbackHelper* transactionCallback = new CallbackHelper();
|
|
ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
|
|
|
|
// If a buffer is being presented, we should not emit a release callback.
|
|
sp<GraphicBuffer> firstBuffer = getBuffer();
|
|
ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
|
|
submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, firstBufferCallbackId,
|
|
*releaseCallback);
|
|
{
|
|
ExpectedResult expected;
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
|
|
ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
|
|
}
|
|
|
|
// Destroying a currently presenting layer emits a callback.
|
|
Transaction t;
|
|
t.reparent(layer, nullptr);
|
|
t.apply();
|
|
layer = nullptr;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
|
|
}
|
|
|
|
// Destroying a never presented layer emits a callback.
|
|
TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_OffScreenLayerDestroy) {
|
|
sp<SurfaceControl> layer = createBufferStateLayer();
|
|
|
|
// make layer offscreen
|
|
Transaction t;
|
|
t.reparent(layer, nullptr);
|
|
t.apply();
|
|
|
|
CallbackHelper* transactionCallback = new CallbackHelper();
|
|
ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
|
|
|
|
// Submitting a buffer does not emit a callback.
|
|
sp<GraphicBuffer> firstBuffer = getBuffer();
|
|
ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
|
|
submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, firstBufferCallbackId,
|
|
*releaseCallback);
|
|
{
|
|
ExpectedResult expected;
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
|
|
ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
|
|
}
|
|
|
|
// Submitting a second buffer will replace the drawing state buffer and emit a callback.
|
|
sp<GraphicBuffer> secondBuffer = getBuffer();
|
|
ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
|
|
submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, secondBufferCallbackId,
|
|
*releaseCallback);
|
|
{
|
|
ExpectedResult expected;
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
|
|
}
|
|
|
|
// Destroying the offscreen layer emits a callback.
|
|
layer = nullptr;
|
|
ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBufferCallbackId));
|
|
}
|
|
|
|
TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) {
|
|
sp<SurfaceControl> layer = createBufferStateLayer();
|
|
CallbackHelper transactionCallback;
|
|
ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
|
|
|
|
// If a buffer is being presented, we should not emit a release callback.
|
|
sp<GraphicBuffer> firstBuffer = getBuffer();
|
|
ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
|
|
|
|
// Try to present 100ms in the future
|
|
nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
|
|
|
|
Transaction t;
|
|
t.setBuffer(layer, firstBuffer, firstBufferCallbackId, releaseCallback->getCallback());
|
|
t.setAcquireFence(layer, Fence::NO_FENCE);
|
|
t.addTransactionCompletedCallback(transactionCallback.function,
|
|
transactionCallback.getContext());
|
|
t.setDesiredPresentTime(time);
|
|
t.apply();
|
|
|
|
ExpectedResult expected;
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
|
|
EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
|
|
|
|
// Dropping frames in transaction queue emits a callback
|
|
sp<GraphicBuffer> secondBuffer = getBuffer();
|
|
ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
|
|
t.setBuffer(layer, secondBuffer, secondBufferCallbackId, releaseCallback->getCallback());
|
|
t.setAcquireFence(layer, Fence::NO_FENCE);
|
|
t.addTransactionCompletedCallback(transactionCallback.function,
|
|
transactionCallback.getContext());
|
|
t.setDesiredPresentTime(time);
|
|
t.apply();
|
|
|
|
expected = ExpectedResult();
|
|
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
|
|
ExpectedResult::Buffer::NOT_ACQUIRED,
|
|
ExpectedResult::PreviousBuffer::RELEASED);
|
|
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
|
|
ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
|
|
}
|
|
|
|
} // namespace android
|