Camera3: add dynamic buffer count water mark support

The dynamic buffer count water mark starts from zero, and grows when the max of
hand-out buffer counts grow, until reach to the max allowed total buffer count.
For the case where the clients almost always uses less number of buffers than
the max allowed total buffer count, the dynamic buffer count water mark will
remain low, and the memory footprint will be smaller.

Change-Id: I305f40232f4740d3e9bedf14ee9b76a81e29e244
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
index 6d79167..73d9d3d 100644
--- a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
@@ -113,9 +113,10 @@
     currentStreamSet.streamInfoMap.add(streamId, streamInfo);
     currentStreamSet.handoutBufferCountMap.add(streamId, 0);
 
-    // The watermark should be the max of buffer count of each stream inside a stream set.
-    if (streamInfo.totalBufferCount > currentStreamSet.allocatedBufferWaterMark) {
-       currentStreamSet.allocatedBufferWaterMark = streamInfo.totalBufferCount;
+    // The max allowed buffer count should be the max of buffer count of each stream inside a stream
+    // set.
+    if (streamInfo.totalBufferCount > currentStreamSet.maxAllowedBufferCount) {
+       currentStreamSet.maxAllowedBufferCount = streamInfo.totalBufferCount;
     }
 
     return OK;
@@ -147,12 +148,15 @@
 
     // Remove the stream info from info map and recalculate the buffer count water mark.
     infoMap.removeItem(streamId);
-    currentSet.allocatedBufferWaterMark = 0;
+    currentSet.maxAllowedBufferCount = 0;
     for (size_t i = 0; i < infoMap.size(); i++) {
-        if (infoMap[i].totalBufferCount > currentSet.allocatedBufferWaterMark) {
-            currentSet.allocatedBufferWaterMark = infoMap[i].totalBufferCount;
+        if (infoMap[i].totalBufferCount > currentSet.maxAllowedBufferCount) {
+            currentSet.maxAllowedBufferCount = infoMap[i].totalBufferCount;
         }
     }
+    // Lazy solution: when a stream is unregistered, the streams will be reconfigured, reset
+    // the water mark and let it grow again.
+    currentSet.allocatedBufferWaterMark = 0;
 
     // Remove this stream set if all its streams have been removed.
     if (freeBufs.size() == 0 && handOutBufferCounts.size() == 0 && infoMap.size() == 0) {
@@ -181,6 +185,14 @@
     }
 
     StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+    BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
+    size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
+    if (bufferCount >= streamSet.maxAllowedBufferCount) {
+        ALOGE("%s: bufferCount (%zu) exceeds the max allowed buffer count (%zu) of this stream set",
+                __FUNCTION__, bufferCount, streamSet.maxAllowedBufferCount);
+        return INVALID_OPERATION;
+    }
+
     GraphicBufferEntry buffer =
             getFirstBufferFromBufferListLocked(streamSet.freeBuffers, streamId);
 
@@ -202,10 +214,10 @@
         }
 
         // Increase the hand-out buffer count for tracking purpose.
-        BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
-        size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
         bufferCount++;
-
+        if (bufferCount > streamSet.allocatedBufferWaterMark) {
+            streamSet.allocatedBufferWaterMark = bufferCount;
+        }
         *gb = buffer.graphicBuffer;
         *fenceFd = buffer.fenceFd;
         ALOGV("%s: get buffer (%p) with handle (%p).",
@@ -220,7 +232,8 @@
         if (streamSet.streamInfoMap.size() > 1) {
             for (size_t i = 0; i < streamSet.streamInfoMap.size(); i++) {
                 firstOtherStreamId = streamSet.streamInfoMap[i].streamId;
-                if (firstOtherStreamId != streamId) {
+                if (firstOtherStreamId != streamId &&
+                        hasBufferForStreamLocked(streamSet.freeBuffers, firstOtherStreamId)) {
                     break;
                 }
             }
@@ -299,6 +312,8 @@
         for (size_t j = 0; j < mStreamSetMap[i].streamInfoMap.size(); j++) {
             lines.appendFormat("          Stream %d\n", mStreamSetMap[i].streamInfoMap[j].streamId);
         }
+        lines.appendFormat("          Stream set max allowed buffer count: %zu\n",
+                mStreamSetMap[i].maxAllowedBufferCount);
         lines.appendFormat("          Stream set buffer count water mark: %zu\n",
                 mStreamSetMap[i].allocatedBufferWaterMark);
         lines.appendFormat("          Handout buffer counts:\n");
@@ -337,7 +352,7 @@
         return false;
     }
 
-    size_t bufferWaterMark = mStreamSetMap[setIdx].allocatedBufferWaterMark;
+    size_t bufferWaterMark = mStreamSetMap[setIdx].maxAllowedBufferCount;
     if (bufferWaterMark == 0 || bufferWaterMark > kMaxBufferCount) {
         ALOGW("%s: stream %d with stream set %d is not registered correctly to stream set map,"
                 " as the water mark (%zu) is wrong!",
@@ -379,6 +394,19 @@
     return OK;
 }
 
+bool Camera3BufferManager::hasBufferForStreamLocked(BufferList& buffers, int streamId) {
+    BufferList::iterator i = buffers.begin();
+    while (i != buffers.end()) {
+        ssize_t idx = i->indexOfKey(streamId);
+        if (idx != NAME_NOT_FOUND) {
+            return true;
+        }
+        i++;
+    }
+
+    return false;
+}
+
 Camera3BufferManager::GraphicBufferEntry Camera3BufferManager::getFirstBufferFromBufferListLocked(
         BufferList& buffers, int streamId) {
     // Try to get the first buffer from the free buffer list if there is one.