From patchwork Wed Aug 13 10:25:01 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: 24098 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 BBED3C3295 for ; Wed, 13 Aug 2025 10:25:10 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5DE1369253; Wed, 13 Aug 2025 12:25:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Q1b5glAw"; 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 3831D6923C for ; Wed, 13 Aug 2025 12:25:05 +0200 (CEST) Received: from pb-laptop.local (185.221.141.188.nat.pool.zt.hu [185.221.141.188]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BDCC5351 for ; Wed, 13 Aug 2025 12:24:11 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1755080651; bh=J4gwGlh2r6YAzia66QDh6qgPCGZW/+A8kiIyyEu3Zo0=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Q1b5glAwQwzp1IMdniD+QsMkvOJr804EzPvGQf4noeG/sHzjr7uJJ9FvSFFXtFafH b/hGpoBRwncSqHQr6U5v8mhCR0L56tiCu1cbmURKNU48C/+yQVrFCjwq2GckUDIe1K x+FKzO9DDw3wGycnVd1Ve/AVia5emjc9x/eIjFHs= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v2 2/2] libcamera: pipeline: virtual: Move image generation to separate thread Date: Wed, 13 Aug 2025 12:25:01 +0200 Message-ID: <20250813102501.1645940-3-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250813102501.1645940-1-barnabas.pocze@ideasonboard.com> References: <20250813102501.1645940-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 --- src/libcamera/pipeline/virtual/virtual.cpp | 87 ++++++++++++++-------- src/libcamera/pipeline/virtual/virtual.h | 9 ++- 2 files changed, 66 insertions(+), 30 deletions(-) diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index 049ebcba5..48857491d 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -107,6 +107,14 @@ private: bool initFrameGenerator(Camera *camera); + void onBufferCompleted(FrameBuffer *buffer) + { + Request *request = buffer->request(); + + if (completeBuffer(request, buffer)) + completeRequest(request); + } + DmaBufAllocator dmaBufAllocator_; bool resetCreated_ = false; @@ -129,6 +137,38 @@ VirtualCameraData::VirtualCameraData(PipelineHandler *pipe, /* \todo Support multiple streams and pass multi_stream_test */ streamConfigs_.resize(kMaxStream); + + moveToThread(this); +} + +void VirtualCameraData::queueRequest(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(); + + for (const auto [i, p] : utils::enumerate(buffer->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 +331,27 @@ int PipelineHandlerVirtual::start([[maybe_unused]] Camera *camera, for (auto &s : data->streamConfigs_) s.seq = 0; + data->bufferCompleted.connect(this, &PipelineHandlerVirtual::onBufferCompleted); + 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,35 +360,8 @@ 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; - - for (const auto [i, p] : utils::enumerate(buffer->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::queueRequest, ConnectionTypeQueued, request); return 0; } diff --git a/src/libcamera/pipeline/virtual/virtual.h b/src/libcamera/pipeline/virtual/virtual.h index 683cb82b4..2d83dfe54 100644 --- a/src/libcamera/pipeline/virtual/virtual.h +++ b/src/libcamera/pipeline/virtual/virtual.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include @@ -25,7 +27,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 +58,12 @@ public: ~VirtualCameraData() = default; + void queueRequest(Request *request); + Configuration config_; std::vector streamConfigs_; + Signal bufferCompleted; }; } /* namespace libcamera */