From patchwork Fri Sep 26 11:07:07 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 24470 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 61502C328C for ; Fri, 26 Sep 2025 11:07:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 48A5F6B5F8; Fri, 26 Sep 2025 13:07:17 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Nof9Ai2I"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 45B7B6B5AA for ; Fri, 26 Sep 2025 13:07:13 +0200 (CEST) Received: from pb-laptop.local (185.221.140.70.nat.pool.zt.hu [185.221.140.70]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C5E8E1807; Fri, 26 Sep 2025 13:05:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1758884747; bh=BRgpObAqrkaLmk3MaO4r97Uxh/5ymfSWs1KPGwiWopg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Nof9Ai2I1rP3y/1RbIUopx/SHLLtNWnlXfTxXwZZSXAsCyzBKh/a22BpXOibXH9ZK kRrVa10+gVAcNZDPR68qmxqXp+fw0ekHeA9TLWhyrUyFPEYGn+VGDHe2wSps5INjfd D4Ht727n37aoitKzk3zNrDuyGW04K/Z4lp/vRgwU= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Cc: Laurent Pinchart Subject: [PATCH v3 1/2] libcamera: base: thread: Make `removeMessages()` public Date: Fri, 26 Sep 2025 13:07:07 +0200 Message-ID: <20250926110708.94112-2-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20250926110708.94112-1-barnabas.pocze@ideasonboard.com> References: <20250926110708.94112-1-barnabas.pocze@ideasonboard.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" Sometimes there is a need to remove pending messages of an object. For example, when the main purpose of a thread is to carry out work asynchronously using invoke messages, then there might be a need to stop processing because some kind of state has changed. This can be done in two main ways: flushing messages or removing them. This changes enables the second option, which is useful if the effects of the pending messages are no longer desired. Signed-off-by: Barnabás Pőcze Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- include/libcamera/base/thread.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libcamera/base/thread.h b/include/libcamera/base/thread.h index b9284c2c0..eb1a52ab1 100644 --- a/include/libcamera/base/thread.h +++ b/include/libcamera/base/thread.h @@ -50,6 +50,7 @@ public: void dispatchMessages(Message::Type type = Message::Type::None, Object *receiver = nullptr); + void removeMessages(Object *receiver); protected: int exec(); @@ -64,7 +65,6 @@ private: void setThreadAffinityInternal(); void postMessage(std::unique_ptr msg, Object *receiver); - void removeMessages(Object *receiver); friend class Object; friend class ThreadData; From patchwork Fri Sep 26 11:07:08 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 24471 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 E1D29C328C for ; Fri, 26 Sep 2025 11:07:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 311F46B5F9; Fri, 26 Sep 2025 13:07:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="VtJcv2Rx"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9282D6B5C2 for ; Fri, 26 Sep 2025 13:07:13 +0200 (CEST) Received: from pb-laptop.local (185.221.140.70.nat.pool.zt.hu [185.221.140.70]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1E6B41E5D; Fri, 26 Sep 2025 13:05:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1758884748; bh=Y9lCZ+P5KnXYeK5LtyttfTfvkbfBHGWMK0emYPBFSME=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VtJcv2Rx4SBVlKvxMMFi/lY6NfcrPSZiF2HFyapbfOeEsdIwoDIQHAj8X4JVjsS62 mVjnWWQRDKFafGGukHfD+IMP5wT0a+wMzX5QqPdiYy+uXpQE/XzbGVwVI62D7booY8 txCkin7c2tBbYKWNhC/ICDNE376dGKOar1HBO7Wg= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Cc: Laurent Pinchart Subject: [PATCH v3 2/2] libcamera: pipeline: virtual: Move image generation to separate thread Date: Fri, 26 Sep 2025 13:07:08 +0200 Message-ID: <20250926110708.94112-3-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20250926110708.94112-1-barnabas.pocze@ideasonboard.com> References: <20250926110708.94112-1-barnabas.pocze@ideasonboard.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" Currently the virtual pipeline generates the images synchronously. This is not ideal because it blocks the camera manager's internal thread, and because its behaviour is different from other existing pipeline handlers, all of which complete requests asynchronously. So move the image generation to a separate thread by deriving `VirtualCameraData` from `Thread`, as well as `Object` and using the existing asynchronous signal and method call mechanism. Signed-off-by: Barnabás Pőcze Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- src/libcamera/pipeline/virtual/virtual.cpp | 91 +++++++++++++++------- src/libcamera/pipeline/virtual/virtual.h | 11 ++- 2 files changed, 71 insertions(+), 31 deletions(-) diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index f9538129c..23eae852f 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -106,6 +106,7 @@ private: } bool initFrameGenerator(Camera *camera); + void bufferCompleted(FrameBuffer *buffer); DmaBufAllocator dmaBufAllocator_; @@ -129,6 +130,39 @@ VirtualCameraData::VirtualCameraData(PipelineHandler *pipe, /* \todo Support multiple streams and pass multi_stream_test */ streamConfigs_.resize(kMaxStream); + + moveToThread(this); +} + +void VirtualCameraData::processRequest(Request *request) +{ + for (auto const &[stream, buffer] : request->buffers()) { + bool found = false; + /* map buffer and fill test patterns */ + for (auto &streamConfig : streamConfigs_) { + if (stream == &streamConfig.stream) { + FrameMetadata &fmd = buffer->_d()->metadata(); + + fmd.status = FrameMetadata::Status::FrameSuccess; + fmd.sequence = streamConfig.seq++; + fmd.timestamp = currentTimestamp(); + + Span planes = buffer->planes(); + for (const auto [i, p] : utils::enumerate(planes)) + fmd.planes()[i].bytesused = p.length; + + found = true; + + if (streamConfig.frameGenerator->generateFrame( + stream->configuration().size, buffer)) + fmd.status = FrameMetadata::Status::FrameError; + + bufferCompleted.emit(buffer); + break; + } + } + ASSERT(found); + } } VirtualCameraConfiguration::VirtualCameraConfiguration(VirtualCameraData *data) @@ -291,11 +325,27 @@ int PipelineHandlerVirtual::start([[maybe_unused]] Camera *camera, for (auto &s : data->streamConfigs_) s.seq = 0; + data->bufferCompleted.connect(this, &PipelineHandlerVirtual::bufferCompleted); + data->start(); + return 0; } -void PipelineHandlerVirtual::stopDevice([[maybe_unused]] Camera *camera) +void PipelineHandlerVirtual::stopDevice(Camera *camera) { + VirtualCameraData *data = cameraData(camera); + + /* Cancel pending work. */ + data->exit(); + data->wait(); + data->removeMessages(data); + + /* Process pending `bufferCompleted` signals. */ + thread()->dispatchMessages(Message::Type::InvokeMessage, this); + data->bufferCompleted.disconnect(this); + + while (!data->queuedRequests_.empty()) + cancelRequest(data->queuedRequests_.front()); } int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera, @@ -304,36 +354,9 @@ int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera, VirtualCameraData *data = cameraData(camera); const auto timestamp = currentTimestamp(); - for (auto const &[stream, buffer] : request->buffers()) { - bool found = false; - /* map buffer and fill test patterns */ - for (auto &streamConfig : data->streamConfigs_) { - if (stream == &streamConfig.stream) { - FrameMetadata &fmd = buffer->_d()->metadata(); - - fmd.status = FrameMetadata::Status::FrameSuccess; - fmd.sequence = streamConfig.seq++; - fmd.timestamp = timestamp; - - Span planes = buffer->planes(); - for (const auto [i, p] : utils::enumerate(planes)) - fmd.planes()[i].bytesused = p.length; - - found = true; - - if (streamConfig.frameGenerator->generateFrame( - stream->configuration().size, buffer)) - fmd.status = FrameMetadata::Status::FrameError; - - completeBuffer(request, buffer); - break; - } - } - ASSERT(found); - } - request->metadata().set(controls::SensorTimestamp, timestamp); - completeRequest(request); + data->invokeMethod(&VirtualCameraData::processRequest, + ConnectionTypeQueued, request); return 0; } @@ -415,6 +438,14 @@ bool PipelineHandlerVirtual::initFrameGenerator(Camera *camera) return true; } +void PipelineHandlerVirtual::bufferCompleted(FrameBuffer *buffer) +{ + Request *request = buffer->request(); + + if (completeBuffer(request, buffer)) + completeRequest(request); +} + REGISTER_PIPELINE_HANDLER(PipelineHandlerVirtual, "virtual") } /* namespace libcamera */ diff --git a/src/libcamera/pipeline/virtual/virtual.h b/src/libcamera/pipeline/virtual/virtual.h index 683cb82b4..215e56fa3 100644 --- a/src/libcamera/pipeline/virtual/virtual.h +++ b/src/libcamera/pipeline/virtual/virtual.h @@ -11,6 +11,10 @@ #include #include +#include +#include +#include + #include #include @@ -25,7 +29,9 @@ namespace libcamera { using VirtualFrame = std::variant; -class VirtualCameraData : public Camera::Private +class VirtualCameraData : public Camera::Private, + public Thread, + public Object { public: const static unsigned int kMaxStream = 3; @@ -54,9 +60,12 @@ public: ~VirtualCameraData() = default; + void processRequest(Request *request); + Configuration config_; std::vector streamConfigs_; + Signal bufferCompleted; }; } /* namespace libcamera */