blob: 579a26ebf449937ca6872da8a7492df00b12995b [file] [log] [blame]
/*
* 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