高通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博客

相关推荐
佛系小嘟嘟10 分钟前
Android Studio不显示需要的tag日志解决办法《All logs entries are hidden by the filter》
android·ide·android studio
mariokkm15 分钟前
Django一分钟:django中收集关联对象关联数据的方法
android·django·sqlite
长亭外的少年33 分钟前
如何查看 Android 项目的依赖结构树
android
深海呐2 小时前
Android 从本地选择视频,用APP播放或进行其他处理
android·音视频·从本地选择视频,用app播放·从本地选择视频,并拿到信息·跳转到本地视频列表
深海呐3 小时前
Android Google登录接入
android·google登录接入·android 谷歌登录接入·google登录·android google
货拉拉技术3 小时前
多元消息融合分发平台
javascript·后端·架构
daiyang123...3 小时前
MySQL【知识改变命运】11
android·数据库·mysql
8931519603 小时前
Android开发教程案例源码分享-匹配动画多个头像飘动效果
android·android开发·android教程·kotlin教程·android头像飘动动画·android匹配动画·android多个头像飘动动画
冷心笑看丽美人4 小时前
Spring 框架七大模块(Java EE 学习笔记03)
学习·spring·架构·java-ee
老码沉思录4 小时前
Android开发实战班 - 网络编程 - WebSocket 实时通信
android·网络·websocket