上一篇文章对一些前置内容进行了介绍,这篇文章我们将继续阅读C2BufferQueueBlockPool的buffer分配相关内容。
1、fetchGraphicBlock
正式进入代码学习之前,还是先看下UML类图:
C2BufferQueueBlockPool::Impl的fetchGraphicBlock实现如下:
c2_status_t fetchGraphicBlock(
uint32_t width,
uint32_t height,
uint32_t format,
C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
C2Fence *fence) {
block->reset();
static int kMaxIgbpRetryDelayUs = 10000;
std::unique_lock<std::mutex> lock(mMutex);
// 1. 检查是否有效
if (mInvalidated) {
return C2_BAD_STATE;
}
// 2.
if (mLastDqLogTs == 0) {
mLastDqLogTs = getTimestampNow();
} else {
int64_t now = getTimestampNow();
if (now >= mLastDqLogTs + 5000000) {
if (now >= mLastDqTs + 1000000 || mDqFailure > 5) {
ALOGW("last successful dequeue was %lld us ago, "
"%zu consecutive failures",
(long long)(now - mLastDqTs), mDqFailure);
}
mLastDqLogTs = now;
}
}
// 3. 删除mProducerId == 0的情况
// 4.
c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block, fence);
// 5. 检查返回值
if (status == C2_BLOCKING) {
lock.unlock();
if (!fence) {
// in order not to drain cpu from component's spinning
::usleep(kMaxIgbpRetryDelayUs);
}
}
return status;
}
- 检查mInvalidated是否为true,如果是则直接返回C2_BAD_STATE;使用者调用invalidate后将无法再分配buffer;
- fetchGraphicBlock添加log打印机制,每隔5s会进入if语句,如果超过1s没有获取output buffer或者连续获取失败5次就打印log;
- 移除mProducerId == 0的情况,因为有surface的情况下该条件不会成立;
- 进入fetchFromIgbp_l方法获取block。
- 检查返回值,如果返回C2_BLOCKING且fence为NULL,进入sleep;
1.1 fetchFromIgbp_l(一)
fetchFromIgbp_l代码比较长,我们分段阅读:
C2AndroidMemoryUsage androidUsage = usage;
status_t status{};
int slot{};
bool bufferNeedsReallocation{};
sp<Fence> fence = new Fence();
// 拿到同步变量
C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem(): nullptr;
{ // Call dequeueBuffer().
c2_status_t c2Status;
if (syncVar) {
uint32_t waitId;
// 1. 加锁,调用isDequeueableLocked判断当前是否有buffer可以dequeue
syncVar->lock();
if (!syncVar->isDequeueableLocked(&waitId)) {
syncVar->unlock();
// 2. 如果返回false,说明当前producer无可用buffer,创建C2Fence并返回C2_BLOCKING
if (c2Fence) {
*c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
}
return C2_BLOCKING;
}
// 3. 如果当前同步状态不是STATUS_ACTIVE,此时不能获取buffer,返回C2_BLOCKING
if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_ACTIVE) {
waitId = syncVar->getWaitIdLocked();
syncVar->unlock();
if (c2Fence) {
*c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
}
return C2_BLOCKING;
}
// 4. 到这说明producer有可用buffer,修改mCurDequeueCount,解锁
syncVar->notifyDequeuedLocked();
syncVar->unlock();
// 5. 调用dequeueBuffer,从producer获取buffer
c2Status = dequeueBuffer(width, height, format, androidUsage,
&slot, &bufferNeedsReallocation, &fence);
// 6. 如果获取失败需要重新修改mCurDequeueCount
if (c2Status != C2_OK) {
syncVar->lock();
syncVar->notifyQueuedLocked();
syncVar->unlock();
}
} else {
c2Status = dequeueBuffer(width, height, format, usage,
&slot, &bufferNeedsReallocation, &fence);
}
if (c2Status != C2_OK) {
return c2Status;
}
// 7. 刷新debug flag
mDqFailure = 0;
mLastDqTs = getTimestampNow();
}
上述代码的主要目的是调用IGraphicBufferProducer::dequeueBuffer方法,从NativeWindow中获取一块buffer。在真正获取buffer之前,会检查同步变量C2SyncVariables的状态:
- 如果isDequeueableLocked返回true,说明producer中有空闲buffer,否则说明buffer已经全部dequeue出来;
- 如果getSyncStatusLocked返回值不等于STATUS_ACTIVE,说明当前producer还未配置完成,也有可能当前正处于切换surface的状态,这些状态下是不能获取output buffer的;
接下来进入到dequeueBuffer中,代码不长但是HIDL调用让人难受:
c2_status_t dequeueBuffer(
uint32_t width,
uint32_t height,
uint32_t format,
C2AndroidMemoryUsage androidUsage,
int *slot, bool *needsRealloc, sp<Fence> *fence) {
status_t status{};
using Input = HGraphicBufferProducer::DequeueBufferInput;
using Output = HGraphicBufferProducer::DequeueBufferOutput;
// 1. 获取output buffer
Return<void> transResult = mProducer->dequeueBuffer(
Input{
width,
height,
format,
androidUsage.asGrallocUsage()},
[&status, slot, needsRealloc,
fence](HStatus hStatus,
int32_t hSlot,
Output const& hOutput) {
*slot = static_cast<int>(hSlot);
if (!h2b(hStatus, &status) ||
!h2b(hOutput.fence, fence)) {
status = ::android::BAD_VALUE;
} else {
*needsRealloc =
hOutput.bufferNeedsReallocation;
}
});
// 2. 检查返回值
if (!transResult.isOk() || status != android::OK) {
if (transResult.isOk()) {
++mDqFailure;
if (status == android::INVALID_OPERATION ||
status == android::TIMED_OUT ||
status == android::WOULD_BLOCK) {
// Dequeue buffer is blocked temporarily. Retrying is
// required.
return C2_BLOCKING;
}
}
ALOGD("cannot dequeue buffer %d", status);
return C2_BAD_VALUE;
}
return C2_OK;
}
2、小结
原文阅读:
Android Codec2(二三)C2BufferQueueBlockPool - Ⅱ
扫描下方二维码,关注公众号《青山渺渺》阅读音视频开发内容。