高通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函数实现如下

cpp 复制代码
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实现如下

cpp 复制代码
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链表尾

cpp 复制代码
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的引用计数。

cpp 复制代码
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,会设置超时等待)

cpp 复制代码
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博客

相关推荐
AI人H哥会Java6 小时前
【Spring】Spring的模块架构与生态圈—Spring MVC与Spring WebFlux
java·开发语言·后端·spring·架构
小屁不止是运维6 小时前
麒麟操作系统服务架构保姆级教程(二)ssh远程连接
linux·运维·服务器·学习·架构·ssh
不会写代码的女程序猿6 小时前
关于ETL的两种架构(ETL架构和ELT架构)
数据仓库·架构·etl
花追雨7 小时前
Android -- 双屏异显之方法一
android·双屏异显
小趴菜82277 小时前
安卓 自定义矢量图片控件 - 支持属性修改矢量图路径颜色
android
氤氲息7 小时前
Android v4和v7冲突
android
KdanMin7 小时前
高通Android 12 Launcher应用名称太长显示完整
android
chenjk47 小时前
Android不可擦除分区写文件恢复出厂设置,无法读写问题
android
袁震7 小时前
Android-Glide缓存机制
android·缓存·移动开发·glide
工程师老罗7 小时前
Android笔试面试题AI答之SQLite(2)
android·jvm·sqlite