Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14ef48d8 authored by Yin-Chia Yeh's avatar Yin-Chia Yeh
Browse files

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
parent 0abfd799
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -913,6 +913,7 @@ status_t Camera3Device::convertMetadataListToRequestListLocked(
        for (auto& outputStream : (*firstRequest)->mOutputStreams) {
            if (outputStream->isVideoStream()) {
                (*firstRequest)->mBatchSize = requestList->size();
                outputStream->setBatchSize(requestList->size());
                break;
            }
        }
+5 −0
Original line number Diff line number Diff line
@@ -134,6 +134,11 @@ status_t Camera3FakeStream::updateStream(const std::vector<sp<Surface>> &/*outpu
    return INVALID_OPERATION;
}

status_t Camera3FakeStream::setBatchSize(size_t /*batchSize*/) {
    ALOGE("%s: this method is not supported!", __FUNCTION__);
    return INVALID_OPERATION;
}

}; // namespace camera3

}; // namespace android
+2 −0
Original line number Diff line number Diff line
@@ -98,6 +98,8 @@ class Camera3FakeStream :
            const std::vector<size_t> &removedSurfaceIds,
            KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);

    virtual status_t setBatchSize(size_t batchSize) override;

  protected:

    /**
+148 −2
Original line number Diff line number Diff line
@@ -189,6 +189,65 @@ status_t Camera3OutputStream::getBufferLocked(camera_stream_buffer *buffer,
    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 @@ status_t Camera3OutputStream::returnBufferLocked(
        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 @@ status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, i
         * 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 @@ status_t Camera3OutputStream::disconnectLocked() {
        return OK;
    }

    returnPrefetchedBuffersLocked();

    ALOGV("%s: disconnecting stream %d from native window", __FUNCTION__, getId());

    res = native_window_api_disconnect(mConsumer.get(),
@@ -1013,6 +1113,52 @@ void Camera3OutputStream::dumpImageToDisk(nsecs_t timestamp,
    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
+30 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H
#define ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H

#include <mutex>
#include <utils/RefBase.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
@@ -205,6 +206,19 @@ class Camera3OutputStream :
            const std::vector<size_t> &removedSurfaceIds,
            KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);

    /**
     * Set the batch size for buffer operations. The output stream will request
     * buffers from buffer queue on a batch basis. Currently only video streams
     * are allowed to set the batch size. Also if the stream is managed by
     * buffer manager (Surface group in Java API) then batching is also not
     * supported. Changing batch size on the fly while there is already batched
     * buffers in the stream is also not supported.
     * If the batch size is larger than the max dequeue count set
     * by the camera HAL, the batch size will be set to the max dequeue count
     * instead.
     */
    virtual status_t setBatchSize(size_t batchSize = 1) override;

    /**
     * Apply ZSL related consumer usage quirk.
     */
@@ -292,12 +306,26 @@ class Camera3OutputStream :
    // Whether to drop valid buffers.
    bool mDropBuffers;


    // Protecting batch states below, must be acquired after mLock
    std::mutex mBatchLock;

    // The batch size for buffer operation
    size_t mBatchSize = 1;

    // Prefetched buffers (ready to be handed to client)
    std::vector<Surface::BatchBuffer> mBatchedBuffers;

    // ---- End of mBatchLock protected scope ----

    /**
     * Internal Camera3Stream interface
     */
    virtual status_t getBufferLocked(camera_stream_buffer *buffer,
            const std::vector<size_t>& surface_ids);

    virtual status_t getBuffersLocked(/*out*/std::vector<OutstandingBuffer>* buffers) override;

    virtual status_t returnBufferLocked(
            const camera_stream_buffer &buffer,
            nsecs_t timestamp, const std::vector<size_t>& surface_ids);
@@ -330,6 +358,8 @@ class Camera3OutputStream :
    // Dump images to disk before returning to consumer
    void dumpImageToDisk(nsecs_t timestamp, ANativeWindowBuffer* anwBuffer, int fence);

    void returnPrefetchedBuffersLocked();

    static const int32_t kDequeueLatencyBinSize = 5; // in ms
    CameraLatencyHistogram mDequeueBufferLatency;

Loading