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/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 4b5889c..c835f51 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -189,6 +189,65 @@
return OK;
}
+status_t Camera3OutputStream::getBuffersLocked(std::vector<OutstandingBuffer>* outBuffers) {
+ status_t res;
+
+ if ((res = getBufferPreconditionCheckLocked()) != OK) {
+ return res;
+ }
+
+ if (mUseBufferManager) {
+ ALOGE("%s: stream %d is managed by buffer manager and does not support batch operation",
+ __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+
+ sp<Surface> consumer = mConsumer;
+ /**
+ * Release the lock briefly to avoid deadlock for below scenario:
+ * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
+ * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
+ * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
+ * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
+ * StreamingProcessor lock.
+ * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
+ * and try to lock bufferQueue lock.
+ * Then there is circular locking dependency.
+ */
+ mLock.unlock();
+
+ size_t numBuffersRequested = outBuffers->size();
+ std::vector<Surface::BatchBuffer> buffers(numBuffersRequested);
+
+ nsecs_t dequeueStart = systemTime(SYSTEM_TIME_MONOTONIC);
+ res = consumer->dequeueBuffers(&buffers);
+ nsecs_t dequeueEnd = systemTime(SYSTEM_TIME_MONOTONIC);
+ mDequeueBufferLatency.add(dequeueStart, dequeueEnd);
+
+ mLock.lock();
+
+ if (res != OK) {
+ if (shouldLogError(res, mState)) {
+ ALOGE("%s: Stream %d: Can't dequeue %zu output buffers: %s (%d)",
+ __FUNCTION__, mId, numBuffersRequested, strerror(-res), res);
+ }
+ checkRetAndSetAbandonedLocked(res);
+ return res;
+ }
+ checkRemovedBuffersLocked();
+
+ /**
+ * FenceFD now owned by HAL except in case of error,
+ * in which case we reassign it to acquire_fence
+ */
+ for (size_t i = 0; i < numBuffersRequested; i++) {
+ handoutBufferLocked(*(outBuffers->at(i).outBuffer),
+ &(buffers[i].buffer->handle), /*acquireFence*/buffers[i].fenceFd,
+ /*releaseFence*/-1, CAMERA_BUFFER_STATUS_OK, /*output*/true);
+ }
+ return OK;
+}
+
status_t Camera3OutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
ANativeWindowBuffer* buffer, int anwReleaseFence,
const std::vector<size_t>&) {
@@ -200,6 +259,10 @@
nsecs_t timestamp, const std::vector<size_t>& surface_ids) {
ATRACE_HFR_CALL();
+ if (mHandoutTotalBufferCount == 1) {
+ returnPrefetchedBuffersLocked();
+ }
+
status_t res = returnAnyBufferLocked(buffer, timestamp, /*output*/true, surface_ids);
if (res != OK) {
@@ -580,11 +643,46 @@
* and try to lock bufferQueue lock.
* Then there is circular locking dependency.
*/
- sp<ANativeWindow> currentConsumer = mConsumer;
+ sp<Surface> consumer = mConsumer;
+ size_t remainingBuffers = camera_stream::max_buffers - mHandoutTotalBufferCount;
mLock.unlock();
+ std::unique_lock<std::mutex> batchLock(mBatchLock);
nsecs_t dequeueStart = systemTime(SYSTEM_TIME_MONOTONIC);
- res = currentConsumer->dequeueBuffer(currentConsumer.get(), anb, fenceFd);
+
+ if (mBatchSize == 1) {
+ sp<ANativeWindow> anw = consumer;
+ res = anw->dequeueBuffer(anw.get(), anb, fenceFd);
+ } else {
+ res = OK;
+ if (mBatchedBuffers.size() == 0) {
+ size_t batchSize = mBatchSize;
+ if (remainingBuffers == 0) {
+ ALOGE("%s: cannot get buffer while all buffers are handed out", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (batchSize > remainingBuffers) {
+ batchSize = remainingBuffers;
+ }
+ // Refill batched buffers
+ mBatchedBuffers.resize(batchSize);
+ res = consumer->dequeueBuffers(&mBatchedBuffers);
+ if (res != OK) {
+ ALOGE("%s: batch dequeueBuffers call failed! %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ mBatchedBuffers.clear();
+ }
+ }
+
+ if (res == OK) {
+ // Dispatch batch buffers
+ *anb = mBatchedBuffers.back().buffer;
+ *fenceFd = mBatchedBuffers.back().fenceFd;
+ mBatchedBuffers.pop_back();
+ }
+ }
+ batchLock.unlock();
+
nsecs_t dequeueEnd = systemTime(SYSTEM_TIME_MONOTONIC);
mDequeueBufferLatency.add(dequeueStart, dequeueEnd);
@@ -678,6 +776,8 @@
return OK;
}
+ returnPrefetchedBuffersLocked();
+
ALOGV("%s: disconnecting stream %d from native window", __FUNCTION__, getId());
res = native_window_api_disconnect(mConsumer.get(),
@@ -1013,6 +1113,52 @@
graphicBuffer->unlock();
}
+status_t Camera3OutputStream::setBatchSize(size_t batchSize) {
+ Mutex::Autolock l(mLock);
+ std::lock_guard<std::mutex> lock(mBatchLock);
+ if (batchSize == 0) {
+ ALOGE("%s: invalid batch size 0", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (mUseBufferManager) {
+ ALOGE("%s: batch operation is not supported with buffer manager", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (!isVideoStream()) {
+ ALOGE("%s: batch operation is not supported with non-video stream", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (batchSize != mBatchSize) {
+ if (mBatchedBuffers.size() != 0) {
+ ALOGE("%s: change batch size from %zu to %zu dynamically is not supported",
+ __FUNCTION__, mBatchSize, batchSize);
+ return INVALID_OPERATION;
+ }
+
+ if (camera_stream::max_buffers < batchSize) {
+ ALOGW("%s: batch size is capped by max_buffers %d", __FUNCTION__,
+ camera_stream::max_buffers);
+ batchSize = camera_stream::max_buffers;
+ }
+ mBatchSize = batchSize;
+ }
+ return OK;
+}
+
+void Camera3OutputStream::returnPrefetchedBuffersLocked() {
+ std::lock_guard<std::mutex> batchLock(mBatchLock);
+ if (mBatchedBuffers.size() != 0) {
+ ALOGW("%s: %zu extra prefetched buffers detected. Returning",
+ __FUNCTION__, mBatchedBuffers.size());
+
+ mConsumer->cancelBuffers(mBatchedBuffers);
+ mBatchedBuffers.clear();
+ }
+}
+
}; // namespace camera3
}; // namespace android