EID 或 HUD 中 SurfaceView 区域不显示。

.png)
从上面的截图说明 UnityMain 线程卡在了 Surface::dequeueBuffer() 函数,而 Binder 线程中 dequeueBuffer() 函数卡在了 BufferQueueConsumer::disconnect() 函数中等待锁。
sequenceDiagram
participant S as Surface
participant P as BufferQueueProducer
participant C as BufferQueueConsumer
S ->> P: dequeueBuffer
Note right of P: Lock mutex
P ->> C: onFrameDequeued
Note right of C: Lock mutex
C ->> C: disconnect()
Note right of C: Unlock
C -->> P:
Note right of P: Unlock
BufferQueueProducer::dequeueBuffer() 中在 mCore->mMutex 锁代码块中调用了 onFrameDequeued()。
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
...
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
...
if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
if (mCore->mConsumerListener != nullptr) {
mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
}
}
} // Autolock scope
...
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
if (mCore->mConsumerListener != nullptr) {
mCore->mConsumerListener->onFrameDequeued(
mSlots[*outSlot].mGraphicBuffer->getId());
}
}
...
} // Autolock scope
...
}
从调用栈看,onFrameDequeued() 最终调用了 BufferQueueConsumer::disconnect(),而在该函数中又申请了锁 mCore->mMutex。
status_t BufferQueueConsumer::disconnect() {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mCore->mMutex);
...
return NO_ERROR;
}
因为 std::mutex 是不可重入的锁,所以 disconnect() 函数卡死了。
HUD 不显示的问题原因是当 camera 释放时会调用 BufferQueueProducer::cancelBuffer(),该函数中锁代码块中调用 onFrameCancelled(),而 onFrameCancelled() 有可能会调用 disconnect(),所以与 EID 不显示问题原因相同。
Google 已经在 2023 年 8 月通过这个 CL 修复了该问题。修复方法很简单,把 onFrameDequeue() 调用移动到锁代码块之外。
