From patchwork Tue Apr 28 13:26:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 26585 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 574E9C32F6 for ; Tue, 28 Apr 2026 13:40:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A64B762FD2; Tue, 28 Apr 2026 15:40:00 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="eC+2Mlev"; dkim-atps=neutral Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5795262FA9 for ; Tue, 28 Apr 2026 15:39:58 +0200 (CEST) Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-48334ee0aeaso119535585e9.1 for ; Tue, 28 Apr 2026 06:39:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1777383598; x=1777988398; 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=DfyoUEdbNejVMNt5pagfVzjLrj4e3pi5RkF1g99Nv5w=; b=eC+2MlevifynfxlVaLOsoQCSxrqd70S9Eoq75Jm7vMZXXvktFDO5UI/cpAfJ1vmzh0 8wLWZ+wiFaz1+W1yf4DJuaYJmV9BjixT74+xrRfK4TnZ0nS+wmZ4b4yx8I56Lgr2YNRE PsZQMcS9DaAdC7CiAECpnzsbBdQHx7Frjqqd5tS/PWJjs5JYJwVWPG4l0eN5mHScbNjV UPOWOLaLF4hwTXBCk0kPAJ88Xr5a0fDqCfGQcUFIQ8WYxQl68y3rdryeny9mtLQRKn4+ fQTgOjGSrgTw3v93NohU3VDdd/GtQZ85tgtq3VMLzHhCpyCvX3Vr22/jWixANM+l+3vG 5gVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777383598; x=1777988398; 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=DfyoUEdbNejVMNt5pagfVzjLrj4e3pi5RkF1g99Nv5w=; b=j6Ivh7PTTrTyajqyf9b5/moi3ZUwz/+F96Jy5jrKTzCx5CpR4kGUaP15gRGrR4FvLt IWUGUJqMk1yf9Z4CylyeEEAhFDwpSbPygFkQ4LZ8CKeBgdksVmK1wBYHahqNdW7YB2SO TjGdXAjF2RaBK5LtEdmfybp8YBks3RlO+a0kQEoXEY4OdqB4rzZytqGByB49kQn7cBkG LTtFCh/nf9w+jU2qawbh4OQvj+pPgfGURXLJvkupmPrjKVrFamkl41Nn1VedHLokiyOu e7RG71sqBCAVOSacFKER7JJYWbgEbYHpiAkUBWhTQ2IeU8mTZZH6q2WVAsRSDEhg/K3t SmFQ== X-Gm-Message-State: AOJu0YyF569gbkhA6HBWra9BEIBK/o7X0IrRl2tkEOs7Bny4Uv8+nUQf nnxP/d0/Vm9ZWvJrursn4vCH+lmkhTpt34xw6/+kUWWniWm4WUjpF8zThZCWJOrg+BS8OQ/g5Lh Catgz X-Gm-Gg: AeBDiesLo8Y5ZF0mtbVsWUgu+YhQrGN1Tn//Jw5091pZOOy6sj5M6OmZDuEMx4YCS72 f21aRy7KlksBu/6RE0GQiDvPi3La1lREe4aX6ZFgj5WTr/qFwG649Nl1l1mFhsfbyhhFQHtTSpP 7XqdkakQJXeyohTvM/z/WPCi7fE87NLxC0abMxfTgBbdlMdo2QgBM77BibvwJ0Xt1gp1/dLVCHf wWe4IGfU1vhYMGICxzoTd2k8r/e8q1vK/jqVLT3IfSfiAHDMOM5qSiKGOo9Nwhivar6VZaF/onM cgHsLCHkBtya+VjU+053sivGhzWlm/I8ny0o2ExWPX67V6jpz1ExD1zjuCE6v9KLZc1KTDmJW3m 8nISnrpDD07GGq/3kCKFo/fbRU85TFqTvhoCYQNibuWta7BiQGfENyBoMiQc+SpBuoCJ81yEQvK S4hqsQs4h4iuPutb5caUIqz6yr+Zp7xeCcoHr3czMlkwrMeFi4na6yo/iYnmb2DXDqjhHc4fgc2 tOoJK2MvR8pd6Xx4B85Bt58TA+Ldct/dkt38kozxmvNuBm3y0SI X-Received: by 2002:a05:600c:1f92:b0:48a:5821:5ffc with SMTP id 5b1f17b1804b1-48a77ae0329mr49672395e9.2.1777383597497; Tue, 28 Apr 2026 06:39:57 -0700 (PDT) Received: from davidp-pi5.pitowers.org ([2a00:1098:3142:1f:88ea:c658:5b20:5e46]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48a77c24ca0sm51374525e9.14.2026.04.28.06.39.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Apr 2026 06:39:57 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Cc: David Plowman Subject: [PATCH v3 1/3] pipeline: rpi: Simplify delayed controls Date: Tue, 28 Apr 2026 14:26:37 +0100 Message-ID: <20260428133952.6582-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260428133952.6582-1-david.plowman@raspberrypi.com> References: <20260428133952.6582-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" The queueCount_ value is redundant and can be removed. The pipeline handler calls the push and applyControls functions in a an interleaved manner, so queueCount_ was merely tracking writeCount_, and is therefore unnecessary. Signed-off-by: David Plowman --- .../pipeline/rpi/common/delayed_controls.cpp | 62 +++++++++++-------- .../pipeline/rpi/common/delayed_controls.h | 1 - 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/libcamera/pipeline/rpi/common/delayed_controls.cpp b/src/libcamera/pipeline/rpi/common/delayed_controls.cpp index 19c71946..b647174f 100644 --- a/src/libcamera/pipeline/rpi/common/delayed_controls.cpp +++ b/src/libcamera/pipeline/rpi/common/delayed_controls.cpp @@ -32,14 +32,15 @@ namespace RPi { * * Some sensor controls take effect with a delay as the sensor needs time to * adjust, for example exposure and analog gain. This is a helper class to deal - * with such controls and the intended users are pipeline handlers. + * with such controls. * - * The idea is to extend the concept of the buffer depth of a pipeline the - * application needs to maintain to also cover controls. Just as with buffer - * depth if the application keeps the number of requests queued above the - * control depth the controls are guaranteed to take effect for the correct - * request. The control depth is determined by the control with the greatest - * delay. + * The idea is to maintain a queue of controls that have been submitted. + * Whenever a new frame starts, we can "peak back" into this queue to send + * controls at the correct number of frames in advance to account for each + * control's delay. + * + * The overall delay for controls becomes the maximum delay of any of the + * controls. */ /** @@ -119,7 +120,6 @@ DelayedControls::DelayedControls(V4L2Device *device, */ void DelayedControls::reset(unsigned int cookie) { - queueCount_ = 1; writeCount_ = 0; cookies_[0] = cookie; @@ -146,18 +146,25 @@ void DelayedControls::reset(unsigned int cookie) * \brief Push a set of controls on the queue * \param[in] controls List of controls to add to the device queue * - * Push a set of controls to the control queue. This increases the control queue - * depth by one. + * Push a set of controls to the control queue. The next call to applyControls + * will advance the slot in the queue where the next call to push will write + * controls. * * \returns true if \a controls are accepted, or false otherwise */ bool DelayedControls::push(const ControlList &controls, const unsigned int cookie) { /* Copy state from previous frame. */ - for (auto &ctrl : values_) { - Info &info = ctrl.second[queueCount_]; - info = values_[ctrl.first][queueCount_ - 1]; - info.updated = false; + if (writeCount_ > 0) { + for (auto &ctrl : values_) { + Info &info = ctrl.second[writeCount_]; + info = values_[ctrl.first][writeCount_ - 1]; + info.updated = false; + } + } else { + /* Although it works, we don't expect this. */ + LOG(RPiDelayedControls, Warning) + << "push called before applyControls"; } /* Update with new controls. */ @@ -175,18 +182,17 @@ bool DelayedControls::push(const ControlList &controls, const unsigned int cooki if (controlParams_.find(id) == controlParams_.end()) return false; - Info &info = values_[id][queueCount_]; + Info &info = values_[id][writeCount_]; info = Info(control.second); LOG(RPiDelayedControls, Debug) << "Queuing " << id->name() << " to " << info.toString() - << " at index " << queueCount_; + << " at index " << writeCount_; } - cookies_[queueCount_] = cookie; - queueCount_++; + cookies_[writeCount_] = cookie; return true; } @@ -200,9 +206,9 @@ bool DelayedControls::push(const ControlList &controls, const unsigned int cooki * the callers responsibility to not read too old sequence numbers that have been * pushed out of the history. * - * Historic values are evicted by pushing new values onto the queue using - * push(). The max history from the current sequence number that yields valid - * values are thus 16 minus number of controls pushed. + * Historic values are evicted by new frames arriving and applyControls + * advancing the head of the queue in the ring buffer. The valid history in + * the queue consists of 16 entries from this head of the queue. * * \return The controls at \a sequence number */ @@ -234,6 +240,10 @@ std::pair DelayedControls::get(uint32_t sequence) * number. Any user of these helpers is responsible to inform the helper about * the start of any frame. This can be connected with ease to the start of a * exposure (SOE) V4L2 event. + * + * Controls will be sent to the device, each staggered by the appropriate + * number of frames for that control, so that they are applied at the same + * time. */ void DelayedControls::applyControls(uint32_t sequence) { @@ -277,12 +287,12 @@ void DelayedControls::applyControls(uint32_t sequence) } } - writeCount_ = sequence + 1; - - while (writeCount_ > queueCount_) { + while (writeCount_ < sequence + 1) { + writeCount_++; LOG(RPiDelayedControls, Debug) - << "Queue is empty, auto queue no-op."; - push({}, cookies_[queueCount_ - 1]); + << "Pushing noop with for index " << writeCount_ + << " with cookie " << cookies_[writeCount_ - 1]; + push({}, cookies_[writeCount_ - 1]); } device_->setControls(&out); diff --git a/src/libcamera/pipeline/rpi/common/delayed_controls.h b/src/libcamera/pipeline/rpi/common/delayed_controls.h index 487b0057..48ad5a0f 100644 --- a/src/libcamera/pipeline/rpi/common/delayed_controls.h +++ b/src/libcamera/pipeline/rpi/common/delayed_controls.h @@ -76,7 +76,6 @@ private: std::unordered_map controlParams_; unsigned int maxDelay_; - uint32_t queueCount_; uint32_t writeCount_; std::unordered_map> values_; RingBuffer cookies_;