高通Camx-内存池架构/ImageBuffer
一、Camx内存池架构简介
1.1内存池使用的好处
首先,在项目中如果频繁的使用malloc申请内存空间,堆上分配和释放内存,会导致性能的损失。由于堆空间申请的存储地址是不连续的,也会使系统中出现的大量的内存碎片,降低内存的利用率,而内存池的使用就很好的解决了这些性能和内存上的问题。
其次,内存池使用映射到连续物理地址的Ion Buffer(本质是linux的file文件),通过文件描述符可以实现各个进程间数据的共享,比堆空间的使用范围大多了,能更好的link上下路各个模块间的链路。
1.2Camx内存池架构总览
高通Camx架构中的imageBuffer在pipeline的流转中扮演着重要的作用,而管理imagerBuffer的ImageBufferManager与MemPool Manager共同承担起高通Camx架构中的内存池的调度角色,下图简单绘制了一下相互的调用关系。
二、Camx ImageBufferManager
代码路径:vendor\qcom\proprietary\camx\src\core\camximagebuffermanager.cpp
vendor\qcom\proprietary\camx\src\core\camximagebuffer.cpp
2.1 ImageBufferManager相关函数解析
2.1.1 ImageBufferManager::Create函数实现如下
CamxResult ImageBufferManager::Create(
const CHAR* pBufferManagerName,
BufferManagerCreateData* pCreateData,
ImageBufferManager** ppImageBufferManager)
{
CamxResult result = CamxResultSuccess;
ImageBufferManager* pImageBufferManager = NULL;
if (CamxResultSuccess == result)
{
*ppImageBufferManager = NULL;
pImageBufferManager = CAMX_NEW ImageBufferManager();
}
if (CamxResultSuccess == result)
{
// 从内存池中为ImageBufferManager分配内存
result = pImageBufferManager->Initialize(pBufferManagerName, pCreateData);
if (CamxResultSuccess == result)
{
CAMX_LOG_INFO(CamxLogGroupMemMgr, "[%s] : allocRequired=%d, immediateAllocBufferCount=%d, maxBufferCount=%d", pBufferManagerName, pCreateData->allocateBufferMemory, pCreateData->immediateAllocBufferCount, pCreateData->maxBufferCount);
// 初始化ImageBufferManager中的buffers,这里是重点
result = pImageBufferManager->InitializeBuffers(pCreateData);
}
}
if (CamxResultSuccess == result)
{
*ppImageBufferManager = pImageBufferManager;
}
return result;
2.1.2 ImageBufferManager::InitializeBuffer实现如下
CamxResult ImageBufferManager::InitializeBuffers(
BufferManagerCreateData* pCreateData) {
m_createData = *pCreateData;
m_currentFormat = pCreateData->bufferProperties.imageFormat;
m_maxBufferCount = pCreateData->maxBufferCount;
m_immediateAllocBufferCount = pCreateData->immediateAllocBufferCount;
// Allocate the minimum number of buffers needed immediately serially
for (UINT i = 0; i < m_immediateAllocBufferCount; i++)
{
// ①new buffer
ImageBuffer* pBuffer = CAMX_NEW ImageBuffer();
if (NULL != pBuffer)
{
// ②buffer初始化,buffer相关信息放在全局变量m_createData中
result = pBuffer->Initialize(m_pBufferManagerName, &m_createData, &m_currentFormat, this,m_hMemPoolBufMgrHandle);
// ③Allocate the buffer
result = pBuffer->Allocate();
LDLLNode* pNode = static_cast<LDLLNode*>(CAMX_CALLOC(sizeof(LDLLNode)));
if (NULL != pNode)
{
pNode->pData = pBuffer;
// ④Add the buffer to the free buffer list
m_freeBufferList.InsertToTail(pNode);
}
}
2.1.3 ImageBufferManager::GetImageBuffer函数实现如下
①从free-list中找buffer;如果没有
②再去busy-list中找引用计数为0的buffer;还是没有
③看看能不能自己创建buffer;如果不能创建
④超时等待n次去获取busy-list中引用计数为0的buffer
经过上述操作,如果能获取到buffer,先添加buffer的引用计数,然后封装成node,放到busy-list链表尾
ImageBuffer* ImageBufferManager::GetImageBuffer()
{
// first:先从free_list的头部取出buffer节点
LDLLNode* pNode = m_freeBufferList.RemoveFromHead();
ImageBuffer* pBuffer = NULL;
// Check the free List for an available buffer
if (NULL != pNode)
{
// 取出buffer节点中的buffer
pBuffer = static_cast<ImageBuffer*>(pNode->pData);
CAMX_FREE(pNode); // 释放该节点所占用的内存
pNode = NULL;
}
// second:如果free_list中没有可用buffer,就去busy_list中去取
if (NULL == pBuffer)
{
// 从头遍历busy_list中的节点
pNode = m_busyBufferList.Head();
while (NULL != pNode)
{
ImageBuffer* pBusyBuffer = static_cast<ImageBuffer*>(pNode->pData);
LDLLNode* pNext = LightweightDoublyLinkedList::NextNode(pNode);
if (0 == pBusyBuffer->GetReferenceCount())
{
// 如果该busy-buffer的引用计数为0,说明该buffer可以被使用
m_busyBufferList.RemoveNode(pNode);
// 就从busy_list中取出node,这里会先移出,然后后面会再次添加到busy-list的尾部
if (NULL == pBuffer) {
// We will keep this buffer
pBuffer = pBusyBuffer; // 将上述busy-buffer赋值给pBuffer
CAMX_FREE(pNode); // 释放该节点所占用的内存
pNode = NULL;
}
else
{
// 如果从busy-list取节点时刚好发现pBuffer不为空了,而pNode都从busy-list中取出来了,这次不用就下次再用吧,将其放到free-list中
m_freeBufferList.InsertToTail(pNode);
}
}
pNode = pNext;
}
}
// third:如果free-list和busy-list中没有找到引用计数为0的buffer,看看能不能创建buffer
if (NULL == pBuffer)
{
UINT numOfFreeBuffers = m_freeBufferList.NumNodes();
UINT numOfBusyBuffers = m_busyBufferList.NumNodes();
// If no free buffers were found either in the free list or the busy list, we check to see if an additional buffer
// can be allocated immediately
if ((numOfFreeBuffers + numOfBusyBuffers) < m_maxBufferCount)
{
// 只要没有达到创建buffer的max值,就能额外创建buffer
CamxResult result = CamxResultSuccess;
// 下面就是buffer创建三部曲
pBuffer = CAMX_NEW ImageBuffer();
if (NULL != pBuffer)
{
result = pBuffer->Initialize(m_pBufferManagerName, &m_createData, &m_currentFormat, this, m_hMemPoolBufMgrHandle);
}
else
{
CAMX_LOG_ERROR(CamxLogGroupMemMgr, "[%s] Ran out of memory to allocate", GetBufferManagerName());
result = CamxResultENoMemory;
}
if (CamxResultSuccess == result)
{
if ((TRUE == m_createData.allocateBufferMemory) &&
(FALSE == m_createData.bEnableLateBinding))
{
// Allocate the buffer
result = pBuffer->Allocate();
}
}
}
}
// four:如果上面三种情况都不满足,那么就只能等待busy-list中的buffer释放了
if (NULL == pBuffer)
{
pNode = NULL;
while (NULL == pNode)
{
// 设置超时等待,接收busy-list中的buffer释放信号
result = m_pWaitFreeBuffer->TimedWait(m_pLock->GetNativeHandle(), BufferTimeoutMilliseconds);
pNode = m_freeBufferList.RemoveFromHead();
if (timeoutCount >= MaxTimeoutCount)
{
// 这里会进行几次超时等待,如果超过最大等待次数就退出循环,等不下去了,累了!!
CAMX_LOG_ERROR(CamxLogGroupMemMgr, "[%s] Waited %ux%u times and failed to get buffer, returning a null buffer", GetBufferManagerName(), timeoutCount, BufferTimeoutMilliseconds);
break;
}
else if (CamxResultETimeout == result)
{ // 继续超时等待,死等,还挺执着!!
// timeout happened, print an error log and continue waiting
timeoutCount++;
CAMX_LOG_WARN(CamxLogGroupMemMgr, "[%s] Waiting %ux%d ms timedout, there might be a leak or performance issue", GetBufferManagerName(), timeoutCount, BufferTimeoutMilliseconds);
}
else if (CamxResultSuccess != result)
{
// 函数执行期间遇到错误,退出循环
timeoutCount++;
CAMX_LOG_ERROR(CamxLogGroupMemMgr, "[%s] Waiting %dx%d ms failed for unknown reason, result=%s",GetBufferManagerName(), timeoutCount,BufferTimeoutMilliseconds,Utils::CamxResultToString(result));
break;
}
}
// 如果free-list中取到节点了,万事大吉,取其buffer。念念不忘,必有回响!
if (NULL != pNode)
{
pBuffer = static_cast<ImageBuffer*>(pNode->pData);
CAMX_FREE(pNode);
pNode = NULL;
CAMX_LOG_INFO(CamxLogGroupMemMgr, "[%s] Returning a buffer [%p] from the free list after wait ended",GetBufferManagerName(), pBuffer);
}
}
// 几经波折,如果能取到buffer,会进一步处理
if (NULL != pBuffer)
{
m_pLock->Unlock();
pBuffer->AddImageReference(); // 添加buffer的引用计数
m_pLock->Lock();
pBuffer->SetBusyState(TRUE); // 我很忙
pBuffer->ClearReferenceHolderData();
pNode = static_cast<LDLLNode*>(CAMX_CALLOC(sizeof(LDLLNode)));
if (NULL != pNode)
{
pNode->pData = pBuffer;
m_busyBufferList.InsertToTail(pNode); // 将节点插入到busy-list的尾部
m_peakBufferHolders = Utils::MaxUINT(m_peakBufferHolders, m_busyBufferList.NumNodes());
}
}
return pBuffer;
}
2.1.4 ImageBufferManager::AddReference函数实现如下
①查看该buffer是否在free-list中,如果在则将其从free-list中移到busy-list中,并添加该buffer的引用计数;如果不在
②查看该buffer是否在busy-list中,如果在则增加该buffer的引用计数。
UINT ImageBufferManager::AddReference(
ImageBuffer* pImageBuffer) {
BOOL doneFlag = FALSE;
UINT count = 0;
LDLLNode* pNode;
// 看看该buffer是不是在free-list中,如果是,则将其从中移除并添加到busy-list中
pNode = m_freeBufferList.Head();
while (NULL != pNode)
{
if (pImageBuffer == static_cast<ImageBuffer*>(pNode->pData))
{
// Add reference to the image buffer
count = pImageBuffer->AddImageReference();
// Move if from free list to busy list
m_freeBufferList.RemoveNode(pNode);
pImageBuffer->SetBusyState(TRUE);
m_busyBufferList.InsertToTail(pNode);
CAMX_LOG_VERBOSE(CamxLogGroupMemMgr, "[%s] Image buffer %p in free list, reference count after adding = %d",GetBufferManagerName(), pImageBuffer, pImageBuffer->GetReferenceCount());
doneFlag = TRUE;
break;
}
pNode = LightweightDoublyLinkedList::NextNode(pNode);
}
// 添加引用的buffer不在freelist中
if (FALSE == doneFlag)
{
pNode = m_busyBufferList.Head();
// Didn't find the image buffer in free list, then search busy list
while (NULL != pNode)
{
// Found the image buffer in busy list
if (pImageBuffer == static_cast<ImageBuffer*>(pNode->pData))
{
// Add reference to the image buffer
count = pImageBuffer->AddImageReference();
CAMX_LOG_VERBOSE(CamxLogGroupMemMgr, "[%s] Image buffer %p in busy list, reference count after adding = %d",GetBufferManagerName(), pImageBuffer, pImageBuffer->GetReferenceCount());
doneFlag = TRUE;
break;
}
pNode = LightweightDoublyLinkedList::NextNode(pNode);
}
}
if (FALSE == doneFlag)
{
CAMX_LOG_ERROR(CamxLogGroupMemMgr, "[%s] Add reference to image buffer %p failed.",GetBufferManagerName(), pImageBuffer);
}
m_pLock->Unlock();
return count;
}
CAMX_INLINE UINT AddImageReference()
{
UINT count = CamxAtomicIncU(&m_aReferenceCount); // 引用计数+1
return count; // 返回添加引用计数后的数值
}
2.1.5 ImageBufferManager::ReleaseReference函数实现如下
①查看该buffer是否在busy-list中;如果在
②查看将该buffer引用计数-1后的引用计数值;如果为0
③将其从busy-list移动到free-list链表中,并发送信号通知那些等待buffer的线程去取buffer(GetImageBuffer中如果取不到buffer,会设置超时等待)
UINT ImageBufferManager::ReleaseReference(
ImageBuffer* pImageBuffer)
{
BOOL doneFlag = FALSE;
UINT count = 0;
LDLLNode* pNode = m_busyBufferList.Head();
while (NULL != pNode)
{
// Found the image buffer in busy list,
if (pImageBuffer == static_cast<ImageBuffer*>(pNode->pData))
{
if (0 < pImageBuffer->GetReferenceCount())
{
// count为该buffer引用计数-1后的结果
count = pImageBuffer->ReleaseImageReference();
if (0 == count)
{
// Move the buffer to the free list
m_busyBufferList.RemoveNode(pNode);
if ((TRUE == m_createData.bEnableLateBinding) &&
(TRUE == m_createData.allocateBufferMemory) &&
(TRUE == pImageBuffer->HasBackingBuffer()))
{
// If late binding is enabled, release the buffer and just add the holder ImageBuffer object into
// free list. When this ImageBuffer is acquired, explicit BindBuffer on this ImageBuffer object will be
// called to allocate or bind the buffer back to this ImageBuffer
pImageBuffer->Release(FALSE);
}
pImageBuffer->SetBusyState(FALSE);// 为即将插入到free-list中的buffer设置状态时传参false,为busy-list时传参true
m_freeBufferList.InsertToTail(pNode);
// 发送信号通知等待此buffer的线程,有buffer用了,不用等了
m_pWaitFreeBuffer->Signal();
}
}
doneFlag = TRUE;
break;
}
pNode = LightweightDoublyLinkedList::NextNode(pNode);
}
if (FALSE == doneFlag)
{
CAMX_LOG_ERROR(CamxLogGroupMemMgr, "[%s] Didn't find the image buffer %p in busy list.",GetBufferManagerName(), pImageBuffer);
}
return count;
}
三、Camx MemPoolManager
在对ImageBufferManager接口进行详细说明后,对MemPoolMgr中如何获取ImagerBufferManager所需的ImageBuffer进行了下图的关系说明。
四、Camx ImageBuffer
imageBuffer在pipeline中扮演着非常重要的决策,负责图像数据在node之间的流转。在pipeline中node之间的连接是靠port来链接的,而port的连接正是通过共享内存(一组imageBuffer)来实现的
高通Camx架构通过一个 Import 操作,把MemPoolBuffer的bufferInfo,传递给 imagebuffer的bufferInfo。 ImageBuffer 在 pipeline node port口 之间是通过传递这个bufferInfo,把一个node 的outputport 传递给 inputport的。
以下是简单的ImageBuffer在pipeline里面的流转示意图:
五、Ending
本博文主要对一些文章中高通内存池架构/ImageBuffer进行拆分进行框架上的梳理,很多具体的各个模块细节可参考以下博文:
1.ImageBufferManager具体在整个流程中的调用以及ImgeBuffer在pipeline之间的流转:ImageBufferManager介绍-CSDN博客
DMABuffer剖析_dma buffer-CSDN博客
2.MemPoolMgr与ImageBufferManager之间更详细的link以及更深层次的涉及理解:DMABuffer剖析_dma buffer-CSDN博客
3.关于ION buffer本质与mmap映射使用更详细的说明:
Android ION 相关信息查看方法_kernel查看ion-CSDN博客
ION基本概念介绍和原理分析_ion buffer-CSDN博客