From patchwork Thu May 14 15:00:46 2026 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: 26758 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 72843BDCBC for ; Thu, 14 May 2026 15:00:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7E4F162FE1; Thu, 14 May 2026 17:00:52 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Cme7H6gA"; 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 4CE9962FE1 for ; Thu, 14 May 2026 17:00:50 +0200 (CEST) Received: from pb-laptop.local (185.182.215.166.nat.pool.zt.hu [185.182.215.166]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EEBDAD7E for ; Thu, 14 May 2026 17:00:40 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1778770841; bh=0jeO9HTWQOs7fa80SwCWmYA5AroSHS0PNLBrgnjUFBg=; h=From:To:Subject:Date:From; b=Cme7H6gAKDKwBrb4yWpl+sZDr68wZY5vhmysYuk3R7+hIprC3qji8jm6amAnx5wqg OBq0sNZHuZXUZk4lB989b+WejPa8hVdhrYvjdf27Owarl0JMQSAF1XYNPOJUPsZm1F popYl/Uy+lBYdKowh3Yv2huGu1zuqHd7cZF0BZ5A= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [PATCH v1] libcamera: pipeline: virtual: Make copy of request's buffer map Date: Thu, 14 May 2026 17:00:46 +0200 Message-ID: <20260514150046.503654-1-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.54.0 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" A `Request` must not be accessed by a pipeline handler after it has called `completeRequest()` as that call essentially transfers the request back to the application. The change that moved frame generation to a separate thread violated this requirement. After the last buffer of a request is completed, the loop in `VirtualCameraData::processRequest()` will reference the buffer map in the loop condition. This is problematic because concurrently the request will be completed, then reused or destroyed. This was mostly hidden by the fact that most application use `ReuseBuffers`, so the buffer map nodes do not change. Fix that by making a copy of the (stream, buffer) pairs locally. Fixes: 6c251ae3ef0e ("libcamera: pipeline: virtual: Move image generation to separate thread") Signed-off-by: Barnabás Pőcze --- src/libcamera/pipeline/virtual/virtual.cpp | 39 ++++++++++++---------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index 81d2dddab..6e0cb6478 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -130,33 +130,38 @@ VirtualCameraData::VirtualCameraData(PipelineHandler *pipe, void VirtualCameraData::processRequest(Request *request) { + std::array, kMaxStream> buffers; + size_t bufferCount = 0; + for (const auto &[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; - + buffers[bufferCount++] = { &streamConfig, buffer }; found = true; - - if (streamConfig.frameGenerator->generateFrame( - stream->configuration().size, buffer)) - fmd.status = FrameMetadata::Status::FrameError; - - bufferCompleted.emit(buffer); break; } } ASSERT(found); } + + for (size_t i = 0; i < bufferCount; i++) { + const auto &[stream, buffer] = buffers[i]; + FrameMetadata &fmd = buffer->_d()->metadata(); + + fmd.status = FrameMetadata::Status::FrameSuccess; + fmd.sequence = stream->seq++; + fmd.timestamp = currentTimestamp(); + + Span planes = buffer->planes(); + for (const auto [j, p] : utils::enumerate(planes)) + fmd.planes()[j].bytesused = p.length; + + if (stream->frameGenerator->generateFrame(stream->stream.configuration().size, buffer)) + fmd.status = FrameMetadata::Status::FrameError; + + bufferCompleted.emit(buffer); + } } VirtualCameraConfiguration::VirtualCameraConfiguration(VirtualCameraData *data)