diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index be017ad4..e89d6a33 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -33,7 +33,8 @@ class PipelineHandler : public std::enable_shared_from_this, public Object { public: - PipelineHandler(CameraManager *manager); + PipelineHandler(CameraManager *manager, + unsigned int maxQueuedRequestsDevice = 32); virtual ~PipelineHandler(); virtual bool match(DeviceEnumerator *enumerator) = 0; @@ -80,6 +81,7 @@ protected: virtual void releaseDevice(Camera *camera); CameraManager *manager_; + const unsigned int maxQueuedRequestsDevice_; private: void unlockMediaDevices(); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index b50eda5e..e5f9e55c 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -62,13 +62,17 @@ LOG_DEFINE_CATEGORY(Pipeline) /** * \brief Construct a PipelineHandler instance * \param[in] manager The camera manager + * \param[in] maxQueuedRequestsDevice The maximum number of requests queued to + * the device * * In order to honour the std::enable_shared_from_this<> contract, * PipelineHandler instances shall never be constructed manually, but always * through the PipelineHandlerFactoryBase::create() function. */ -PipelineHandler::PipelineHandler(CameraManager *manager) - : manager_(manager), useCount_(0) +PipelineHandler::PipelineHandler(CameraManager *manager, + unsigned int maxQueuedRequestsDevice) + : manager_(manager), maxQueuedRequestsDevice_(maxQueuedRequestsDevice), + useCount_(0) { } @@ -360,20 +364,28 @@ void PipelineHandler::unlockMediaDevices() */ void PipelineHandler::stop(Camera *camera) { + /* + * Take all waiting requests so that they are not requeued in response + * to completeRequest() being called inside stopDevice(). Cancel them + * after the device to keep them in order. + */ + Camera::Private *data = camera->_d(); + std::queue waitingRequests; + waitingRequests.swap(data->waitingRequests_); + /* Stop the pipeline handler and let the queued requests complete. */ stopDevice(camera); - Camera::Private *data = camera->_d(); - /* Cancel and signal as complete all waiting requests. */ - while (!data->waitingRequests_.empty()) { - Request *request = data->waitingRequests_.front(); - data->waitingRequests_.pop(); + while (!waitingRequests.empty()) { + Request *request = waitingRequests.front(); + waitingRequests.pop(); cancelRequest(request); } /* Make sure no requests are pending. */ ASSERT(data->queuedRequests_.empty()); + ASSERT(data->waitingRequests_.empty()); data->requestSequence_ = 0; } @@ -430,9 +442,9 @@ void PipelineHandler::registerRequest(Request *request) * requests which have to be prepared to make sure they are ready for being * queued to the pipeline handler. * - * The queue of waiting requests is iterated and all prepared requests are - * passed to the pipeline handler in the same order they have been queued by - * calling this function. + * The queue of waiting requests is iterated and up to \a + * maxQueuedRequestsDevice_ prepared requests are passed to the pipeline handler + * in the same order they have been queued by calling this function. * * If a Request fails during the preparation phase or if the pipeline handler * fails in queuing the request to the hardware the request is cancelled. @@ -487,12 +499,19 @@ void PipelineHandler::doQueueRequests(Camera *camera) { Camera::Private *data = camera->_d(); while (!data->waitingRequests_.empty()) { + if (data->queuedRequests_.size() == maxQueuedRequestsDevice_) + break; + Request *request = data->waitingRequests_.front(); if (!request->_d()->prepared_) break; - doQueueRequest(request); + /* + * Pop the request first, in case doQueueRequests() is called + * recursively from within doQueueRequest() + */ data->waitingRequests_.pop(); + doQueueRequest(request); } } @@ -568,6 +587,9 @@ void PipelineHandler::completeRequest(Request *request) data->queuedRequests_.pop_front(); camera->requestComplete(req); } + + /* Allow any waiting requests to be queued to the pipeline. */ + doQueueRequests(camera); } /** @@ -768,6 +790,20 @@ void PipelineHandler::disconnect() * constant for the whole lifetime of the pipeline handler. */ +/** + * \var PipelineHandler::maxQueuedRequestsDevice_ + * \brief The maximum number of requests the pipeline handler shall queue to the + * device + * + * maxQueuedRequestsDevice_ limits the number of request that the + * pipeline handler shall queue to the underlying hardware, in order to + * saturate the pipeline with requests. The application may choose to queue + * as many requests as it desires, however only maxQueuedRequestsDevice_ + * requests will be queued to the hardware at a given point in time. The + * remaining requests will be kept waiting in the internal waiting + * queue, to be queued at a later stage. + */ + /** * \fn PipelineHandler::name() * \brief Retrieve the pipeline handler name