From patchwork Thu Mar 12 15:10:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 26278 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id AC224C32B5 for ; Thu, 12 Mar 2026 16:00:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5787462663; Thu, 12 Mar 2026 17:00:19 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="Xr8J3aGe"; dkim-atps=neutral Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com [IPv6:2a00:1450:4864:20::333]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BF8B86262E for ; Thu, 12 Mar 2026 17:00:16 +0100 (CET) Received: by mail-wm1-x333.google.com with SMTP id 5b1f17b1804b1-48334ee0aeaso11220835e9.1 for ; Thu, 12 Mar 2026 09:00:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1773331216; x=1773936016; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=c1qwLKbT2jFaV25UIuR5ZCzbWFQsDZVYMtj/3BK5ZBc=; b=Xr8J3aGeS8j5Pw0yKH9zCAYx3Yj0WHc+nhBArd7RcyaqqjfWekBcEUcfuyIOR9ZsBH 6PNLMoaMafLTIOh2q3mI+ZncGmZTUSp6frJ7eMkaHJBHoHSGNVek9pdwRYJY5X39Tq3O +igqbiYaYOAXVEyVnHlqqVVr4UxAAvIVxizzDKqW/uHydMRwiTtuPdoVO8pIoxk+HU9d HOx5OcpnvutSd6ECo5TR45h41zZC+Qec08oiE/1X+a6Aq+hhQ3NlzZm6ld7TbC5TOFdO tpfQXaIAcJXxmRkLp2S7VbGGlnW6U1Tmqbv017y9qt00SxcXuXMN7FTSMHxHbWkh3g57 6jag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1773331216; x=1773936016; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=c1qwLKbT2jFaV25UIuR5ZCzbWFQsDZVYMtj/3BK5ZBc=; b=iDzbwMOF+WXg6ksx2tx66gSNUyPLdD9DOFExsVQ/bUqJYPIjjSzPmSfhBlL05J+Gci EsMdoHNHAty3Uv6t0WiH+ibyhkz1PXN2CqiNAxMJ08tt4KcsTnqJCsVAFSducHxVEq0R qUDVFFM5tbVwb52bqq/WEABz3aF5MBd+vXYXJPU6t8HiJlcefaGOm2tVh9XCABe2Gtvz MFa7WuMsyavF0J+JJlZCHMjUQuY0wnIg2+QwHJk2Rqf3CsedkhSCJFXGgfook/8vIodZ cenkummP+SALZoQqCRygMEUke/PTth8Dw5Sn6WaM52iiM4vOn7EXB4Mi5j1VG5Qmo3xx vPQw== X-Gm-Message-State: AOJu0YzbJDmsl6oXr5g2O4Z0kpbEPuJhzXPYqeCzwziT1vaZyvZuIyvT 3IXIgdBPPogRqZNxtfCBvh0o/3xTIBEl9vP4IPWbrO/vwtWYCsAGgJ0pHyz+Gn2oM+igHLbx+cq H3Mw2kDY= X-Gm-Gg: ATEYQzzwXbVUpkVs8KHArpXNLHRyiqfpYkBk4E0lVO7oHFUDu7XuAYVzhFEZ9TpIpGE lOOrJCW3DPDzUxPypZTApAZtwziWHEdYjUhipBL0Wzcm8vLJQutNxq2HRL4JcraV0mjSrDwFuCg 8OSzFjytXDbjgX3PaPAwjBnWnVUCV4/Y/U6PI7NDl2GuhrOUt4xGmQz6rmly9iH/hT0E9WJ5lPD KC/7vtCOOteH+TjDZ2M9VqN/Ho89lBTV87Kw3YpRGErwlRl08R6Bf5XuYUCBtub3lHiJoUfsN1R nEg5c1LhGjsbjkXFPDHSNtLfPsYAVfG6ryvvm0GKN9xNL+O+dGfhOXeTpRRlhyEElqe6f7/Rt1I rrxoul3SiQb4xbom+wu12PfIyVOiW5/fedXZoIWZjezPYfHMS14fa86/b4wONuuHm/EmRkgSFi0 IKVyWR4FNd4oPRF1VLkBwohB+UWCMyeYETd+gQ9cvKrw5Mk3cxMeSPIAXY3lFQbs5jORnDud8cI d9ffDqARbvecFOVYN+oNcOK45tfbxiyNemQUkjxFA== X-Received: by 2002:a05:600c:3b9f:b0:480:690e:f14a with SMTP id 5b1f17b1804b1-4854b0d18e3mr122688975e9.14.1773331215489; Thu, 12 Mar 2026 09:00:15 -0700 (PDT) Received: from davidp-pi5.pitowers.org ([2a00:1098:3142:1f:88ea:c658:5b20:5e46]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe1abf84sm9084111f8f.14.2026.03.12.09.00.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2026 09:00:14 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Cc: David Plowman Subject: [RFC PATCH v2 1/1] libcamera: Add independent queue for ControlLists Date: Thu, 12 Mar 2026 15:10:38 +0000 Message-ID: <20260312160009.18654-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260312160009.18654-1-david.plowman@raspberrypi.com> References: <20260312160009.18654-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add `Camera::queueControls()` whose purpose is to apply controls as soon as possible, without going through `Request::controls()`. A new virtual function `PipelineHandler::queueControlsDevice()` is provided for pipeline handler to implement fast-tracked application of controls. If the pipeline handler does not implement that functionality, or it fails, then a fallback mechanism is used. The controls will be saved for later, and they will be merged into the control list of the next available request sent to the pipeline handler (`Camera::Private::waitingRequests_`). This patch is derived directly from Barnabas's previous verion that implemented the same idea but with a single ControlList, rather than allowing multiple ControlLists to be queued up for consecutive frames. Signed-off-by: David Plowman --- include/libcamera/camera.h | 1 + include/libcamera/internal/camera.h | 1 + include/libcamera/internal/pipeline_handler.h | 7 ++ src/libcamera/camera.cpp | 61 +++++++++++++++ src/libcamera/pipeline_handler.cpp | 76 +++++++++++++++++++ 5 files changed, 146 insertions(+) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index b24a2974..93a484e4 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -147,6 +147,7 @@ public: std::unique_ptr createRequest(uint64_t cookie = 0); int queueRequest(Request *request); + int queueControls(ControlList &&controls); int start(const ControlList *controls = nullptr); int stop(); diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h index 8a2e9ed5..17dda925 100644 --- a/include/libcamera/internal/camera.h +++ b/include/libcamera/internal/camera.h @@ -38,6 +38,7 @@ public: std::list queuedRequests_; std::queue waitingRequests_; + std::queue queuedControls_; ControlInfoMap controlInfo_; ControlList properties_; diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index b4f97477..c25213de 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -57,6 +57,7 @@ public: void registerRequest(Request *request); void queueRequest(Request *request); + int queueControls(Camera *camera, ControlList controls); bool completeBuffer(Request *request, FrameBuffer *buffer); void completeRequest(Request *request); @@ -76,6 +77,12 @@ protected: unsigned int useCount() const { return useCount_; } virtual int queueRequestDevice(Camera *camera, Request *request) = 0; + + virtual int queueControlsDevice([[maybe_unused]] Camera *camera, [[maybe_unused]] const ControlList &controls) + { + return -EOPNOTSUPP; + } + virtual void stopDevice(Camera *camera) = 0; virtual bool acquireDevice(Camera *camera); diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index f724a1be..f0244707 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -637,6 +637,16 @@ Camera::Private::~Private() * queued requests was reached. */ +/** + * \var Camera::Private::queuedControls_ + * \brief The queue of pending control lists + * + * This queue maintains a list of all the control lists that need to be sent + * to the pipeline handler with subsequent requests. The top item in the queue + * will always be sent with the next request going to + * PipelineHandler::queueRequestDevice(). + */ + /** * \var Camera::Private::controlInfo_ * \brief The set of controls supported by the camera @@ -1378,6 +1388,57 @@ int Camera::queueRequest(Request *request) return 0; } +/** + * \brief Queue controls to be applied as soon as possible + * \param[in] controls The list of controls to queue + * + * This function tries to ensure that the controls in \a controls are applied + * to the camera as soon as possible. If there are still pending controls waiting + * to be applied (because of previous calls to Camera::queueControls), then + * these controls will be applied as soon as possible on a frame after those. + * + * The exact guarantees are camera dependent, but it is guaranteed that the + * controls will be applied no later than with the next \ref Request"request" + * that the application \ref Camera::queueRequest() "queues" (after any requests + * have been *used up" for sending previously queued controls). + * + * \context This function is \threadsafe. It may only be called when the camera + * is in the Running state as defined in \ref camera_operation. + * + * \return 0 on success or a negative error code otherwise + * \retval -ENODEV The camera has been disconnected from the system + * \retval -EACCES The camera is not running + */ +int Camera::queueControls(ControlList &&controls) +{ + Private *const d = _d(); + + /* + * Like requests, controls can't be queued if the camera is not running. + * Controls can be applied immediately when the camera starts using the + * Camera::Start method. + */ + + int ret = d->isAccessAllowed(Private::CameraRunning); + if (ret < 0) + return ret; + + /* + * We want to be able to queue empty control lists, as this gives a way of + * forcing another frame with the same controls as last time, before queueing + * another control list that might change them again. + */ + + patchControlList(controls); + + /* + * \todo Or `ConnectionTypeBlocking` to get the return value? + */ + d->pipe_->invokeMethod(&PipelineHandler::queueControls, ConnectionTypeQueued, this, std::move(controls)); + + return 0; +} + /** * \brief Start capture from camera * \param[in] controls Controls to be applied before starting the Camera diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 5c469e5b..1a87b28c 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -398,6 +398,12 @@ void PipelineHandler::stop(Camera *camera) ASSERT(data->queuedRequests_.empty()); ASSERT(data->waitingRequests_.empty()); + /* + * Clear out any unapplied controls. If an application wants to be + * sure controls have been applied, it should wait before stopping. + */ + data->queuedControls_ = {}; + data->requestSequence_ = 0; } @@ -477,6 +483,49 @@ void PipelineHandler::queueRequest(Request *request) request->_d()->prepare(300ms); } +/** + * \brief Queue controls to apply as soon as possible + * \param[in] camera The camera + * \param[in] controls The controls to apply + * + * This function tries to queue \a controls immediately to the device by + * calling queueControlsDevice(). If that fails, then a fallback mechanism + * is used to ensure that \a controls will be merged into the control list + * of the next available request submitted to the pipeline handler. + * + * \context This function is called from the CameraManager thread. + */ +int PipelineHandler::queueControls(Camera *camera, ControlList controls) +{ + Camera::Private *data = camera->_d(); + int ret = queueControlsDevice(camera, controls); + + /* + * Don't worry about later request's controls overriding the ones + * sent here - the application needs to deal with that. + */ + + if (ret == -EOPNOTSUPP) { + /* + * Fall back to adding the controls to the next request that enters the + * pipeline handler. See PipelineHandler::doQueueRequest(). + */ + data->queuedControls_.push(std::move(controls)); + + /* Counts as "success". */ + ret = 0; + + } else if (ret < 0) { + /* + * The pipeline handler is claiming to support queueControlsDevice, + * but it has failed. This is an error. + */ + LOG(Pipeline, Debug) << "Fast tracking controls failed: " << res; + } + + return ret; +} + /** * \brief Queue one requests to the device */ @@ -495,9 +544,21 @@ void PipelineHandler::doQueueRequest(Request *request) return; } + if (!data->queuedControls_.empty()) { + /* + * Note that `ControlList::MergePolicy::KeepExisting` is used. This is + * needed to ensure that if `request` is newer than pendingControls_, + * then its controls take precedence. + */ + request->controls().merge(data->queuedControls_.front(), + ControlList::MergePolicy::KeepExisting); + } + int ret = queueRequestDevice(camera, request); if (ret) cancelRequest(request); + else if (!data->queuedControls_.empty()) + data->queuedControls_.pop(); } /** @@ -543,6 +604,21 @@ void PipelineHandler::doQueueRequests(Camera *camera) * \return 0 on success or a negative error code otherwise */ +/** + * \fn PipelineHandler::queueControlsDevice() + * \brief Queue controls to be applied as soon as possible + * \param[in] camera The camera + * \param[in] controls The controls to apply + * + * This function queues \a controls to \a camera so that they can be + * applied as soon as possible + * + * \context This function is called from the CameraManager thread. + * + * \return 0 on success or a negative error code otherwise + * \return -EOPNOTSUPP if fast-tracking controls is not supported + */ + /** * \brief Complete a buffer for a request * \param[in] request The request the buffer belongs to