Patch Detail
Show a patch.
GET /api/1.1/patches/23840/?format=api
{ "id": 23840, "url": "https://patchwork.libcamera.org/api/1.1/patches/23840/?format=api", "web_url": "https://patchwork.libcamera.org/patch/23840/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20250717125931.2848300-3-stefan.klug@ideasonboard.com>", "date": "2025-07-17T12:59:22", "name": "[v3,2/5] libcamera: pipeline_handler: Allow to limit the number of queued requests", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "f9b71fdea9c95e2a92301949d85a8c3ac3d02307", "submitter": { "id": 184, "url": "https://patchwork.libcamera.org/api/1.1/people/184/?format=api", "name": "Stefan Klug", "email": "stefan.klug@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/23840/mbox/", "series": [ { "id": 5294, "url": "https://patchwork.libcamera.org/api/1.1/series/5294/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5294", "date": "2025-07-17T12:59:20", "name": "rkisp1: Allow usage of more than 4 buffers", "version": 3, "mbox": "https://patchwork.libcamera.org/series/5294/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/23840/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/23840/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 14BC1C3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 17 Jul 2025 12:59:45 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C496468F8D;\n\tThu, 17 Jul 2025 14:59:44 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A204B68F7A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 17 Jul 2025 14:59:41 +0200 (CEST)", "from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:7b93:8acd:d82d:248d])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id A0B641E74; \n\tThu, 17 Jul 2025 14:59:07 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"gtlnfJNJ\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1752757147;\n\tbh=FtP3LBEAlKfycED7DyqSkyT0cPc5DT8/6QdDfB3TVPs=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=gtlnfJNJpJlUTXWWfusfdDqT9AOrBrmhg/YBwVzAJ/lfXDDde7wOm0fdw36YTIzoo\n\t7VeiNqYioRMvUR0SQjFvrukeP+qH9fSXYe3I5JCj6Msox2Xu+MeQpa1aCcsisUE/h7\n\trlOQeJGiKJpFeX5+Nv3u9oRVuURVb33/0BU5oyX8=", "From": "Stefan Klug <stefan.klug@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Stefan Klug <stefan.klug@ideasonboard.com>, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, Umang Jain <uajain@igalia.com>,\n\t=?utf-8?q?Sven_P=C3=BCschel?= <s.pueschel@pengutronix.de>", "Subject": "[PATCH v3 2/5] libcamera: pipeline_handler: Allow to limit the\n\tnumber of queued requests", "Date": "Thu, 17 Jul 2025 14:59:22 +0200", "Message-ID": "<20250717125931.2848300-3-stefan.klug@ideasonboard.com>", "X-Mailer": "git-send-email 2.48.1", "In-Reply-To": "<20250717125931.2848300-1-stefan.klug@ideasonboard.com>", "References": "<20250717125931.2848300-1-stefan.klug@ideasonboard.com>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Add a maxQueuedRequestsDevice constructor parameter to allow pipeline\nhandler classes to limit the maximum number of requests that get queued\nto the device in queueRequestDevice().\n\nThe default value is set to an arbitrary number of 32 which is big\nenough for all currently known use cases.\n\nSigned-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nSigned-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\nReviewed-by: Umang Jain <uajain@igalia.com>\nTested-By: Sven Püschel<s.pueschel@pengutronix.de>\n\n---\nChanges in v3:\n- Updated documentation for maxQueuedRequestsDevice_\n- Collected tag\n\nChanges in v2:\n- Moved the fix to properly handle the waitingRequests queue when\n stopping the device into this patch as they belong together\n- Added documentation for maxQueuedRequestsDevice_\n- Fixed an issue with doQueueRequests() beeing called recursively\n\nChanges in v1:\n- Used a const member variable to carry the maximum number of requests\n- Improved commit message\n- Added docs\n---\n include/libcamera/internal/pipeline_handler.h | 4 +-\n src/libcamera/pipeline_handler.cpp | 58 +++++++++++++++----\n 2 files changed, 50 insertions(+), 12 deletions(-)", "diff": "diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h\nindex be017ad47219..e89d6a33e398 100644\n--- a/include/libcamera/internal/pipeline_handler.h\n+++ b/include/libcamera/internal/pipeline_handler.h\n@@ -33,7 +33,8 @@ class PipelineHandler : public std::enable_shared_from_this<PipelineHandler>,\n \t\t\tpublic Object\n {\n public:\n-\tPipelineHandler(CameraManager *manager);\n+\tPipelineHandler(CameraManager *manager,\n+\t\t\tunsigned int maxQueuedRequestsDevice = 32);\n \tvirtual ~PipelineHandler();\n \n \tvirtual bool match(DeviceEnumerator *enumerator) = 0;\n@@ -80,6 +81,7 @@ protected:\n \tvirtual void releaseDevice(Camera *camera);\n \n \tCameraManager *manager_;\n+\tconst unsigned int maxQueuedRequestsDevice_;\n \n private:\n \tvoid unlockMediaDevices();\ndiff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\nindex b50eda5e0f86..e5f9e55c9783 100644\n--- a/src/libcamera/pipeline_handler.cpp\n+++ b/src/libcamera/pipeline_handler.cpp\n@@ -62,13 +62,17 @@ LOG_DEFINE_CATEGORY(Pipeline)\n /**\n * \\brief Construct a PipelineHandler instance\n * \\param[in] manager The camera manager\n+ * \\param[in] maxQueuedRequestsDevice The maximum number of requests queued to\n+ * the device\n *\n * In order to honour the std::enable_shared_from_this<> contract,\n * PipelineHandler instances shall never be constructed manually, but always\n * through the PipelineHandlerFactoryBase::create() function.\n */\n-PipelineHandler::PipelineHandler(CameraManager *manager)\n-\t: manager_(manager), useCount_(0)\n+PipelineHandler::PipelineHandler(CameraManager *manager,\n+\t\t\t\t unsigned int maxQueuedRequestsDevice)\n+\t: manager_(manager), maxQueuedRequestsDevice_(maxQueuedRequestsDevice),\n+\t useCount_(0)\n {\n }\n \n@@ -360,20 +364,28 @@ void PipelineHandler::unlockMediaDevices()\n */\n void PipelineHandler::stop(Camera *camera)\n {\n+\t/*\n+\t * Take all waiting requests so that they are not requeued in response\n+\t * to completeRequest() being called inside stopDevice(). Cancel them\n+\t * after the device to keep them in order.\n+\t */\n+\tCamera::Private *data = camera->_d();\n+\tstd::queue<Request *> waitingRequests;\n+\twaitingRequests.swap(data->waitingRequests_);\n+\n \t/* Stop the pipeline handler and let the queued requests complete. */\n \tstopDevice(camera);\n \n-\tCamera::Private *data = camera->_d();\n-\n \t/* Cancel and signal as complete all waiting requests. */\n-\twhile (!data->waitingRequests_.empty()) {\n-\t\tRequest *request = data->waitingRequests_.front();\n-\t\tdata->waitingRequests_.pop();\n+\twhile (!waitingRequests.empty()) {\n+\t\tRequest *request = waitingRequests.front();\n+\t\twaitingRequests.pop();\n \t\tcancelRequest(request);\n \t}\n \n \t/* Make sure no requests are pending. */\n \tASSERT(data->queuedRequests_.empty());\n+\tASSERT(data->waitingRequests_.empty());\n \n \tdata->requestSequence_ = 0;\n }\n@@ -430,9 +442,9 @@ void PipelineHandler::registerRequest(Request *request)\n * requests which have to be prepared to make sure they are ready for being\n * queued to the pipeline handler.\n *\n- * The queue of waiting requests is iterated and all prepared requests are\n- * passed to the pipeline handler in the same order they have been queued by\n- * calling this function.\n+ * The queue of waiting requests is iterated and up to \\a\n+ * maxQueuedRequestsDevice_ prepared requests are passed to the pipeline handler\n+ * in the same order they have been queued by calling this function.\n *\n * If a Request fails during the preparation phase or if the pipeline handler\n * fails in queuing the request to the hardware the request is cancelled.\n@@ -487,12 +499,19 @@ void PipelineHandler::doQueueRequests(Camera *camera)\n {\n \tCamera::Private *data = camera->_d();\n \twhile (!data->waitingRequests_.empty()) {\n+\t\tif (data->queuedRequests_.size() == maxQueuedRequestsDevice_)\n+\t\t\tbreak;\n+\n \t\tRequest *request = data->waitingRequests_.front();\n \t\tif (!request->_d()->prepared_)\n \t\t\tbreak;\n \n-\t\tdoQueueRequest(request);\n+\t\t/*\n+\t\t * Pop the request first, in case doQueueRequests() is called\n+\t\t * recursively from within doQueueRequest()\n+\t\t */\n \t\tdata->waitingRequests_.pop();\n+\t\tdoQueueRequest(request);\n \t}\n }\n \n@@ -568,6 +587,9 @@ void PipelineHandler::completeRequest(Request *request)\n \t\tdata->queuedRequests_.pop_front();\n \t\tcamera->requestComplete(req);\n \t}\n+\n+\t/* Allow any waiting requests to be queued to the pipeline. */\n+\tdoQueueRequests(camera);\n }\n \n /**\n@@ -768,6 +790,20 @@ void PipelineHandler::disconnect()\n * constant for the whole lifetime of the pipeline handler.\n */\n \n+/**\n+ * \\var PipelineHandler::maxQueuedRequestsDevice_\n+ * \\brief The maximum number of requests the pipeline handler shall queue to the\n+ * device\n+ *\n+ * maxQueuedRequestsDevice_ limits the number of request that the\n+ * pipeline handler shall queue to the underlying hardware, in order to\n+ * saturate the pipeline with requests. The application may choose to queue\n+ * as many requests as it desires, however only maxQueuedRequestsDevice_\n+ * requests will be queued to the hardware at a given point in time. The\n+ * remaining requests will be kept waiting in the internal waiting\n+ * queue, to be queued at a later stage.\n+ */\n+\n /**\n * \\fn PipelineHandler::name()\n * \\brief Retrieve the pipeline handler name\n", "prefixes": [ "v3", "2/5" ] }