/* * 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 #include "LayerTransactionTest.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& releaseFence, std::optional /*currentMaxAcquiredBufferCount*/) { if (!callbackContext) { FAIL() << "failed to get callback context"; } ReleaseBufferCallbackHelper* helper = static_cast(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(), 0U) << "extra callbacks received"; mCallbackDataQueue = {}; } android::ReleaseBufferCallback getCallback() { return std::bind(function, static_cast(this) /* callbackContext */, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); } std::mutex mMutex; std::condition_variable mConditionVariable; std::queue>> mCallbackDataQueue; }; class ReleaseBufferCallbackTest : public LayerTransactionTest { public: virtual sp createBufferStateLayer() { return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState); } static void submitBuffer(const sp& layer, sp buffer, sp fence, CallbackHelper& callback, const ReleaseCallbackId& id, ReleaseBufferCallbackHelper& releaseCallback) { Transaction t; t.setBuffer(layer, buffer, fence, id.framenumber, 0 /* producerId */, releaseCallback.getCallback()); 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& expectedReleaseBufferId) { ReleaseCallbackId actualReleaseBufferId; releaseCallback.getCallbackData(&actualReleaseBufferId); EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId); releaseCallback.verifyNoCallbacks(); } static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() { static std::vector sCallbacks; sCallbacks.emplace_back(new ReleaseBufferCallbackHelper()); return sCallbacks.back(); } static sp getBuffer() { return sp::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, 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 layer = createBufferStateLayer(); CallbackHelper transactionCallback; ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); // If a buffer is being presented, we should not emit a release callback. sp 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 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 layer = createBufferStateLayer(); CallbackHelper transactionCallback; ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); // If a buffer is being presented, we should not emit a release callback. sp 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 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 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 layer = createBufferStateLayer(); CallbackHelper* transactionCallback = new CallbackHelper(); ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); // If a buffer is being presented, we should not emit a release callback. sp 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 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 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 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 layer = createBufferStateLayer(); CallbackHelper transactionCallback; ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); // If a buffer is being presented, we should not emit a release callback. sp 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, std::nullopt, firstBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); 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 secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); t.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); 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)); } TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) { sp firstCompletedListener = sp::make(); sp secondCompletedListener = sp::make(); CallbackHelper callback1, callback2; TransactionCompletedListener::setInstance(firstCompletedListener); sp layer = createBufferStateLayer(); ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); sp firstBuffer = getBuffer(); ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); // Send initial buffer for the layer submitBuffer(layer, firstBuffer, Fence::NO_FENCE, callback1, firstBufferCallbackId, *releaseCallback); ExpectedResult expected; expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, ExpectedResult::Buffer::NOT_ACQUIRED); ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); // Sent a second buffer to allow the first buffer to get released. sp secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); Transaction transaction1; transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext()); // Set a different TransactionCompletedListener to mimic a second process TransactionCompletedListener::setInstance(secondCompletedListener); // Make sure the second "process" has a callback set up. Transaction transaction2; transaction2.addTransactionCompletedCallback(callback2.function, callback2.getContext()); // This merging order, merge transaction1 first then transaction2, seems to ensure the listener // for transaction2 is ordered first. This makes sure the wrong process is added first to the // layer's vector of listeners. With the bug, only the secondCompletedListener will get the // release callback id, since it's ordered first. Then firstCompletedListener would fail to get // the release callback id and not invoke the release callback. Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply(); expected = ExpectedResult(); expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, ExpectedResult::Buffer::NOT_ACQUIRED, ExpectedResult::PreviousBuffer::RELEASED); ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } TEST_F(ReleaseBufferCallbackTest, DISABLED_SetBuffer_OverwriteBuffers) { sp layer = createBufferStateLayer(); ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); sp firstBuffer = getBuffer(); ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); // Create transaction with a buffer. Transaction transaction; transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); sp secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); // Call setBuffer on the same transaction with a different buffer. transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) { sp layer = createBufferStateLayer(); ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); sp firstBuffer = getBuffer(); ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); // Create transaction with a buffer. Transaction transaction1; transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); sp secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); // Create a second transaction with a new buffer for the same layer. Transaction transaction2; transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); // merge transaction1 into transaction2 so ensure we get a proper buffer release callback. transaction1.merge(std::move(transaction2)); ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) { sp firstCompletedListener = sp::make(); sp secondCompletedListener = sp::make(); TransactionCompletedListener::setInstance(firstCompletedListener); sp layer = createBufferStateLayer(); ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); sp firstBuffer = getBuffer(); ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); Transaction transaction1; transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); // Sent a second buffer to allow the first buffer to get released. sp secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); Transaction transaction2; transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); // Set a different TransactionCompletedListener to mimic a second process TransactionCompletedListener::setInstance(secondCompletedListener); Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply(); // Make sure we can still get the release callback even though the merge happened in a different // process. ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } TEST_F(ReleaseBufferCallbackTest, SetBuffer_OverwriteBuffersWithNull) { sp layer = createBufferStateLayer(); ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); sp firstBuffer = getBuffer(); ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); // Create transaction with a buffer. Transaction transaction; transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, 0 /* producerId */, releaseCallback->getCallback()); // Call setBuffer on the same transaction with a null buffer. transaction.setBuffer(layer, nullptr, std::nullopt, 0, 0 /* producerId */, releaseCallback->getCallback()); ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } } // namespace android