Patch Detail
Show a patch.
GET /api/patches/24090/?format=api
{ "id": 24090, "url": "https://patchwork.libcamera.org/api/patches/24090/?format=api", "web_url": "https://patchwork.libcamera.org/patch/24090/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/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": "<20250811094926.1308259-3-barnabas.pocze@ideasonboard.com>", "date": "2025-08-11T09:49:26", "name": "[RFC,v1,2/2] libcamera: pipeline: virtual: Move image generation to separate thread", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "1e557ed2bbf768a8bb787343e9809b21200ef1c2", "submitter": { "id": 216, "url": "https://patchwork.libcamera.org/api/people/216/?format=api", "name": "Barnabás Pőcze", "email": "barnabas.pocze@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/24090/mbox/", "series": [ { "id": 5365, "url": "https://patchwork.libcamera.org/api/series/5365/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5365", "date": "2025-08-11T09:49:24", "name": "libcamera: pipeline: virtual: Move image generation to separate thread", "version": 1, "mbox": "https://patchwork.libcamera.org/series/5365/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/24090/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/24090/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 479E5C3295\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 11 Aug 2025 09:49:35 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9E8146145E;\n\tMon, 11 Aug 2025 11:49:32 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0ADFC6922A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 11 Aug 2025 11:49:30 +0200 (CEST)", "from pb-laptop.local (185.221.140.182.nat.pool.zt.hu\n\t[185.221.140.182])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 162854A4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 11 Aug 2025 11:48:38 +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=\"jqPWbOkz\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1754905718;\n\tbh=wFL6fTxVFkofj93mVSP5Hr6wNTuL7h4DvXhDxNkhHqA=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=jqPWbOkz/BO9gMNR/s4PJdlmaD4qzqTgTxccAzUeN3saeniYY0ag7vj7ecMfkUtjC\n\tvg1I7R0mvsWphE8/jhOo9N9Vn7bYecv0zGRPM+PttGlC/7Fs+5qo1xV0vl2AVLi9zq\n\tAri3Yc3KRUph0/ryK6py9PKGh0AxVGvyaPRC5D7s=", "From": "=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Subject": "[RFC PATCH v1 2/2] libcamera: pipeline: virtual: Move image\n\tgeneration to separate thread", "Date": "Mon, 11 Aug 2025 11:49:26 +0200", "Message-ID": "<20250811094926.1308259-3-barnabas.pocze@ideasonboard.com>", "X-Mailer": "git-send-email 2.50.1", "In-Reply-To": "<20250811094926.1308259-1-barnabas.pocze@ideasonboard.com>", "References": "<20250811094926.1308259-1-barnabas.pocze@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": "Currently the virtual pipeline generates the images synchronously. This is not\nideal because it blocks the camera manager's internal thread, and because its\nbehaviour is different from other existing pipeline handlers, all of which\ncomplete requests asynchronously.\n\nSo move the image generation to a separate thread by deriving `VirtualCameraData`\nfrom `Thread`, as well as `Object` and using the existing asynchronous signal\nand method call mechanism.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/libcamera/pipeline/virtual/virtual.cpp | 80 ++++++++++++++--------\n src/libcamera/pipeline/virtual/virtual.h | 8 ++-\n 2 files changed, 58 insertions(+), 30 deletions(-)", "diff": "diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp\nindex 049ebcba5..2ad32ec0a 100644\n--- a/src/libcamera/pipeline/virtual/virtual.cpp\n+++ b/src/libcamera/pipeline/virtual/virtual.cpp\n@@ -129,6 +129,38 @@ VirtualCameraData::VirtualCameraData(PipelineHandler *pipe,\n \n \t/* \\todo Support multiple streams and pass multi_stream_test */\n \tstreamConfigs_.resize(kMaxStream);\n+\n+\tmoveToThread(this);\n+}\n+\n+void VirtualCameraData::queueRequest(Request *request)\n+{\n+\tfor (auto const &[stream, buffer] : request->buffers()) {\n+\t\tbool found = false;\n+\t\t/* map buffer and fill test patterns */\n+\t\tfor (auto &streamConfig : streamConfigs_) {\n+\t\t\tif (stream == &streamConfig.stream) {\n+\t\t\t\tFrameMetadata &fmd = buffer->_d()->metadata();\n+\n+\t\t\t\tfmd.status = FrameMetadata::Status::FrameSuccess;\n+\t\t\t\tfmd.sequence = streamConfig.seq++;\n+\t\t\t\tfmd.timestamp = currentTimestamp();\n+\n+\t\t\t\tfor (const auto [i, p] : utils::enumerate(buffer->planes()))\n+\t\t\t\t\tfmd.planes()[i].bytesused = p.length;\n+\n+\t\t\t\tfound = true;\n+\n+\t\t\t\tif (streamConfig.frameGenerator->generateFrame(\n+\t\t\t\t\t stream->configuration().size, buffer))\n+\t\t\t\t\tfmd.status = FrameMetadata::Status::FrameError;\n+\n+\t\t\t\tbufferCompleted.emit(request, buffer);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tASSERT(found);\n+\t}\n }\n \n VirtualCameraConfiguration::VirtualCameraConfiguration(VirtualCameraData *data)\n@@ -291,11 +323,28 @@ int PipelineHandlerVirtual::start([[maybe_unused]] Camera *camera,\n \tfor (auto &s : data->streamConfigs_)\n \t\ts.seq = 0;\n \n+\tdata->bufferCompleted.connect(this, [&](Request *request, FrameBuffer *buffer) {\n+\t\tif (completeBuffer(request, buffer))\n+\t\t\tcompleteRequest(request);\n+\t});\n+\n+\tdata->start();\n+\n \treturn 0;\n }\n \n-void PipelineHandlerVirtual::stopDevice([[maybe_unused]] Camera *camera)\n+void PipelineHandlerVirtual::stopDevice(Camera *camera)\n {\n+\tVirtualCameraData *data = cameraData(camera);\n+\n+\t/* Flush all work. */\n+\tdata->invokeMethod([] { }, ConnectionTypeBlocking);\n+\tdata->exit();\n+\tdata->wait();\n+\n+\t/* Process queued `bufferCompleted` signal emissions. */\n+\tThread::current()->dispatchMessages(Message::Type::InvokeMessage, this);\n+\tdata->bufferCompleted.disconnect(this);\n }\n \n int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera,\n@@ -304,35 +353,8 @@ int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera,\n \tVirtualCameraData *data = cameraData(camera);\n \tconst auto timestamp = currentTimestamp();\n \n-\tfor (auto const &[stream, buffer] : request->buffers()) {\n-\t\tbool found = false;\n-\t\t/* map buffer and fill test patterns */\n-\t\tfor (auto &streamConfig : data->streamConfigs_) {\n-\t\t\tif (stream == &streamConfig.stream) {\n-\t\t\t\tFrameMetadata &fmd = buffer->_d()->metadata();\n-\n-\t\t\t\tfmd.status = FrameMetadata::Status::FrameSuccess;\n-\t\t\t\tfmd.sequence = streamConfig.seq++;\n-\t\t\t\tfmd.timestamp = timestamp;\n-\n-\t\t\t\tfor (const auto [i, p] : utils::enumerate(buffer->planes()))\n-\t\t\t\t\tfmd.planes()[i].bytesused = p.length;\n-\n-\t\t\t\tfound = true;\n-\n-\t\t\t\tif (streamConfig.frameGenerator->generateFrame(\n-\t\t\t\t\t stream->configuration().size, buffer))\n-\t\t\t\t\tfmd.status = FrameMetadata::Status::FrameError;\n-\n-\t\t\t\tcompleteBuffer(request, buffer);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t\tASSERT(found);\n-\t}\n-\n \trequest->metadata().set(controls::SensorTimestamp, timestamp);\n-\tcompleteRequest(request);\n+\tdata->invokeMethod(&VirtualCameraData::queueRequest, ConnectionTypeQueued, request);\n \n \treturn 0;\n }\ndiff --git a/src/libcamera/pipeline/virtual/virtual.h b/src/libcamera/pipeline/virtual/virtual.h\nindex 683cb82b4..0832fd80c 100644\n--- a/src/libcamera/pipeline/virtual/virtual.h\n+++ b/src/libcamera/pipeline/virtual/virtual.h\n@@ -11,6 +11,7 @@\n #include <variant>\n #include <vector>\n \n+#include <libcamera/base/thread.h>\n #include <libcamera/geometry.h>\n #include <libcamera/stream.h>\n \n@@ -25,7 +26,9 @@ namespace libcamera {\n \n using VirtualFrame = std::variant<TestPattern, ImageFrames>;\n \n-class VirtualCameraData : public Camera::Private\n+class VirtualCameraData : public Camera::Private,\n+\t\t\t public Thread,\n+\t\t\t public Object\n {\n public:\n \tconst static unsigned int kMaxStream = 3;\n@@ -54,9 +57,12 @@ public:\n \n \t~VirtualCameraData() = default;\n \n+\tvoid queueRequest(Request *request);\n+\n \tConfiguration config_;\n \n \tstd::vector<StreamConfig> streamConfigs_;\n+\tSignal<Request *, FrameBuffer *> bufferCompleted;\n };\n \n } /* namespace libcamera */\n", "prefixes": [ "RFC", "v1", "2/2" ] }