diff options
author | Chih-Yu Huang <akahuang@google.com> | 2020-09-03 16:12:39 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-09-03 16:12:39 +0000 |
commit | 789409daebb127bc0fc4647be6a428e38fedc27d (patch) | |
tree | 15b302105bc458b9189b9ea1fa3c39e9966c7183 | |
parent | d13dccf0008e331dd60e27064cbf0342605366e0 (diff) | |
parent | fd0e98187f9a8969e76127f1926733321fd5ef88 (diff) | |
download | v4l2_codec2-789409daebb127bc0fc4647be6a428e38fedc27d.tar.gz |
Make VideoFramePool and C2VdaBqBlockPool non-blocking when fetching am: 9cf0ab57d1 am: fd0e98187f
Original change: https://googleplex-android-review.googlesource.com/c/platform/external/v4l2_codec2/+/12497570
Change-Id: I6e363ac7eb122705d98fe110b1426d5816e555bc
-rw-r--r-- | components/VideoFramePool.cpp | 100 | ||||
-rw-r--r-- | components/include/v4l2_codec2/components/VideoFramePool.h | 11 | ||||
-rw-r--r-- | plugin_store/Android.bp | 2 | ||||
-rw-r--r-- | plugin_store/C2VdaBqBlockPool.cpp | 209 | ||||
-rw-r--r-- | plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h | 13 |
5 files changed, 149 insertions, 186 deletions
diff --git a/components/VideoFramePool.cpp b/components/VideoFramePool.cpp index 22b622d..b6bbfab 100644 --- a/components/VideoFramePool.cpp +++ b/components/VideoFramePool.cpp @@ -57,6 +57,17 @@ c2_status_t VideoFramePool::requestNewBufferSet(C2BlockPool& blockPool, int32_t } // static +bool VideoFramePool::setNotifyBlockAvailableCb(C2BlockPool& blockPool, ::base::OnceClosure cb) { + ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId()); + + if (blockPool.getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) { + C2VdaBqBlockPool* bqPool = static_cast<C2VdaBqBlockPool*>(&blockPool); + return bqPool->setNotifyBlockAvailableCb(std::move(cb)); + } + return false; +} + +// static std::unique_ptr<VideoFramePool> VideoFramePool::Create( std::shared_ptr<C2BlockPool> blockPool, const size_t numBuffers, const media::Size& size, HalPixelFormat pixelFormat, bool isSecure, @@ -106,7 +117,6 @@ VideoFramePool::~VideoFramePool() { ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence()); mClientWeakThisFactory.InvalidateWeakPtrs(); - mCancelGetFrame = true; if (mFetchThread.IsRunning()) { mFetchTaskRunner->PostTask(FROM_HERE, @@ -136,48 +146,68 @@ bool VideoFramePool::getVideoFrame(GetVideoFrameCB cb) { return true; } +// static +void VideoFramePool::getVideoFrameTaskThunk( + scoped_refptr<::base::SequencedTaskRunner> taskRunner, + std::optional<::base::WeakPtr<VideoFramePool>> weakPool) { + ALOGV("%s()", __func__); + ALOG_ASSERT(weakPool); + + taskRunner->PostTask(FROM_HERE, + ::base::BindOnce(&VideoFramePool::getVideoFrameTask, *weakPool)); +} + void VideoFramePool::getVideoFrameTask() { ALOGV("%s()", __func__); ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence()); - // Initial delay: 64us - constexpr size_t kFetchRetryDelayInit = 64; - // Max delay: 16ms (1 frame at 60fps) - constexpr size_t kFetchRetryDelayMax = 16384; - std::optional<FrameWithBlockId> frameWithBlockId; - size_t numRetries = 0; - size_t delay = kFetchRetryDelayInit; - while (true) { - if (mCancelGetFrame) { - ALOGW("Request to get frame canceled after %zu retries", numRetries); - break; + // Variables used to exponential backoff retry when buffer fetching times out. + constexpr size_t kFetchRetryDelayInit = 64; // Initial delay: 64us + constexpr size_t kFetchRetryDelayMax = 16384; // Max delay: 16ms (1 frame at 60fps) + static size_t sNumRetries = 0; + static size_t sDelay = kFetchRetryDelayInit; + + std::shared_ptr<C2GraphicBlock> block; + c2_status_t err = mBlockPool->fetchGraphicBlock(mSize.width(), mSize.height(), + static_cast<uint32_t>(mPixelFormat), + mMemoryUsage, &block); + if (err == C2_TIMED_OUT || err == C2_BLOCKING) { + if (setNotifyBlockAvailableCb(*mBlockPool, + ::base::BindOnce(&VideoFramePool::getVideoFrameTaskThunk, + mFetchTaskRunner, mFetchWeakThis))) { + ALOGV("%s(): fetchGraphicBlock() timeout, waiting for block available.", __func__); + } else { + ALOGV("%s(): fetchGraphicBlock() timeout, waiting %zuus (%zu retry)", __func__, sDelay, + sNumRetries + 1); + mFetchTaskRunner->PostDelayedTask( + FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis), + ::base::TimeDelta::FromMicroseconds(sDelay)); + + sDelay = std::min(sDelay * 2, kFetchRetryDelayMax); // Exponential backoff + sNumRetries++; } - std::shared_ptr<C2GraphicBlock> block; - c2_status_t err = mBlockPool->fetchGraphicBlock(mSize.width(), mSize.height(), - static_cast<uint32_t>(mPixelFormat), - mMemoryUsage, &block); - - if (err == C2_OK) { - ALOG_ASSERT(block != nullptr); - std::optional<uint32_t> bufferId = getBufferIdFromGraphicBlock(*mBlockPool, *block); - std::unique_ptr<VideoFrame> frame = VideoFrame::Create(std::move(block)); - // Only pass the frame + id pair if both have successfully been obtained. - // Otherwise exit the loop so a nullopt is passed to the client. - if (bufferId && frame) { - frameWithBlockId = std::make_pair(std::move(frame), *bufferId); - } - break; - } else if (err != C2_TIMED_OUT && err != C2_BLOCKING) { - ALOGE("Failed to fetch block, err=%d, retry %zu times", err, numRetries); - break; + return; + } + + // Reset to the default value. + sNumRetries = 0; + sDelay = kFetchRetryDelayInit; + + std::optional<FrameWithBlockId> frameWithBlockId; + if (err == C2_OK) { + ALOG_ASSERT(block != nullptr); + std::optional<uint32_t> bufferId = getBufferIdFromGraphicBlock(*mBlockPool, *block); + std::unique_ptr<VideoFrame> frame = VideoFrame::Create(std::move(block)); + // Only pass the frame + id pair if both have successfully been obtained. + // Otherwise exit the loop so a nullopt is passed to the client. + if (bufferId && frame) { + frameWithBlockId = std::make_pair(std::move(frame), *bufferId); } else { - ++numRetries; - ALOGV("fetchGraphicBlock() timeout, waiting %zuus (%zu retry)", delay, numRetries); - usleep(delay); - // Exponential backoff - delay = std::min(delay * 2, kFetchRetryDelayMax); + ALOGE("%s(): Failed to generate VideoFrame or get the buffer id.", __func__); } + } else { + ALOGE("%s(): Failed to fetch block, err=%d", __func__, err); } mClientTaskRunner->PostTask( diff --git a/components/include/v4l2_codec2/components/VideoFramePool.h b/components/include/v4l2_codec2/components/VideoFramePool.h index c05c4ff..71bfe27 100644 --- a/components/include/v4l2_codec2/components/VideoFramePool.h +++ b/components/include/v4l2_codec2/components/VideoFramePool.h @@ -28,7 +28,7 @@ namespace android { class VideoFramePool { public: using FrameWithBlockId = std::pair<std::unique_ptr<VideoFrame>, uint32_t>; - using GetVideoFrameCB = base::OnceCallback<void(std::optional<FrameWithBlockId>)>; + using GetVideoFrameCB = ::base::OnceCallback<void(std::optional<FrameWithBlockId>)>; static std::unique_ptr<VideoFramePool> Create( std::shared_ptr<C2BlockPool> blockPool, const size_t numBuffers, @@ -54,6 +54,8 @@ private: bool initialize(); void destroyTask(); + static void getVideoFrameTaskThunk(scoped_refptr<::base::SequencedTaskRunner> taskRunner, + std::optional<::base::WeakPtr<VideoFramePool>> weakPool); void getVideoFrameTask(); void onVideoFrameReady(std::optional<FrameWithBlockId> frameWithBlockId); @@ -66,6 +68,10 @@ private: // |bufferCount| is the number of requested buffers. static c2_status_t requestNewBufferSet(C2BlockPool& blockPool, int32_t bufferCount); + // Ask |blockPool| to notify when a block is available via |cb|. + // Return true if |blockPool| supports notifying buffer available. + static bool setNotifyBlockAvailableCb(C2BlockPool& blockPool, ::base::OnceClosure cb); + std::shared_ptr<C2BlockPool> mBlockPool; const media::Size mSize; const HalPixelFormat mPixelFormat; @@ -77,9 +83,6 @@ private: ::base::Thread mFetchThread{"VideoFramePoolFetchThread"}; scoped_refptr<::base::SequencedTaskRunner> mFetchTaskRunner; - // Set to true to unconditionally interrupt pending frame requests. - std::atomic<bool> mCancelGetFrame = false; - ::base::WeakPtr<VideoFramePool> mClientWeakThis; ::base::WeakPtr<VideoFramePool> mFetchWeakThis; ::base::WeakPtrFactory<VideoFramePool> mClientWeakThisFactory{this}; diff --git a/plugin_store/Android.bp b/plugin_store/Android.bp index ed9d784..73dccaf 100644 --- a/plugin_store/Android.bp +++ b/plugin_store/Android.bp @@ -21,6 +21,7 @@ cc_library_shared { ], shared_libs: [ "android.hardware.graphics.bufferqueue@2.0", + "libchrome", "libcutils", "libhardware", "libhidlbase", @@ -35,6 +36,7 @@ cc_library_shared { cflags: [ "-Werror", "-Wall", + "-Wno-unused-parameter", // needed for libchrome/base codes "-Wthread-safety", ], } diff --git a/plugin_store/C2VdaBqBlockPool.cpp b/plugin_store/C2VdaBqBlockPool.cpp index f0b95dd..9abc698 100644 --- a/plugin_store/C2VdaBqBlockPool.cpp +++ b/plugin_store/C2VdaBqBlockPool.cpp @@ -17,6 +17,7 @@ #include <C2BlockInternal.h> #include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> #include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> +#include <base/callback.h> #include <log/log.h> #include <system/window.h> #include <types.h> @@ -346,9 +347,6 @@ public: explicit EventNotifier(const std::shared_ptr<Listener>& listener) : mListener(listener) {} virtual ~EventNotifier() = default; - // Enable or disable the notifier. The notifier would notify |mListener| when enabled. - virtual void enable(bool enabled) = 0; - protected: void notify() { ALOGV("%s()", __func__); @@ -367,89 +365,14 @@ public: using EventNotifier::EventNotifier; ~BufferReleasedNotifier() override = default; - // EventNotifier implementation - void enable(bool enabled) override { - ALOGV("%s(%d)", __func__, enabled); - mEnabled = enabled; - } - // HProducerListener implementation Return<void> onBuffersReleased(uint32_t count) override { ALOGV("%s(%u)", __func__, count); - if (count > 0 && mEnabled.load()) { + if (count > 0) { notify(); } return {}; } - -private: - std::atomic<bool> mEnabled{false}; -}; - -// Notifies the listener with exponential backoff delay. -class ExpDelayedNotifier : public EventNotifier { -public: - explicit ExpDelayedNotifier(const std::shared_ptr<Listener>& listener) - : EventNotifier(listener) { - mRunningThread = std::thread(&ExpDelayedNotifier::run, this); - } - ~ExpDelayedNotifier() override { - ALOGV("%s()", __func__); - { - std::unique_lock<std::mutex> lock(mMutex); - mDestroying = true; - mInterruptCv.notify_one(); - } - mRunningThread.join(); - } - - void enable(bool enabled) override { - ALOGV("%s(%d)", __func__, enabled); - std::lock_guard<std::mutex> lock(mMutex); - - if (mEnabled == enabled) { - ALOGW("%s(): ExpDelayedNotifier already triggered %s.", __func__, - enabled ? "on" : "off"); - return; - } - mEnabled = enabled; - mInterruptCv.notify_one(); - } - -private: - void run() { - ALOGV("%s()", __func__); - constexpr size_t kFetchRetryDelayInit = 1000; // Initial delay: 1ms - constexpr size_t kFetchRetryDelayMax = 16000; // Max delay: 16ms (1 frame at 60fps) - - std::unique_lock<std::mutex> lock(mMutex); - while (true) { - mInterruptCv.wait(lock, [this]() { return mEnabled || mDestroying; }); - if (mDestroying) return; - - size_t delay = kFetchRetryDelayInit; - while (mEnabled && !mDestroying) { - mInterruptCv.wait_for(lock, delay * 1us, - [this]() { return !mEnabled || mDestroying; }); - if (mDestroying) return; - if (!mEnabled) break; - - notify(); - delay = std::min(delay * 2, kFetchRetryDelayMax); - } - } - } - - // The background thread for exponential backoff delay. - std::thread mRunningThread; - // The mutex to protect other members and condition variables. - std::mutex mMutex; - // Used to get interrupt when the notifier is enabled or destroyed. - std::condition_variable mInterruptCv; - // Set to true when the notifier is enabled. - bool mEnabled = false; - // Set to true when the notifier is about to be destroyed. - bool mDestroying = false; }; /** @@ -533,6 +456,7 @@ public: c2_status_t updateGraphicBlock(bool willCancel, uint32_t oldSlot, uint32_t* newSlot, std::shared_ptr<C2GraphicBlock>* block /* nonnull */); c2_status_t getMinBuffersForDisplay(size_t* bufferCount); + bool setNotifyBlockAvailableCb(::base::OnceClosure cb); private: friend struct C2VdaBqBlockPoolData; @@ -550,10 +474,6 @@ private: C2AndroidMemoryUsage mUsage = C2MemoryUsage(0); }; - c2_status_t fetchGraphicBlockInternalLocked( - uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage, - std::shared_ptr<C2GraphicBlock>* block /* nonnull */); - // For C2VdaBqBlockPoolData to detach corresponding slot buffer from BufferQueue. void detachBuffer(uint64_t producerId, int32_t slotId); @@ -605,15 +525,13 @@ private: // Listener for buffer release events. sp<EventNotifier> mFetchBufferNotifier; + std::mutex mBufferReleaseMutex; - // Counter for the number of invocations of onProducerBufferReleased - uint64_t mBufferReleaseCount{0}; - // Counter for the number of invocations of configureProducer - uint64_t mConfigCount{0}; - // Cvar which is waited upon after failed attempts to deque buffers from - // mProducer. It is signaled when the consumer releases buffers (through - // onProducerBufferReleased) or when the producer changes. - std::condition_variable mBufferReleaseCv; + // Set to true when the buffer release event is triggered after dequeueing + // buffer from IGBP times out. + bool mBufferReleasedAfterTimedOut GUARDED_BY(mBufferReleaseMutex) = false; + // The callback to notify the caller the buffer is available. + ::base::OnceClosure mNotifyBlockAvailableCb GUARDED_BY(mBufferReleaseMutex); }; C2VdaBqBlockPool::Impl::Impl(const std::shared_ptr<C2Allocator>& allocator) @@ -627,48 +545,6 @@ c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlock( ALOGV("%s()", __func__); std::lock_guard<std::mutex> lock(mMutex); - c2_status_t res = fetchGraphicBlockInternalLocked(width, height, format, usage, block); - if (res != C2_TIMED_OUT) { - ALOGV("%s() fetchGraphicBlockInternalLocked() return %d", __func__, res); - return res; - } - - mFetchBufferNotifier->enable(true); - while (true) { - { - std::unique_lock<std::mutex> releaseLock(mBufferReleaseMutex); - // Wait for either mBufferReleaseCount or mConfigCount to change. - uint64_t curConfigCount = mConfigCount; - uint64_t currentBufferReleaseCount = mBufferReleaseCount; - bool success = mBufferReleaseCv.wait_for( - releaseLock, 100ms, [currentBufferReleaseCount, curConfigCount, this]() { - return currentBufferReleaseCount != mBufferReleaseCount || - curConfigCount != mConfigCount; - }); - if (!success) { - res = C2_TIMED_OUT; - break; - } else if (mConfigCount != curConfigCount) { - res = C2_BAD_STATE; - break; - } - } - - res = fetchGraphicBlockInternalLocked(width, height, format, usage, block); - if (res != C2_TIMED_OUT) { - ALOGV("%s() fetchGraphicBlockInternalLocked() return %d", __func__, res); - break; - } - } - mFetchBufferNotifier->enable(false); - return res; -} - -c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlockInternalLocked( - uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage, - std::shared_ptr<C2GraphicBlock>* block /* nonnull */) { - ALOGV("%s()", __func__); - if (!mProducer) { // Producer will not be configured in byte-buffer mode. Allocate buffers from allocator // directly as a basic graphic block pool. @@ -704,6 +580,10 @@ c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlockInternalLocked( if (status == android::INVALID_OPERATION) { status = android::TIMED_OUT; } + if (status == android::TIMED_OUT) { + std::lock_guard<std::mutex> lock(mBufferReleaseMutex); + mBufferReleasedAfterTimedOut = false; + } if (status != android::NO_ERROR && status != BUFFER_NEEDS_REALLOCATION) { return asC2Error(status); } @@ -717,7 +597,7 @@ c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlockInternalLocked( } if (fenceStatus == -ETIME) { // fence wait timed out - ALOGV("buffer fence wait timed out, wait for retry..."); + ALOGV("%s(): buffer (slot=%d) fence wait timed out", __func__, slot); return C2_TIMED_OUT; } ALOGE("buffer fence wait error: %d", fenceStatus); @@ -805,9 +685,20 @@ c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlockInternalLocked( void C2VdaBqBlockPool::Impl::onEventNotified() { ALOGV("%s()", __func__); - std::lock_guard<std::mutex> lock(mBufferReleaseMutex); - mBufferReleaseCount++; - mBufferReleaseCv.notify_one(); + ::base::OnceClosure outputCb; + { + std::lock_guard<std::mutex> lock(mBufferReleaseMutex); + + mBufferReleasedAfterTimedOut = true; + if (mNotifyBlockAvailableCb) { + outputCb = std::move(mNotifyBlockAvailableCb); + } + } + + // Calling the callback outside the lock to avoid the deadlock. + if (outputCb) { + std::move(outputCb).Run(); + } } c2_status_t C2VdaBqBlockPool::Impl::queryGenerationAndUsage( @@ -832,7 +723,7 @@ c2_status_t C2VdaBqBlockPool::Impl::queryGenerationAndUsage( return C2_CORRUPTED; } if (fenceStatus == -ETIME) { // fence wait timed out - ALOGV("buffer fence wait timed out, wait for retry..."); + ALOGV("%s(): buffer (slot=%d) fence wait timed out", __func__, slot); return C2_TIMED_OUT; } ALOGE("buffer fence wait error: %d", fenceStatus); @@ -966,12 +857,6 @@ void C2VdaBqBlockPool::Impl::configureProducer(const sp<HGraphicBufferProducer>& mSlotAllocations.clear(); } - { - std::lock_guard<std::mutex> releaseLock(mBufferReleaseMutex); - mConfigCount++; - mBufferReleaseCv.notify_one(); - } - if (newProducer->setDequeueTimeout(0) != android::NO_ERROR) { ALOGE("%s(): failed to setDequeueTimeout(0)", __func__); return; @@ -982,9 +867,6 @@ void C2VdaBqBlockPool::Impl::configureProducer(const sp<HGraphicBufferProducer>& if (newProducer->connect(listener, 'ARC\0', false) == android::NO_ERROR) { ALOGI("connected to ARC-specific IGBP listener."); mFetchBufferNotifier = listener; - } else { - ALOGI("Fallback to exponential backoff polling."); - mFetchBufferNotifier = new ExpDelayedNotifier(shared_from_this()); } // HGraphicBufferProducer could (and should) be replaced if the client has set a new generation @@ -1197,6 +1079,32 @@ void C2VdaBqBlockPool::Impl::detachBuffer(uint64_t producerId, int32_t slotId) { } } +bool C2VdaBqBlockPool::Impl::setNotifyBlockAvailableCb(::base::OnceClosure cb) { + ALOGV("%s()", __func__); + if (mFetchBufferNotifier == nullptr) { + return false; + } + + ::base::OnceClosure outputCb; + { + std::lock_guard<std::mutex> lock(mBufferReleaseMutex); + + // If there is any buffer released after dequeueBuffer() timed out, then we could notify the + // caller directly. + if (mBufferReleasedAfterTimedOut) { + outputCb = std::move(cb); + } else { + mNotifyBlockAvailableCb = std::move(cb); + } + } + + // Calling the callback outside the lock to avoid the deadlock. + if (outputCb) { + std::move(outputCb).Run(); + } + return true; +} + C2VdaBqBlockPool::C2VdaBqBlockPool(const std::shared_ptr<C2Allocator>& allocator, const local_id_t localId) : C2BufferQueueBlockPool(allocator, localId), mLocalId(localId), mImpl(new Impl(allocator)) {} @@ -1246,6 +1154,13 @@ c2_status_t C2VdaBqBlockPool::getMinBuffersForDisplay(size_t* bufferCount) { return C2_NO_INIT; } +bool C2VdaBqBlockPool::setNotifyBlockAvailableCb(::base::OnceClosure cb) { + if (mImpl) { + return mImpl->setNotifyBlockAvailableCb(std::move(cb)); + } + return false; +} + C2VdaBqBlockPoolData::C2VdaBqBlockPoolData(uint64_t producerId, int32_t slotId, const std::shared_ptr<C2VdaBqBlockPool::Impl>& pool) : mProducerId(producerId), mSlotId(slotId), mPool(pool) {} diff --git a/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h index b600716..fd524d2 100644 --- a/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h +++ b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h @@ -12,6 +12,7 @@ #include <C2BqBufferPriv.h> #include <C2Buffer.h> #include <C2PlatformSupport.h> +#include <base/callback_forward.h> namespace android { @@ -131,6 +132,18 @@ public: */ c2_status_t getMinBuffersForDisplay(size_t* bufferCount); + /** + * Set the callback that will be triggered when there is block available. + * + * \note C2VdaBqBlockPool-specific function + * + * \param cb the callback function that will be triggered when there is block available. + * + * Return false if we don't support to notify the caller when a buffer is available. + * + */ + bool setNotifyBlockAvailableCb(base::OnceClosure cb); + private: friend struct C2VdaBqBlockPoolData; class Impl; |