EID 或 HUD 中 SurfaceView 区域不显示。
从上面的截图说明 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()
调用移动到锁代码块之外。