diff options
Diffstat (limited to 'libs/gui/tests/BLASTBufferQueue_test.cpp')
-rw-r--r-- | libs/gui/tests/BLASTBufferQueue_test.cpp | 349 |
1 files changed, 343 insertions, 6 deletions
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 9082d275a2..fc7548511d 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -27,6 +27,7 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SyncScreenCaptureListener.h> +#include <gui/test/CallbackUtils.h> #include <private/gui/ComposerService.h> #include <ui/DisplayMode.h> #include <ui/GraphicBuffer.h> @@ -42,6 +43,29 @@ namespace android { using Transaction = SurfaceComposerClient::Transaction; using android::hardware::graphics::common::V1_2::BufferUsage; +class CountProducerListener : public BnProducerListener { +public: + void onBufferReleased() override { + std::scoped_lock<std::mutex> lock(mMutex); + mNumReleased++; + mReleaseCallback.notify_one(); + } + + void waitOnNumberReleased(int32_t expectedNumReleased) { + std::unique_lock<std::mutex> lock(mMutex); + while (mNumReleased < expectedNumReleased) { + ASSERT_NE(mReleaseCallback.wait_for(lock, std::chrono::seconds(3)), + std::cv_status::timeout) + << "did not receive release"; + } + } + +private: + std::mutex mMutex; + std::condition_variable mReleaseCallback; + int32_t mNumReleased GUARDED_BY(mMutex) = 0; +}; + class BLASTBufferQueueHelper { public: BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) { @@ -152,18 +176,19 @@ protected: mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB; } - void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer) { + void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer, + int32_t maxBufferCount = 2) { producer = adapter.getIGraphicBufferProducer(); - setUpProducer(producer); + setUpProducer(producer, maxBufferCount); } - void setUpProducer(sp<IGraphicBufferProducer>& igbProducer) { + void setUpProducer(sp<IGraphicBufferProducer>& igbProducer, int32_t maxBufferCount) { ASSERT_NE(nullptr, igbProducer.get()); - ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2)); + ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(maxBufferCount)); IGraphicBufferProducer::QueueBufferOutput qbOutput; + mProducerListener = new CountProducerListener(); ASSERT_EQ(NO_ERROR, - igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, - &qbOutput)); + igbProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); } @@ -287,6 +312,7 @@ protected: DisplayCaptureArgs mCaptureArgs; ScreenCaptureResults mCaptureResults; + sp<CountProducerListener> mProducerListener; }; TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { @@ -672,6 +698,317 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) { /*border*/ 0, /*outsideRegion*/ true)); } +// b/196339769 verify we can can update the requested size while the in FREEZE scaling mode and +// scale the buffer properly when the mode changes to SCALE_TO_WINDOW +TEST_F(BLASTBufferQueueTest, ScalingModeChanges) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight / 4); + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight / 4, + PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); + + uint32_t* bufData; + buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN), + reinterpret_cast<void**>(&bufData)); + fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b); + buf->unlock(); + + IGraphicBufferProducer::QueueBufferOutput qbOutput; + IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */, + HAL_DATASPACE_UNKNOWN, {}, + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + igbProducer->queueBuffer(slot, input, &qbOutput); + adapter.waitForCallbacks(); + } + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, + {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 4})); + + // update the size to half the display and dequeue a buffer quarter of the display. + adapter.update(mSurfaceControl, mDisplayWidth, mDisplayHeight / 2); + + { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight / 8, + PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); + + uint32_t* bufData; + buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN), + reinterpret_cast<void**>(&bufData)); + g = 255; + fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b); + buf->unlock(); + + IGraphicBufferProducer::QueueBufferOutput qbOutput; + IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */, + HAL_DATASPACE_UNKNOWN, {}, + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, + 0, Fence::NO_FENCE); + igbProducer->queueBuffer(slot, input, &qbOutput); + adapter.waitForCallbacks(); + } + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + // verify we still scale the buffer to the new size (half the screen height) + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, + {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2})); +} + +TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction next; + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + // queue non sync buffer, so this one should get blocked + // Add a present delay to allow the first screenshot to get taken. + nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count(); + queueBuffer(igbProducer, r, g, b, presentTimeDelay); + + CallbackHelper transactionCallback; + next.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); + + mProducerListener->waitOnNumberReleased(1); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction mainTransaction; + + Transaction next; + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + + mainTransaction.merge(std::move(next)); + // Expect 1 buffer to be released even before sending to SurfaceFlinger + mProducerListener->waitOnNumberReleased(1); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction mainTransaction; + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue another buffer without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 1 buffer to be released because the non sync transaction should merge + // with the sync + mProducerListener->waitOnNumberReleased(1); + + mainTransaction.merge(std::move(next)); + // Expect 2 buffers to be released due to merging the two syncs. + mProducerListener->waitOnNumberReleased(2); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer, 3); + + Transaction mainTransaction; + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue a few buffers without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 3 buffers to be released because the non sync transactions should merge + // with the sync + mProducerListener->waitOnNumberReleased(3); + + mainTransaction.merge(std::move(next)); + // Expect 4 buffers to be released due to merging the two syncs. + mProducerListener->waitOnNumberReleased(4); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +// Tests BBQ with a sync transaction when the buffers acquired reaches max and the only way to +// continue processing is for a release callback from SurfaceFlinger. +// This is done by sending a buffer to SF so it can release the previous one and allow BBQ to +// continue acquiring buffers. +TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer, 4); + + Transaction mainTransaction; + + // Send a buffer to SF + queueBuffer(igbProducer, 0, 255, 0, 0); + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue a few buffers without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + + // apply the first synced buffer to ensure we have to wait on SF + mainTransaction.apply(); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 2 buffers to be released because the non sync transactions should merge + // with the sync + mProducerListener->waitOnNumberReleased(3); + + mainTransaction.merge(std::move(next)); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + class TestProducerListener : public BnProducerListener { public: sp<IGraphicBufferProducer> mIgbp; |