/* * Copyright (C) 2023 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "GraphicsTracker_test" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using ::aidl::android::hardware::media::c2::implementation::GraphicsTracker; using ::android::BufferItem; using ::android::BufferQueue; using ::android::Fence; using ::android::GraphicBuffer; using ::android::IGraphicBufferProducer; using ::android::IGraphicBufferConsumer; using ::android::IProducerListener; using ::android::IConsumerListener; using ::android::OK; using ::android::sp; using ::android::wp; namespace { struct BqStatistics { std::atomic mDequeued; std::atomic mQueued; std::atomic mBlocked; std::atomic mDropped; std::atomic mDiscarded; std::atomic mReleased; void log() { ALOGD("Dequeued: %d, Queued: %d, Blocked: %d, " "Dropped: %d, Discarded %d, Released %d", (int)mDequeued, (int)mQueued, (int)mBlocked, (int)mDropped, (int)mDiscarded, (int)mReleased); } void clear() { mDequeued = 0; mQueued = 0; mBlocked = 0; mDropped = 0; mDiscarded = 0; mReleased = 0; } }; struct DummyConsumerListener : public android::BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} }; struct TestConsumerListener : public android::BnConsumerListener { TestConsumerListener(const sp &consumer) : BnConsumerListener(), mConsumer(consumer) {} void onFrameAvailable(const BufferItem&) override { constexpr static int kRenderDelayUs = 1000000/30; // 30fps BufferItem buffer; // consume buffer sp consumer = mConsumer.promote(); if (consumer != nullptr && consumer->acquireBuffer(&buffer, 0) == android::NO_ERROR) { ::usleep(kRenderDelayUs); consumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, buffer.mFence); } } void onBuffersReleased() override {} void onSidebandStreamChanged() override {} wp mConsumer; }; struct TestProducerListener : public android::BnProducerListener { TestProducerListener(std::shared_ptr tracker, std::shared_ptr &stat, uint32_t generation) : BnProducerListener(), mTracker(tracker), mStat(stat), mGeneration(generation) {} virtual void onBufferReleased() override { auto tracker = mTracker.lock(); if (tracker) { mStat->mReleased++; tracker->onReleased(mGeneration); } } virtual bool needsReleaseNotify() override { return true; } virtual void onBuffersDiscarded(const std::vector&) override {} std::weak_ptr mTracker; std::shared_ptr mStat; uint32_t mGeneration; }; struct Frame { AHardwareBuffer *buffer_; sp fence_; Frame() : buffer_{nullptr}, fence_{nullptr} {} Frame(AHardwareBuffer *buffer, sp fence) : buffer_(buffer), fence_(fence) {} ~Frame() { if (buffer_) { AHardwareBuffer_release(buffer_); } } }; struct FrameQueue { bool mStopped; bool mDrain; std::queue> mQueue; std::mutex mMutex; std::condition_variable mCond; FrameQueue() : mStopped{false}, mDrain{false} {} bool queueItem(AHardwareBuffer *buffer, sp fence) { std::shared_ptr frame = std::make_shared(buffer, fence); if (mStopped) { return false; } if (!frame) { return false; } std::unique_lock l(mMutex); mQueue.emplace(frame); l.unlock(); mCond.notify_all(); return true; } void stop(bool drain = false) { bool stopped = false; { std::unique_lock l(mMutex); if (!mStopped) { mStopped = true; mDrain = drain; stopped = true; } l.unlock(); if (stopped) { mCond.notify_all(); } } } bool waitItem(std::shared_ptr *frame) { while(true) { std::unique_lock l(mMutex); if (!mDrain && mStopped) { // stop without consuming the queue. return false; } if (!mQueue.empty()) { *frame = mQueue.front(); mQueue.pop(); return true; } else if (mStopped) { // stop after consuming the queue. return false; } mCond.wait(l); } } }; } // namespace anonymous class GraphicsTrackerTest : public ::testing::Test { public: const uint64_t kTestUsageFlag = GRALLOC_USAGE_SW_WRITE_OFTEN; void queueBuffer(FrameQueue *queue) { while (true) { std::shared_ptr frame; if (!queue->waitItem(&frame)) { break; } uint64_t bid; if (__builtin_available(android __ANDROID_API_T__, *)) { if (AHardwareBuffer_getId(frame->buffer_, &bid) != android::NO_ERROR) { break; } } else { break; } android::status_t ret = frame->fence_->wait(-1); if (ret != android::NO_ERROR) { mTracker->deallocate(bid, frame->fence_); mBqStat->mDiscarded++; continue; } std::shared_ptr blk = _C2BlockFactory::CreateGraphicBlock(frame->buffer_); if (!blk) { mTracker->deallocate(bid, Fence::NO_FENCE); mBqStat->mDiscarded++; continue; } IGraphicBufferProducer::QueueBufferInput input( 0, false, HAL_DATASPACE_UNKNOWN, android::Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferOutput output{}; c2_status_t res = mTracker->render( blk->share(C2Rect(1, 1), C2Fence()), input, &output); if (res != C2_OK) { mTracker->deallocate(bid, Fence::NO_FENCE); mBqStat->mDiscarded++; continue; } if (output.bufferReplaced) { mBqStat->mDropped++; } mBqStat->mQueued++; } } void stopTrackerAfterUs(int us) { ::usleep(us); mTracker->stop(); } protected: bool init(int maxDequeueCount) { mTracker = GraphicsTracker::CreateGraphicsTracker(maxDequeueCount); if (!mTracker) { return false; } BufferQueue::createBufferQueue(&mProducer, &mConsumer); if (!mProducer || !mConsumer) { return false; } return true; } bool configure(sp producerListener, sp consumerListener, int maxAcquiredCount = 1, bool controlledByApp = true) { if (mConsumer->consumerConnect( consumerListener, controlledByApp) != ::android::NO_ERROR) { return false; } if (mConsumer->setMaxAcquiredBufferCount(maxAcquiredCount) != ::android::NO_ERROR) { return false; } IGraphicBufferProducer::QueueBufferOutput qbo{}; if (mProducer->connect(producerListener, NATIVE_WINDOW_API_MEDIA, true, &qbo) != ::android::NO_ERROR) { return false; } if (mProducer->setDequeueTimeout(0) != ::android::NO_ERROR) { return false; } return true; } virtual void TearDown() override { mBqStat->log(); mBqStat->clear(); if (mTracker) { mTracker->stop(); mTracker.reset(); } if (mProducer) { mProducer->disconnect(NATIVE_WINDOW_API_MEDIA); } mProducer.clear(); mConsumer.clear(); } protected: std::shared_ptr mBqStat = std::make_shared(); sp mProducer; sp mConsumer; std::shared_ptr mTracker; }; TEST_F(GraphicsTrackerTest, AllocateAndBlockedTest) { uint32_t generation = 1; const int maxDequeueCount = 10; ASSERT_TRUE(init(maxDequeueCount)); ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new DummyConsumerListener())); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); c2_status_t ret = mTracker->configureGraphics(mProducer, generation); ASSERT_EQ(C2_OK, ret); ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable()); AHardwareBuffer *buf; sp fence; uint64_t bid; // Allocate and check dequeueable if (__builtin_available(android __ANDROID_API_T__, *)) { for (int i = 0; i < maxDequeueCount; ++i) { ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence); ASSERT_EQ(C2_OK, ret); mBqStat->mDequeued++; ASSERT_EQ(maxDequeueCount - (i + 1), mTracker->getCurDequeueable()); ASSERT_EQ(OK, AHardwareBuffer_getId(buf, &bid)); ALOGD("alloced : bufferId: %llu", (unsigned long long)bid); AHardwareBuffer_release(buf); } } else { GTEST_SKIP(); } // Allocate should be blocked ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence); ALOGD("alloc : err(%d, %d)", ret, C2_BLOCKING); ASSERT_EQ(C2_BLOCKING, ret); mBqStat->mBlocked++; ASSERT_EQ(0, mTracker->getCurDequeueable()); } TEST_F(GraphicsTrackerTest, AllocateAndDeallocateTest) { uint32_t generation = 1; const int maxDequeueCount = 10; ASSERT_TRUE(init(maxDequeueCount)); ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new DummyConsumerListener())); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); c2_status_t ret = mTracker->configureGraphics(mProducer, generation); ASSERT_EQ(C2_OK, ret); ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable()); AHardwareBuffer *buf; sp fence; uint64_t bid; std::vector bids; // Allocate and store buffer id if (__builtin_available(android __ANDROID_API_T__, *)) { for (int i = 0; i < maxDequeueCount; ++i) { ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence); ASSERT_EQ(C2_OK, ret); mBqStat->mDequeued++; ASSERT_EQ(OK, AHardwareBuffer_getId(buf, &bid)); bids.push_back(bid); ALOGD("alloced : bufferId: %llu", (unsigned long long)bid); AHardwareBuffer_release(buf); } } else { GTEST_SKIP(); } // Deallocate and check dequeueable for (int i = 0; i < maxDequeueCount; ++i) { ALOGD("dealloc : bufferId: %llu", (unsigned long long)bids[i]); ret = mTracker->deallocate(bids[i], Fence::NO_FENCE); ASSERT_EQ(C2_OK, ret); ASSERT_EQ(i + 1, mTracker->getCurDequeueable()); mBqStat->mDiscarded++; } } TEST_F(GraphicsTrackerTest, DropAndReleaseTest) { uint32_t generation = 1; const int maxDequeueCount = 10; ASSERT_TRUE(init(maxDequeueCount)); ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new DummyConsumerListener())); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); c2_status_t ret = mTracker->configureGraphics(mProducer, generation); ASSERT_EQ(C2_OK, ret); ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable()); FrameQueue frameQueue; std::thread queueThread(&GraphicsTrackerTest::queueBuffer, this, &frameQueue); AHardwareBuffer *buf1, *buf2; sp fence1, fence2; ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf1, &fence1); ASSERT_EQ(C2_OK, ret); mBqStat->mDequeued++; ASSERT_EQ(maxDequeueCount - 1, mTracker->getCurDequeueable()); ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf2, &fence2); ASSERT_EQ(C2_OK, ret); mBqStat->mDequeued++; ASSERT_EQ(maxDequeueCount - 2, mTracker->getCurDequeueable()); // Queue two buffers without consuming, one should be dropped ASSERT_TRUE(frameQueue.queueItem(buf1, fence1)); ASSERT_TRUE(frameQueue.queueItem(buf2, fence2)); frameQueue.stop(true); if (queueThread.joinable()) { queueThread.join(); } ASSERT_EQ(maxDequeueCount - 1, mTracker->getCurDequeueable()); // Consume one buffer and release BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence)); // Nothing to consume ASSERT_NE(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable()); ASSERT_EQ(1, mBqStat->mReleased); ASSERT_EQ(1, mBqStat->mDropped); } TEST_F(GraphicsTrackerTest, RenderTest) { uint32_t generation = 1; const int maxDequeueCount = 10; const int maxNumAlloc = 20; ASSERT_TRUE(init(maxDequeueCount)); ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new TestConsumerListener(mConsumer), 1, false)); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation)); ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount)); int waitFd = -1; ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd)); C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd); FrameQueue frameQueue; std::thread queueThread(&GraphicsTrackerTest::queueBuffer, this, &frameQueue); int numAlloc = 0; while (numAlloc < maxNumAlloc) { AHardwareBuffer *buf; sp fence; c2_status_t ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence); if (ret == C2_BLOCKING) { mBqStat->mBlocked++; c2_status_t waitRes = waitFence.wait(3000000000); if (waitRes == C2_TIMED_OUT || waitRes == C2_OK) { continue; } ALOGE("alloc wait failed: c2_err(%d)", waitRes); break; } if (ret != C2_OK) { ALOGE("alloc error: c2_err(%d)", ret); break; } mBqStat->mDequeued++; if (!frameQueue.queueItem(buf, fence)) { ALOGE("queue to render failed"); break; } ++numAlloc; } frameQueue.stop(true); // Wait more than enough time(1 sec) to render all queued frames for sure. ::usleep(1000000); if (queueThread.joinable()) { queueThread.join(); } ASSERT_EQ(numAlloc, maxNumAlloc); ASSERT_EQ(numAlloc, mBqStat->mDequeued); ASSERT_EQ(mBqStat->mDequeued, mBqStat->mQueued); ASSERT_EQ(mBqStat->mDequeued, mBqStat->mReleased + mBqStat->mDropped); } TEST_F(GraphicsTrackerTest, StopAndWaitTest) { uint32_t generation = 1; const int maxDequeueCount = 2; ASSERT_TRUE(init(maxDequeueCount)); ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new TestConsumerListener(mConsumer), 1, false)); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation)); ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount)); int waitFd = -1; ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd)); C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd); AHardwareBuffer *buf1, *buf2; sp fence; ASSERT_EQ(C2_OK, mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf1, &fence)); mBqStat->mDequeued++; AHardwareBuffer_release(buf1); ASSERT_EQ(C2_OK, mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf2, &fence)); mBqStat->mDequeued++; AHardwareBuffer_release(buf2); ASSERT_EQ(0, mTracker->getCurDequeueable()); ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(3000000000)); std::thread stopThread(&GraphicsTrackerTest::stopTrackerAfterUs, this, 500000); ASSERT_EQ(C2_BAD_STATE, waitFence.wait(3000000000)); if (stopThread.joinable()) { stopThread.join(); } } TEST_F(GraphicsTrackerTest, SurfaceChangeTest) { uint32_t generation = 1; const int maxDequeueCount = 10; const int maxNumAlloc = 20; const int firstPassAlloc = 12; const int firstPassRender = 8; ASSERT_TRUE(init(maxDequeueCount)); ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new TestConsumerListener(mConsumer), 1, false)); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation)); ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount)); int waitFd = -1; ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd)); C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd); AHardwareBuffer *bufs[maxNumAlloc]; sp fences[maxNumAlloc]; FrameQueue frameQueue; std::thread queueThread(&GraphicsTrackerTest::queueBuffer, this, &frameQueue); int numAlloc = 0; for (int i = 0; i < firstPassRender; ++i) { ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &bufs[i], &fences[i])); mBqStat->mDequeued++; numAlloc++; ASSERT_EQ(true, frameQueue.queueItem(bufs[i], fences[i])); } while (numAlloc < firstPassAlloc) { c2_status_t ret = mTracker->allocate( 0, 0, 0, kTestUsageFlag, &bufs[numAlloc], &fences[numAlloc]); if (ret == C2_BLOCKING) { mBqStat->mBlocked++; c2_status_t waitRes = waitFence.wait(3000000000); if (waitRes == C2_TIMED_OUT || waitRes == C2_OK) { continue; } ALOGE("alloc wait failed: c2_err(%d)", waitRes); break; } if (ret != C2_OK) { ALOGE("alloc error: c2_err(%d)", ret); break; } mBqStat->mDequeued++; numAlloc++; } ASSERT_EQ(numAlloc, firstPassAlloc); // switching surface sp oldProducer = mProducer; sp oldConsumer = mConsumer; mProducer.clear(); mConsumer.clear(); BufferQueue::createBufferQueue(&mProducer, &mConsumer); ASSERT_TRUE((bool)mProducer && (bool)mConsumer); generation += 1; ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new TestConsumerListener(mConsumer), 1, false)); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation)); ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount)); ASSERT_EQ(OK, oldProducer->disconnect(NATIVE_WINDOW_API_MEDIA)); oldProducer.clear(); oldConsumer.clear(); for (int i = firstPassRender ; i < firstPassAlloc; ++i) { ASSERT_EQ(true, frameQueue.queueItem(bufs[i], fences[i])); } while (numAlloc < maxNumAlloc) { AHardwareBuffer *buf; sp fence; c2_status_t ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence); if (ret == C2_BLOCKING) { mBqStat->mBlocked++; c2_status_t waitRes = waitFence.wait(3000000000); if (waitRes == C2_TIMED_OUT || waitRes == C2_OK) { continue; } ALOGE("alloc wait failed: c2_err(%d)", waitRes); break; } if (ret != C2_OK) { ALOGE("alloc error: c2_err(%d)", ret); break; } mBqStat->mDequeued++; if (!frameQueue.queueItem(buf, fence)) { ALOGE("queue to render failed"); break; } ++numAlloc; } ASSERT_EQ(numAlloc, maxNumAlloc); frameQueue.stop(true); // Wait more than enough time(1 sec) to render all queued frames for sure. ::usleep(1000000); if (queueThread.joinable()) { queueThread.join(); } // mReleased should not be checked. IProducerListener::onBufferReleased() // from the previous Surface could be missing after a new Surface was // configured. Instead check # of dequeueable and queueBuffer() calls. ASSERT_EQ(numAlloc, mBqStat->mQueued); ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable()); for (int i = 0; i < maxDequeueCount; ++i) { AHardwareBuffer *buf; sp fence; ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); AHardwareBuffer_release(buf); mBqStat->mDequeued++; numAlloc++; } ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &bufs[0], &fences[0])); } TEST_F(GraphicsTrackerTest, maxDequeueIncreaseTest) { uint32_t generation = 1; int maxDequeueCount = 10; int dequeueIncrease = 4; int numAlloc = 0; ASSERT_TRUE(init(maxDequeueCount)); ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new TestConsumerListener(mConsumer), 1, false)); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation)); int waitFd = -1; ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd)); C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd); AHardwareBuffer *buf; sp fence; uint64_t bids[maxDequeueCount]; if (__builtin_available(android __ANDROID_API_T__, *)) { for (int i = 0; i < maxDequeueCount; ++i) { ASSERT_EQ(C2_OK, waitFence.wait(1000000000)); ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); ASSERT_EQ(OK, AHardwareBuffer_getId(buf, &bids[i])); AHardwareBuffer_release(buf); mBqStat->mDequeued++; numAlloc++; } } else { GTEST_SKIP(); } ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000)); ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); ASSERT_EQ(C2_OK, mTracker->deallocate(bids[0], Fence::NO_FENCE)); mBqStat->mDiscarded++; maxDequeueCount += dequeueIncrease; ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount)); for (int i = 0; i < dequeueIncrease + 1; ++i) { ASSERT_EQ(C2_OK, waitFence.wait(1000000000)); ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); AHardwareBuffer_release(buf); mBqStat->mDequeued++; numAlloc++; } ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000)); ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); ASSERT_EQ(C2_OK, mTracker->deallocate(bids[1], Fence::NO_FENCE)); mBqStat->mDiscarded++; maxDequeueCount += dequeueIncrease; ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount)); for (int i = 0; i < dequeueIncrease + 1; ++i) { ASSERT_EQ(C2_OK, waitFence.wait(1000000000)); ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); AHardwareBuffer_release(buf); mBqStat->mDequeued++; numAlloc++; } ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000)); ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); } TEST_F(GraphicsTrackerTest, maxDequeueDecreaseTest) { uint32_t generation = 1; int maxDequeueCount = 12; int dequeueDecrease = 4; int numAlloc = 0; ASSERT_TRUE(init(maxDequeueCount)); ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation), new TestConsumerListener(mConsumer), 1, false)); ASSERT_EQ(OK, mProducer->setGenerationNumber(generation)); ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation)); int waitFd = -1; ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd)); C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd); AHardwareBuffer *buf; sp fence; uint64_t bids[maxDequeueCount]; if (__builtin_available(android __ANDROID_API_T__, *)) { for (int i = 0; i < maxDequeueCount; ++i) { ASSERT_EQ(C2_OK, waitFence.wait(1000000000)); ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); ASSERT_EQ(OK, AHardwareBuffer_getId(buf, &bids[i])); AHardwareBuffer_release(buf); mBqStat->mDequeued++; numAlloc++; } } else { GTEST_SKIP(); } ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000)); ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); int discardIdx = 0; maxDequeueCount -= dequeueDecrease; ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount)); for (int i = 0; i < dequeueDecrease + 1; ++i) { ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000)); ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); ASSERT_EQ(C2_OK, mTracker->deallocate(bids[discardIdx++], Fence::NO_FENCE)); mBqStat->mDiscarded++; } ASSERT_EQ(C2_OK, waitFence.wait(1000000000)); ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); mBqStat->mDequeued++; ASSERT_EQ(C2_OK, mTracker->deallocate(bids[discardIdx++], Fence::NO_FENCE)); mBqStat->mDiscarded++; ASSERT_EQ(C2_OK, mTracker->deallocate(bids[discardIdx++], Fence::NO_FENCE)); mBqStat->mDiscarded++; maxDequeueCount -= dequeueDecrease; ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount)); for (int i = 0; i < dequeueDecrease - 1; ++i) { ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000)); ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); ASSERT_EQ(C2_OK, mTracker->deallocate(bids[discardIdx++], Fence::NO_FENCE)); mBqStat->mDiscarded++; } ASSERT_EQ(C2_OK, waitFence.wait(1000000000)); ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence)); mBqStat->mDequeued++; }