From patchwork Thu Jun 4 09:50:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26838 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 6331BC328C for ; Thu, 4 Jun 2026 09:52:02 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1CA606374B; Thu, 4 Jun 2026 11:52:02 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="Tauiyv0/"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C75D76374A for ; Thu, 4 Jun 2026 11:51:59 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780566718; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=e2ZtmP9i4oRflFR1R1thyUdJEmfiEJhXTTAsM32ddmw=; b=Tauiyv0//k4iHiiWNJK7hWtV5EknRC35s/SaKXXhNHtmmgu3FXkyvKeJlAqxsIl5dZdwQ2 GFFNGUiZphF7/cJiqjspunZ8vNzL6dnvySbmqqjD0cXLrzeN7hDFzM4ljnnVFv9i23PeSS nSwjZlzh8Hyg92IgBB1Yvai8S/28Y58= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-631-gsJ-CtwtOwimgVwDCVAAEQ-1; Thu, 04 Jun 2026 05:51:55 -0400 X-MC-Unique: gsJ-CtwtOwimgVwDCVAAEQ-1 X-Mimecast-MFC-AGG-ID: gsJ-CtwtOwimgVwDCVAAEQ_1780566714 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1DBF418004A9; Thu, 4 Jun 2026 09:51:54 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.156]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6E57A30001A1; Thu, 4 Jun 2026 09:51:52 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , =?utf-8?b?QmFybmFiw6FzIFDFkWN6?= =?utf-8?q?e?= , johannes.goede@oss.qualcomm.com Subject: [RFC PATCH v3 13/17] libcamera: software_isp: Introduce arguments for statistics buffers Date: Thu, 4 Jun 2026 11:50:59 +0200 Message-ID: <20260604095105.68798-18-mzamazal@redhat.com> In-Reply-To: <20260604095105.68798-1-mzamazal@redhat.com> References: <20260604095105.68798-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: -h4M4nOAYnJPFaVsLdfUrv6cWMU8mlNd5csFdXv_1Ls_1780566714 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true 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" Statistics in software ISP is held in two fixed buffers, copying the statistics from the working one to the shared one in SwIspStats::finishFrame. This patch introduces support for a ring of statistics buffers that are never copied, similarly to parameters buffers and to how hardware pipelines operate. This is to address software ISP TODO #2. The patch adds a new argument statsBufferId for the future statistics buffer ids passed to the calls. The buffer ids must be passed to the following groups of methods: - Debayer::process, to signal what statistics buffer is started and stopped being used to gather the statistics. - SoftwareIsp::statsProcessed, a new signal to indicate that the given statistics buffer contents is no longer needed. The type of the buffer id statistics is set to uint32_t because: - It can be used in mojom. - The same type is used for parameters buffers. - It is consistent with the similar types in the hardware pipelines. - It covers file descriptor number range, which will be used as buffer ids. Already provided statistics buffer arguments are renamed from bufferId to statsBufferId for consistency and to avoid confusion between statistics and parameters buffer id arguments. This patch doesn't do more than adding the arguments, to keep the patch simple. The buffer handling will be implemented in the followup patches. Statistics and parameters buffers use is coupled to much extent. It can be tempting to perhaps use the same buffer ids and perhaps some common signals. But it's better to keep them separate to avoid contingent future refactoring in case this coupling becomes less tight in future. Signed-off-by: Milan Zamazal --- .../internal/software_isp/software_isp.h | 3 ++- .../internal/software_isp/swstats_cpu.h | 4 ++-- include/libcamera/ipa/soft.mojom | 3 ++- src/ipa/simple/soft_simple.cpp | 3 ++- src/libcamera/pipeline/simple/simple.cpp | 7 ++++--- src/libcamera/software_isp/debayer.cpp | 3 ++- src/libcamera/software_isp/debayer.h | 1 + src/libcamera/software_isp/debayer_cpu.cpp | 14 ++++++-------- src/libcamera/software_isp/debayer_cpu.h | 7 +++++-- src/libcamera/software_isp/debayer_egl.cpp | 7 +++++-- src/libcamera/software_isp/debayer_egl.h | 7 +++++-- src/libcamera/software_isp/software_isp.cpp | 14 ++++++++++---- src/libcamera/software_isp/swstats_cpu.cpp | 17 +++++++++-------- 13 files changed, 55 insertions(+), 35 deletions(-) diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h index 9ea0ed4db..3e879c630 100644 --- a/include/libcamera/internal/software_isp/software_isp.h +++ b/include/libcamera/internal/software_isp/software_isp.h @@ -92,7 +92,8 @@ private: void paramsBufferReady(const uint32_t paramsBufferId); bool allocateParamsBuffers(const unsigned int bufferCount); void setSensorCtrls(const ControlList &sensorControls); - void statsReady(uint32_t frame, uint32_t bufferId); + void statsReady(uint32_t frame, const uint32_t statsBufferId); + void statsProcessed(const uint32_t statsBufferId); void inputReady(FrameBuffer *input); void outputReady(FrameBuffer *output); std::unique_ptr debayer_; diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h index 686e3d981..fd3f97cbb 100644 --- a/include/libcamera/internal/software_isp/swstats_cpu.h +++ b/include/libcamera/internal/software_isp/swstats_cpu.h @@ -55,8 +55,8 @@ public: int configure(const StreamConfiguration &inputCfg, unsigned int statsBufferCount = 1); void setWindow(const Rectangle &window); void startFrame(uint32_t frame); - void finishFrame(uint32_t frame, uint32_t bufferId); - void processFrame(uint32_t frame, uint32_t bufferId, MappedFrameBuffer &input); + void finishFrame(uint32_t frame, uint32_t statsBufferId); + void processFrame(uint32_t frame, uint32_t statsBufferId, MappedFrameBuffer &input); void processLine0(uint32_t frame, unsigned int y, const uint8_t *src[], unsigned int statsBufferIndex = 0) { diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom index f348f582d..c2c5fe382 100644 --- a/include/libcamera/ipa/soft.mojom +++ b/include/libcamera/ipa/soft.mojom @@ -27,12 +27,13 @@ interface IPASoftInterface { [async] queueRequest(uint32 frame, libcamera.ControlList sensorControls); [async] computeParams(uint32 frame, uint32 paramsBufferId); [async] processStats(uint32 frame, - uint32 bufferId, + uint32 statsBufferId, libcamera.ControlList sensorControls); }; interface IPASoftEventInterface { setSensorControls(libcamera.ControlList sensorControls); + statsProcessed(uint32 statsBufferId); paramsComputed(uint32 paramsBufferId); metadataReady(uint32 frame, libcamera.ControlList metadata); }; diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 0212a3b52..ded75a970 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -298,7 +298,7 @@ void IPASoftSimple::computeParams(const uint32_t frame, } void IPASoftSimple::processStats(const uint32_t frame, - [[maybe_unused]] const uint32_t bufferId, + const uint32_t statsBufferId, const ControlList &sensorControls) { IPAFrameContext &frameContext = context_.frameContexts.get(frame); @@ -312,6 +312,7 @@ void IPASoftSimple::processStats(const uint32_t frame, for (const auto &algo : algorithms()) algo->process(context_, frame, frameContext, stats_, metadata); metadataReady.emit(frame, metadata); + statsProcessed.emit(statsBufferId); /* Sanity check */ if (!sensorControls.contains(V4L2_CID_EXPOSURE) || diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index b49f372a6..2e6bff146 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -369,7 +369,7 @@ private: void conversionInputDone(FrameBuffer *buffer); void conversionOutputDone(FrameBuffer *buffer); - void ispStatsReady(uint32_t frame, uint32_t bufferId); + void ispStatsReady(uint32_t frame, const uint32_t statsBufferId); void metadataReady(uint32_t frame, const ControlList &metadata); void setSensorControls(const ControlList &sensorControls); }; @@ -1044,9 +1044,10 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer) tryCompleteRequest(request); } -void SimpleCameraData::ispStatsReady(uint32_t frame, uint32_t bufferId) +void SimpleCameraData::ispStatsReady(uint32_t frame, + const uint32_t statsBufferId) { - swIsp_->processStats(frame, bufferId, + swIsp_->processStats(frame, statsBufferId, delayedCtrls_->get(frame)); } diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp index 28e04d0df..341930e5b 100644 --- a/src/libcamera/software_isp/debayer.cpp +++ b/src/libcamera/software_isp/debayer.cpp @@ -120,9 +120,10 @@ Debayer::~Debayer() */ /** - * \fn void Debayer::process(uint32_t frame, const uint32_t paramsBufferId, FrameBuffer *input, FrameBuffer *output) + * \fn void Debayer::process(uint32_t frame, const uint32_t statsBufferId, const uint32_t paramsBufferId, FrameBuffer *input, FrameBuffer *output) * \brief Process the bayer data into the requested format * \param[in] frame The frame number + * \param[in] statsBufferId The id of the stats buffer to use * \param[in] paramsBufferId The id of the params buffer in use * \param[in] input The input buffer * \param[in] output The output buffer diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h index 6cefa4c01..259261b1a 100644 --- a/src/libcamera/software_isp/debayer.h +++ b/src/libcamera/software_isp/debayer.h @@ -49,6 +49,7 @@ public: strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0; virtual void process(uint32_t frame, + const uint32_t statsBufferId, const uint32_t paramsBufferId, FrameBuffer *input, FrameBuffer *output) = 0; virtual int start() { return 0; } diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp index 7813eb695..d00830960 100644 --- a/src/libcamera/software_isp/debayer_cpu.cpp +++ b/src/libcamera/software_isp/debayer_cpu.cpp @@ -974,8 +974,11 @@ void DebayerCpu::updateLookupTables(const DebayerParams *params) params_ = *params; } -void DebayerCpu::process(uint32_t frame, const uint32_t paramsBufferId, - FrameBuffer *input, FrameBuffer *output) +void DebayerCpu::process(uint32_t frame, + const uint32_t statsBufferId, + const uint32_t paramsBufferId, + FrameBuffer *input, + FrameBuffer *output) { bench_.startFrame(); @@ -1026,12 +1029,7 @@ void DebayerCpu::process(uint32_t frame, const uint32_t paramsBufferId, /* Measure before emitting signals */ bench_.finishFrame(); - /* - * Buffer ids are currently not used, so pass zeros as its parameter. - * - * \todo Pass real bufferId once stats buffer passing is changed. - */ - stats_->finishFrame(frame, 0); + stats_->finishFrame(frame, statsBufferId); outputBufferReady.emit(output); inputBufferReady.emit(input); } diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h index 56d9087da..37e45e9ea 100644 --- a/src/libcamera/software_isp/debayer_cpu.h +++ b/src/libcamera/software_isp/debayer_cpu.h @@ -45,8 +45,11 @@ public: std::vector formats(PixelFormat input) override; std::tuple strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) override; - void process(uint32_t frame, const uint32_t paramsBufferId, - FrameBuffer *input, FrameBuffer *output) override; + void process(uint32_t frame, + const uint32_t statsBufferId, + const uint32_t paramsBufferId, + FrameBuffer *input, + FrameBuffer *output) override; int start() override; void stop() override; SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) override; diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 77696f1e7..09e7833ed 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -549,8 +549,11 @@ int DebayerEGL::debayerGPU(FrameBuffer *input, FrameBuffer *output, const Debaye return 0; } -void DebayerEGL::process(uint32_t frame, const uint32_t paramsBufferId, - FrameBuffer *input, FrameBuffer *output) +void DebayerEGL::process(uint32_t frame, + [[maybe_unused]] const uint32_t statsBufferId, + const uint32_t paramsBufferId, + FrameBuffer *input, + FrameBuffer *output) { bench_.startFrame(); diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h index 43e3dcb15..36c856c10 100644 --- a/src/libcamera/software_isp/debayer_egl.h +++ b/src/libcamera/software_isp/debayer_egl.h @@ -53,8 +53,11 @@ public: std::vector formats(PixelFormat input) override; std::tuple strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) override; - void process(uint32_t frame, const uint32_t paramsBufferId, - FrameBuffer *input, FrameBuffer *output) override; + void process(uint32_t frame, + const uint32_t statsBufferId, + const uint32_t paramsBufferId, + FrameBuffer *input, + FrameBuffer *output) override; int start() override; void stop() override; diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index dbe4cc270..c44e035b8 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -171,6 +171,7 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, return; } + ipa_->statsProcessed.connect(this, &SoftwareIsp::statsProcessed); ipa_->metadataReady.connect(this, [this](uint32_t frame, const ControlList &metadata) { metadataReady.emit(frame, metadata); @@ -444,9 +445,10 @@ int SoftwareIsp::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output const uint32_t paramsBufferId = availableParams_.back(); availableParams_.pop_back(); ipa_->computeParams(frame, paramsBufferId); + const uint32_t statsBufferId = 0; debayer_->invokeMethod(&Debayer::process, - ConnectionTypeQueued, frame, paramsBufferId, - input, output); + ConnectionTypeQueued, frame, + statsBufferId, paramsBufferId, input, output); return 0; } @@ -461,9 +463,13 @@ void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls) setSensorControls.emit(sensorControls); } -void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId) +void SoftwareIsp::statsReady(uint32_t frame, const uint32_t statsBufferId) +{ + ispStatsReady.emit(frame, statsBufferId); +} + +void SoftwareIsp::statsProcessed([[maybe_unused]] const uint32_t statsBufferId) { - ispStatsReady.emit(frame, bufferId); } void SoftwareIsp::inputReady(FrameBuffer *input) diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index fb7a5301d..0e645f7e1 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2023, Linaro Ltd - * Copyright (C) 2023, Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -346,11 +346,12 @@ void SwStatsCpu::startFrame(uint32_t frame) /** * \brief Finish statistics calculation for the current frame * \param[in] frame The frame number - * \param[in] bufferId ID of the statistics buffer + * \param[in] statsBufferId ID of the statistics buffer * * This may only be called after a successful setWindow() call. */ -void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId) +void SwStatsCpu::finishFrame(uint32_t frame, + const uint32_t statsBufferId) { bool valid = frame % kStatPerNumFrames == 0; @@ -367,7 +368,7 @@ void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId) } sharedStats_->valid = valid; - statsReady.emit(frame, bufferId); + statsReady.emit(frame, statsBufferId); } /** @@ -538,22 +539,22 @@ void SwStatsCpu::processBayerFrame2(MappedFrameBuffer &in) /** * \brief Calculate statistics for a frame in one go * \param[in] frame The frame number - * \param[in] bufferId ID of the statistics buffer + * \param[in] statsBufferId ID of the statistics buffer * \param[in] input The frame to process * * This may only be called after a successful setWindow() call. */ -void SwStatsCpu::processFrame(uint32_t frame, uint32_t bufferId, MappedFrameBuffer &input) +void SwStatsCpu::processFrame(uint32_t frame, uint32_t statsBufferId, MappedFrameBuffer &input) { if (frame % kStatPerNumFrames) { - finishFrame(frame, bufferId); + finishFrame(frame, statsBufferId); return; } bench_.startFrame(); startFrame(frame); (this->*processFrame_)(input); - finishFrame(frame, bufferId); + finishFrame(frame, statsBufferId); bench_.finishFrame(); }