Camera: batching dequeueBuffer call for batched requests
Add plumbing to call into batched version of getBuffer call.
Also add some logic so batched version can be used even
when camera HAL is request buffer one at a time.
Camera3OutputStream will batch dequeue buffers on a batch unit.
This way we can save extra IPCs even when the client (camera
framework or camera HAL) does not call into batch dequeue API
(dequeueBuffers) directly.
As a safety measure, right before client is about to return
all outstanding buffers, all prefetched buffers will also
be returned.
TODO:
- Consider also batch queueBuffer path
- switch to batch API in requestStreamBuffer API
Test: GCA high speed recording
Bug: 113788435
Change-Id: I3cc0b62e8a6891d6ff6cad0e3c78e1d7abff6317
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index ffdb90b..4cb954e 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -629,7 +629,8 @@
}
// Wait for new buffer returned back if we are running into the limit.
- if (getHandoutOutputBufferCountLocked() == camera_stream::max_buffers) {
+ size_t numOutstandingBuffers = getHandoutOutputBufferCountLocked();
+ if (numOutstandingBuffers == camera_stream::max_buffers) {
ALOGV("%s: Already dequeued max output buffers (%d), wait for next returned one.",
__FUNCTION__, camera_stream::max_buffers);
nsecs_t waitStart = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -647,6 +648,14 @@
}
return res;
}
+
+ size_t updatedNumOutstandingBuffers = getHandoutOutputBufferCountLocked();
+ if (updatedNumOutstandingBuffers >= numOutstandingBuffers) {
+ ALOGE("%s: outsanding buffer count goes from %zu to %zu, "
+ "getBuffer(s) call must not run in parallel!", __FUNCTION__,
+ numOutstandingBuffers, updatedNumOutstandingBuffers);
+ return INVALID_OPERATION;
+ }
}
res = getBufferLocked(buffer, surface_ids);
@@ -898,6 +907,12 @@
ALOGE("%s: This type of stream does not support output", __FUNCTION__);
return INVALID_OPERATION;
}
+
+status_t Camera3Stream::getBuffersLocked(std::vector<OutstandingBuffer>*) {
+ ALOGE("%s: This type of stream does not support output", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
status_t Camera3Stream::returnBufferLocked(const camera_stream_buffer &,
nsecs_t, const std::vector<size_t>&) {
ALOGE("%s: This type of stream does not support output", __FUNCTION__);
@@ -971,6 +986,80 @@
mBufferFreedListener = listener;
}
+status_t Camera3Stream::getBuffers(std::vector<OutstandingBuffer>* buffers,
+ nsecs_t waitBufferTimeout) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+ status_t res = OK;
+
+ if (buffers == nullptr) {
+ ALOGI("%s: buffers must not be null!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ size_t numBuffersRequested = buffers->size();
+ if (numBuffersRequested == 0) {
+ ALOGE("%s: 0 buffers are requested!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ // This function should be only called when the stream is configured already.
+ if (mState != STATE_CONFIGURED) {
+ ALOGE("%s: Stream %d: Can't get buffers if stream is not in CONFIGURED state %d",
+ __FUNCTION__, mId, mState);
+ if (mState == STATE_ABANDONED) {
+ return DEAD_OBJECT;
+ } else {
+ return INVALID_OPERATION;
+ }
+ }
+
+ size_t numOutstandingBuffers = getHandoutOutputBufferCountLocked();
+ // Wait for new buffer returned back if we are running into the limit.
+ while (numOutstandingBuffers + numBuffersRequested > camera_stream::max_buffers) {
+ ALOGV("%s: Already dequeued %zu output buffers and requesting %zu (max is %d), waiting.",
+ __FUNCTION__, numOutstandingBuffers, numBuffersRequested,
+ camera_stream::max_buffers);
+ nsecs_t waitStart = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (waitBufferTimeout < kWaitForBufferDuration) {
+ waitBufferTimeout = kWaitForBufferDuration;
+ }
+ res = mOutputBufferReturnedSignal.waitRelative(mLock, waitBufferTimeout);
+ nsecs_t waitEnd = systemTime(SYSTEM_TIME_MONOTONIC);
+ mBufferLimitLatency.add(waitStart, waitEnd);
+ if (res != OK) {
+ if (res == TIMED_OUT) {
+ ALOGE("%s: wait for output buffer return timed out after %lldms (max_buffers %d)",
+ __FUNCTION__, waitBufferTimeout / 1000000LL,
+ camera_stream::max_buffers);
+ }
+ return res;
+ }
+ size_t updatedNumOutstandingBuffers = getHandoutOutputBufferCountLocked();
+ if (updatedNumOutstandingBuffers >= numOutstandingBuffers) {
+ ALOGE("%s: outsanding buffer count goes from %zu to %zu, "
+ "getBuffer(s) call must not run in parallel!", __FUNCTION__,
+ numOutstandingBuffers, updatedNumOutstandingBuffers);
+ return INVALID_OPERATION;
+ }
+ numOutstandingBuffers = updatedNumOutstandingBuffers;
+ }
+
+ res = getBuffersLocked(buffers);
+ if (res == OK) {
+ for (auto& outstandingBuffer : *buffers) {
+ camera_stream_buffer* buffer = outstandingBuffer.outBuffer;
+ fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/true);
+ if (buffer->buffer) {
+ Mutex::Autolock l(mOutstandingBuffersLock);
+ mOutstandingBuffers.push_back(*buffer->buffer);
+ }
+ }
+ }
+
+ return res;
+}
+
}; // namespace camera3
}; // namespace android