Message ID | 20250707075400.9079-3-stefan.klug@ideasonboard.com |
---|---|
State | New |
Headers | show |
Series |
|
Related | show |
Quoting Stefan Klug (2025-07-07 08:53:36) > Add a maxQueuedRequestsDevice constructor parameter to allow pipeline > handler classes to limit the maximum number of requests that get queued > to the device in queueRequestDevice(). > > The default value is set to an arbitrary number of 32 which is big > enough for all currently known use cases. > > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com> > > --- > > Changes in v2: > - Moved the fix to properly handle the waitingRequests queue when > stopping the device into this patch as they belong together > - Added documentation for maxQueuedRequestsDevice_ > - Fixed an issue with doQueueRequests beeing() called recursively > > Changes in v1: > - Used a const member variable to carry the maximum number of requests > - Improved commit message > - Added docs > --- > include/libcamera/internal/pipeline_handler.h | 4 +- > src/libcamera/pipeline_handler.cpp | 56 +++++++++++++++---- > 2 files changed, 48 insertions(+), 12 deletions(-) > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h > index be017ad47219..e89d6a33e398 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<PipelineHandler>, > 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 b50eda5e0f86..e8601be108f0 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<Request *> waitingRequests; > + waitingRequests.swap(data->waitingRequests_); Yes, this looks good - solves the issue and maintains the order. Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > + > /* 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,18 @@ 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 > + * > + * Some pipeline handlers do not work efficiently with too many requests queued > + * into the device. Still it is beneficial for the application to circulate more > + * than the minimum number of buffers. This variable limits the number of > + * requests that get queued to the device at once. The rest is kept in a > + * a waiting queue. > + */ > + > /** > * \fn PipelineHandler::name() > * \brief Retrieve the pipeline handler name > -- > 2.48.1 >
diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index be017ad47219..e89d6a33e398 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<PipelineHandler>, 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 b50eda5e0f86..e8601be108f0 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<Request *> 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,18 @@ 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 + * + * Some pipeline handlers do not work efficiently with too many requests queued + * into the device. Still it is beneficial for the application to circulate more + * than the minimum number of buffers. This variable limits the number of + * requests that get queued to the device at once. The rest is kept in a + * a waiting queue. + */ + /** * \fn PipelineHandler::name() * \brief Retrieve the pipeline handler name