QCamera2: Add memory pools for stream buffers

  Add the necessary functionality for caching
  stream buffers in memory pools for each
  stream type. The buffers will be re-used
  as long as the camera client doesn't trigger
  internal preview restart by updating the
  use case(zsl vs. non-zsl), picture size etc.

CRs-Fixed: 571777
Change-Id: I6097662ef2206016f512a3406ce1860fce44d5ef
diff --git a/QCamera2/HAL/QCamera2HWI.cpp b/QCamera2/HAL/QCamera2HWI.cpp
index cd1b820..244ecd0 100644
--- a/QCamera2/HAL/QCamera2HWI.cpp
+++ b/QCamera2/HAL/QCamera2HWI.cpp
@@ -1564,7 +1564,10 @@
     case CAM_STREAM_TYPE_PREVIEW:
         {
             if (isNoDisplayMode()) {
-                mem = new QCameraStreamMemory(mGetMemory, bCachedMem);
+                mem = new QCameraStreamMemory(mGetMemory,
+                                              bCachedMem,
+                                              &m_memoryPool,
+                                              stream_type);
             } else {
                 cam_dimension_t dim;
                 QCameraGrallocMemory *grallocMemory =
@@ -1605,7 +1608,10 @@
     case CAM_STREAM_TYPE_RAW:
     case CAM_STREAM_TYPE_METADATA:
     case CAM_STREAM_TYPE_OFFLINE_PROC:
-        mem = new QCameraStreamMemory(mGetMemory, bCachedMem);
+        mem = new QCameraStreamMemory(mGetMemory,
+                                      bCachedMem,
+                                      &m_memoryPool,
+                                      stream_type);
         break;
     case CAM_STREAM_TYPE_VIDEO:
         {
diff --git a/QCamera2/HAL/QCamera2HWI.h b/QCamera2/HAL/QCamera2HWI.h
index f459d69..5f18f52 100644
--- a/QCamera2/HAL/QCamera2HWI.h
+++ b/QCamera2/HAL/QCamera2HWI.h
@@ -45,6 +45,7 @@
 #include "QCameraAllocator.h"
 #include "QCameraPostProc.h"
 #include "QCameraThermalAdapter.h"
+#include "QCameraMem.h"
 
 extern "C" {
 #include <mm_camera_interface.h>
@@ -481,6 +482,7 @@
     pthread_mutex_t m_lock;
     pthread_cond_t m_cond;
     qcamera_api_result_t m_apiResult;
+    QCameraMemoryPool m_memoryPool;
 
     pthread_mutex_t m_evtLock;
     pthread_cond_t m_evtCond;
diff --git a/QCamera2/HAL/QCameraMem.cpp b/QCamera2/HAL/QCameraMem.cpp
index 8c09e6b..53a2b14 100644
--- a/QCamera2/HAL/QCameraMem.cpp
+++ b/QCamera2/HAL/QCameraMem.cpp
@@ -58,8 +58,12 @@
  *
  * RETURN     : None
  *==========================================================================*/
-QCameraMemory::QCameraMemory(bool cached)
-    :m_bCached(cached)
+QCameraMemory::QCameraMemory(bool cached,
+        QCameraMemoryPool *pool,
+        cam_stream_type_t streamType)
+    :m_bCached(cached),
+     mMemoryPool(pool),
+     mStreamType(streamType)
 {
     mBufferCount = 0;
     memset(mMemInfo, 0, sizeof(mMemInfo));
@@ -251,13 +255,30 @@
     }
 
     for (int i = mBufferCount; i < new_bufCnt; i ++) {
-        rc = allocOneBuffer(mMemInfo[i], heap_id, size);
-        if (rc < 0) {
-            ALOGE("%s: AllocateIonMemory failed", __func__);
-            for (int j = i-1; j >= mBufferCount; j--)
-                deallocOneBuffer(mMemInfo[j]);
-            break;
+        if ( NULL == mMemoryPool ) {
+            ALOGE("%s : No memory pool available", __func__);
+            rc = allocOneBuffer(mMemInfo[i], heap_id, size, m_bCached);
+            if (rc < 0) {
+                ALOGE("%s: AllocateIonMemory failed", __func__);
+                for (int j = i-1; j >= 0; j--)
+                    deallocOneBuffer(mMemInfo[j]);
+                break;
+            }
+        } else {
+            rc = mMemoryPool->allocateBuffer(mMemInfo[i],
+                                             heap_id,
+                                             size,
+                                             m_bCached,
+                                             mStreamType);
+            if (rc < 0) {
+                ALOGE("%s: Memory pool allocation failed", __func__);
+                for (int j = i-1; j >= 0; j--)
+                    mMemoryPool->releaseBuffer(mMemInfo[j],
+                                               mStreamType);
+                break;
+            }
         }
+
     }
     return rc;
 }
@@ -273,8 +294,13 @@
  *==========================================================================*/
 void QCameraMemory::dealloc()
 {
-    for (int i = 0; i < mBufferCount; i++)
-        deallocOneBuffer(mMemInfo[i]);
+    for (int i = 0; i < mBufferCount; i++) {
+        if ( NULL == mMemoryPool ) {
+            deallocOneBuffer(mMemInfo[i]);
+        } else {
+            mMemoryPool->releaseBuffer(mMemInfo[i], mStreamType);
+        }
+    }
 }
 
 /*===========================================================================
@@ -286,12 +312,16 @@
  *   @memInfo : [output] reference to struct to store additional memory allocation info
  *   @heap    : [input] heap id to indicate where the buffers will be allocated from
  *   @size    : [input] lenght of the buffer to be allocated
+ *   @cached  : [input] flag whether buffer needs to be cached
  *
  * RETURN     : int32_t type of status
  *              NO_ERROR  -- success
  *              none-zero failure code
  *==========================================================================*/
-int QCameraMemory::allocOneBuffer(QCameraMemInfo &memInfo, int heap_id, int size)
+int QCameraMemory::allocOneBuffer(QCameraMemInfo &memInfo,
+        int heap_id,
+        int size,
+        bool cached)
 {
     int rc = OK;
     struct ion_handle_data handle_data;
@@ -310,7 +340,7 @@
     /* to make it page size aligned */
     alloc.len = (alloc.len + 4095) & (~4095);
     alloc.align = 4096;
-    if (m_bCached) {
+    if (cached) {
         alloc.flags = ION_FLAG_CACHED;
     }
     alloc.heap_mask = heap_id;
@@ -332,6 +362,11 @@
     memInfo.fd = ion_info_fd.fd;
     memInfo.handle = ion_info_fd.handle;
     memInfo.size = alloc.len;
+    memInfo.cached = cached;
+    memInfo.heap_id = heap_id;
+
+    ALOGD("%s : ION buffer %p with size %d allocated",
+            __func__, memInfo.handle, size);
     return OK;
 
 ION_MAP_FAILED:
@@ -375,6 +410,168 @@
 }
 
 /*===========================================================================
+ * FUNCTION   : QCameraMemoryPool
+ *
+ * DESCRIPTION: default constructor of QCameraMemoryPool
+ *
+ * PARAMETERS : None
+ *
+ * RETURN     : None
+ *==========================================================================*/
+QCameraMemoryPool::QCameraMemoryPool()
+{
+    pthread_mutex_init(&mLock, NULL);
+}
+
+
+/*===========================================================================
+ * FUNCTION   : ~QCameraMemoryPool
+ *
+ * DESCRIPTION: deconstructor of QCameraMemoryPool
+ *
+ * PARAMETERS : None
+ *
+ * RETURN     : None
+ *==========================================================================*/
+QCameraMemoryPool::~QCameraMemoryPool()
+{
+    clear();
+    pthread_mutex_destroy(&mLock);
+}
+
+/*===========================================================================
+ * FUNCTION   : releaseBuffer
+ *
+ * DESCRIPTION: release one cached buffers
+ *
+ * PARAMETERS :
+ *   @memInfo : reference to struct that stores additional memory allocation info
+ *   @streamType: Type of stream the buffers belongs to
+ *
+ * RETURN     : none
+ *==========================================================================*/
+void QCameraMemoryPool::releaseBuffer(
+        struct QCameraMemory::QCameraMemInfo &memInfo,
+        cam_stream_type_t streamType)
+{
+    pthread_mutex_lock(&mLock);
+
+    mPools[streamType].push_back(memInfo);
+
+    pthread_mutex_unlock(&mLock);
+}
+
+/*===========================================================================
+ * FUNCTION   : clear
+ *
+ * DESCRIPTION: clears all cached buffers
+ *
+ * PARAMETERS : none
+ *
+ * RETURN     : none
+ *==========================================================================*/
+void QCameraMemoryPool::clear()
+{
+    pthread_mutex_lock(&mLock);
+
+    for (int i = CAM_STREAM_TYPE_DEFAULT; i < CAM_STREAM_TYPE_MAX; i++ ) {
+        List<struct QCameraMemory::QCameraMemInfo>::iterator it = mPools[i].begin();
+        for( ; it != mPools[i].end() ; it++) {
+            QCameraMemory::deallocOneBuffer(*it);
+        }
+
+        mPools[i].clear();
+    }
+
+    pthread_mutex_unlock(&mLock);
+}
+
+/*===========================================================================
+ * FUNCTION   : findBufferLocked
+ *
+ * DESCRIPTION: search for a appropriate cached buffer
+ *
+ * PARAMETERS :
+ *   @memInfo : reference to struct that stores additional memory allocation info
+ *   @heap_id : type of heap
+ *   @size    : size of the buffer
+ *   @cached  : whether the buffer should be cached
+ *   @streaType: type of stream this buffer belongs to
+ *
+ * RETURN     : int32_t type of status
+ *              NO_ERROR  -- success
+ *              none-zero failure code
+ *==========================================================================*/
+int QCameraMemoryPool::findBufferLocked(
+        struct QCameraMemory::QCameraMemInfo &memInfo,
+        int heap_id,
+        uint32_t size,
+        bool cached,
+        cam_stream_type_t streamType)
+{
+    int rc = NAME_NOT_FOUND;
+
+    if (mPools[streamType].empty()) {
+        return NAME_NOT_FOUND;
+    }
+
+    List<struct QCameraMemory::QCameraMemInfo>::iterator it = mPools[streamType].begin();
+    for( ; it != mPools[streamType].end() ; it++) {
+        if( ((*it).size >= size) &&
+            ((*it).heap_id == heap_id) &&
+            ((*it).cached == cached) ) {
+            memInfo = *it;
+            ALOGE("%s : Found buffer %p size %d",
+                    __func__, memInfo.handle, memInfo.size);
+            mPools[streamType].erase(it);
+            rc = NO_ERROR;
+            break;
+        }
+    }
+
+    return rc;
+}
+
+/*===========================================================================
+ * FUNCTION   : allocateBuffer
+ *
+ * DESCRIPTION: allocates a buffer from the memory pool,
+ *              it will re-use cached buffers if possible
+ *
+ * PARAMETERS :
+ *   @memInfo : reference to struct that stores additional memory allocation info
+ *   @heap_id : type of heap
+ *   @size    : size of the buffer
+ *   @cached  : whether the buffer should be cached
+ *   @streaType: type of stream this buffer belongs to
+ *
+ * RETURN     : int32_t type of status
+ *              NO_ERROR  -- success
+ *              none-zero failure code
+ *==========================================================================*/
+int QCameraMemoryPool::allocateBuffer(
+        struct QCameraMemory::QCameraMemInfo &memInfo,
+        int heap_id,
+        int size,
+        bool cached,
+        cam_stream_type_t streamType)
+{
+    int rc = NO_ERROR;
+
+    pthread_mutex_lock(&mLock);
+
+    rc = findBufferLocked(memInfo, heap_id, size, cached, streamType);
+    if (NAME_NOT_FOUND == rc ) {
+        ALOGE("%s : Buffer not found!", __func__);
+        rc = QCameraMemory::allocOneBuffer(memInfo, heap_id, size, cached);
+    }
+
+    pthread_mutex_unlock(&mLock);
+
+    return rc;
+}
+
+/*===========================================================================
  * FUNCTION   : QCameraHeapMemory
  *
  * DESCRIPTION: constructor of QCameraHeapMemory for ion memory used internally in HAL
@@ -620,8 +817,10 @@
  * RETURN     : none
  *==========================================================================*/
 QCameraStreamMemory::QCameraStreamMemory(camera_request_memory getMemory,
-                                         bool cached)
-    :QCameraMemory(cached),
+        bool cached,
+        QCameraMemoryPool *pool,
+        cam_stream_type_t streamType)
+    :QCameraMemory(cached, pool, streamType),
      mGetMemory(getMemory)
 {
     for (int i = 0; i < MM_CAMERA_MAX_NUM_FRAMES; i ++)
@@ -1163,7 +1362,7 @@
     struct ion_fd_data ion_info_fd;
     memset(&ion_info_fd, 0, sizeof(ion_info_fd));
 
-    ALOGI(" %s : E ", __FUNCTION__);
+    ALOGD(" %s : E ", __func__);
 
     if (!mWindow) {
         ALOGE("Invalid native window");
@@ -1325,7 +1524,7 @@
     }
 
 end:
-    ALOGI(" %s : X ",__func__);
+    ALOGD(" %s : X ",__func__);
     return ret;
 }
 
diff --git a/QCamera2/HAL/QCameraMem.h b/QCamera2/HAL/QCameraMem.h
index 1dee2cd..18a2a24 100644
--- a/QCamera2/HAL/QCameraMem.h
+++ b/QCamera2/HAL/QCameraMem.h
@@ -32,6 +32,7 @@
 
 #include <hardware/camera.h>
 #include <utils/Mutex.h>
+#include <utils/List.h>
 
 extern "C" {
 #include <sys/types.h>
@@ -41,6 +42,8 @@
 
 namespace qcamera {
 
+class QCameraMemoryPool;
+
 // Base class for all memory types. Abstract.
 class QCameraMemory {
 
@@ -61,29 +64,69 @@
     virtual int getMatchBufIndex(const void *opaque, bool metadata) const = 0;
     virtual void *getPtr(int index) const= 0;
 
-    QCameraMemory(bool cached);
+    QCameraMemory(bool cached,
+                  QCameraMemoryPool *pool = NULL,
+                  cam_stream_type_t streamType = CAM_STREAM_TYPE_DEFAULT);
     virtual ~QCameraMemory();
 
     void getBufDef(const cam_frame_len_offset_t &offset,
                 mm_camera_buf_def_t &bufDef, int index) const;
 
 protected:
+
+    friend class QCameraMemoryPool;
+
     struct QCameraMemInfo {
         int fd;
         int main_ion_fd;
         struct ion_handle *handle;
         uint32_t size;
+        bool cached;
+        int heap_id;
     };
 
     int alloc(int count, int size, int heap_id);
     void dealloc();
-    int allocOneBuffer(struct QCameraMemInfo &memInfo, int heap_id, int size);
-    void deallocOneBuffer(struct QCameraMemInfo &memInfo);
+    static int allocOneBuffer(struct QCameraMemInfo &memInfo,
+                              int heap_id,
+                              int size,
+                              bool cached);
+    static void deallocOneBuffer(struct QCameraMemInfo &memInfo);
     int cacheOpsInternal(int index, unsigned int cmd, void *vaddr);
 
     bool m_bCached;
     int mBufferCount;
     struct QCameraMemInfo mMemInfo[MM_CAMERA_MAX_NUM_FRAMES];
+    QCameraMemoryPool *mMemoryPool;
+    cam_stream_type_t mStreamType;
+};
+
+class QCameraMemoryPool {
+
+public:
+
+    QCameraMemoryPool();
+    virtual ~QCameraMemoryPool();
+
+    int allocateBuffer(struct QCameraMemory::QCameraMemInfo &memInfo,
+                       int heap_id,
+                       int size,
+                       bool cached,
+                       cam_stream_type_t streamType);
+    void releaseBuffer(struct QCameraMemory::QCameraMemInfo &memInfo,
+                       cam_stream_type_t streamType);
+    void clear();
+
+protected:
+
+    int findBufferLocked(struct QCameraMemory::QCameraMemInfo &memInfo,
+                         int heap_id,
+                         uint32_t size,
+                         bool cached,
+                         cam_stream_type_t streamType);
+
+    android::List<QCameraMemory::QCameraMemInfo> mPools[CAM_STREAM_TYPE_MAX];
+    pthread_mutex_t mLock;
 };
 
 // Internal heap memory is used for memories used internally
@@ -110,7 +153,10 @@
 // framework. They are allocated from /dev/ion or gralloc.
 class QCameraStreamMemory : public QCameraMemory {
 public:
-    QCameraStreamMemory(camera_request_memory getMemory, bool cached);
+    QCameraStreamMemory(camera_request_memory getMemory,
+                        bool cached,
+                        QCameraMemoryPool *pool = NULL,
+                        cam_stream_type_t streamType = CAM_STREAM_TYPE_DEFAULT);
     virtual ~QCameraStreamMemory();
 
     virtual int allocate(int count, int size);
diff --git a/QCamera2/HAL/QCameraStateMachine.cpp b/QCamera2/HAL/QCameraStateMachine.cpp
index f90e24e..2b4f149 100644
--- a/QCamera2/HAL/QCameraStateMachine.cpp
+++ b/QCamera2/HAL/QCameraStateMachine.cpp
@@ -352,6 +352,10 @@
         {
             bool needRestart = false;
             rc = m_parent->updateParameters((char*)payload, needRestart);
+            if (needRestart) {
+                // Clear memory pools
+                m_parent->m_memoryPool.clear();
+            }
             if (rc == NO_ERROR) {
                 rc = m_parent->commitParameterChanges();
             }
@@ -670,6 +674,8 @@
                 if (needRestart) {
                     // need restart preview for parameters to take effect
                     m_parent->unpreparePreview();
+                    // Clear memory pools
+                    m_parent->m_memoryPool.clear();
                     // commit parameter changes to server
                     m_parent->commitParameterChanges();
                     // prepare preview again
@@ -953,6 +959,8 @@
                     // need restart preview for parameters to take effect
                     // stop preview
                     m_parent->stopPreview();
+                    // Clear memory pools
+                    m_parent->m_memoryPool.clear();
                     // commit parameter changes to server
                     m_parent->commitParameterChanges();
                     // start preview again
@@ -2494,6 +2502,8 @@
                     // need restart preview for parameters to take effect
                     // stop preview
                     m_parent->stopPreview();
+                    // Clear memory pools
+                    m_parent->m_memoryPool.clear();
                     // commit parameter changes to server
                     m_parent->commitParameterChanges();
                     // start preview again