| /* |
| * 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 |