From patchwork Sat May 10 14:12:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 23353 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 1DB63C3226 for ; Sat, 10 May 2025 14:12:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AF89268B58; Sat, 10 May 2025 16:12:36 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="dVBRNBAK"; 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 B3E486175C for ; Sat, 10 May 2025 16:12:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1746886354; 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=ThoR+7gOig1UdDsJYs7+uny/8qEpLOOMvaUeMrbthhA=; b=dVBRNBAKIUn+IZkFpUSg9ttDcOi/6h0I5scfItx5wkMIrIhFDUuEeMV4FD/ONtZX04WyXp VYBlHvwEs8a3pw6Qhz7/oSvfDo25peMxSRzqMQn6sp4V20eSvODDiT3pgaZHwiNjAG2Ncw C7ZrkaopPh8+7BKFHOB/8FbsVMGYh+U= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-479-fjnfjBK4MYOo04SRaKwftg-1; Sat, 10 May 2025 10:12:31 -0400 X-MC-Unique: fjnfjBK4MYOo04SRaKwftg-1 X-Mimecast-MFC-AGG-ID: fjnfjBK4MYOo04SRaKwftg_1746886350 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 6899F1956088; Sat, 10 May 2025 14:12:30 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.224.58]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CB44018011FA; Sat, 10 May 2025 14:12:28 +0000 (UTC) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede , Kieran Bingham Subject: [PATCH v2 1/8] libcamera: swstats_cpu: Update statsProcessFn() / processLine0() documentation Date: Sat, 10 May 2025 16:12:13 +0200 Message-ID: <20250510141220.54872-2-hdegoede@redhat.com> In-Reply-To: <20250510141220.54872-1-hdegoede@redhat.com> References: <20250510141220.54872-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: w4equjJcRTsTWg8WLyy2WdLLSx5nE-9tEy8rBAoR-7k_1746886350 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" Update the documentation of the statsProcessFn() / processLine0() src[] pointer argument to take into account that swstats_cpu may also be used with planar input data or with non Bayer single plane input data. The statsProcessFn typedef is private, so no documentation is generated for it. Move the new updated src[] pointer argument documentation to processLine0() so that it gets included in the generated docs. Reviewed-by: Kieran Bingham Reviewed-by: Milan Zamazal Signed-off-by: Hans de Goede --- src/libcamera/software_isp/swstats_cpu.cpp | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index c520c806..a9a3e77a 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -71,6 +71,19 @@ namespace libcamera { * patternSize height == 1. * It'll process line 0 and 1 for input formats with patternSize height >= 2. * This function may only be called after a successful setWindow() call. + * + * This function takes an array of src pointers each pointing to a line in + * the source image. + * + * Bayer input data requires (patternSize_.height + 1) src pointers, with + * the middle element of the array pointing to the actual line being processed. + * Earlier element(s) will point to the previous line(s) and later element(s) + * to the next line(s). See the DebayerCpu::debayerFn documentation for details. + * + * Planar input data requires a src pointer for each plane, with src[0] pointing + * to the line in plane 0, etc. + * + * For non Bayer single plane input data only a single src pointer is required. */ /** @@ -89,20 +102,6 @@ namespace libcamera { * \brief Signals that the statistics are ready */ -/** - * \typedef SwStatsCpu::statsProcessFn - * \brief Called when there is data to get statistics from - * \param[in] src The input data - * - * These functions take an array of (patternSize_.height + 1) src - * pointers each pointing to a line in the source image. The middle - * element of the array will point to the actual line being processed. - * Earlier element(s) will point to the previous line(s) and later - * element(s) to the next line(s). - * - * See the documentation of DebayerCpu::debayerFn for more details. - */ - /** * \var unsigned int SwStatsCpu::ySkipMask_ * \brief Skip lines where this bitmask is set in y From patchwork Sat May 10 14:12:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 23354 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 987BEC3226 for ; Sat, 10 May 2025 14:12:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 57C7168B56; Sat, 10 May 2025 16:12:38 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="Fwl0wOzE"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6332668B53 for ; Sat, 10 May 2025 16:12:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1746886355; 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=E8jMikr/qzegO8Rb3a2wYoN59+BVL7IA8F/k3I1pVyg=; b=Fwl0wOzEW0AtI9aDQam17pp8zBRfmuy3inTz458+yjNFV5wF/ZhQMS94HU86bAy/UpS20l LaudZumnr0OQStKv+Z5U8aCikk3EjxVLfSYXNbMjo2sZSGfAiqJQn7tDccZHPHloeg/p5q Gbunh2uOxdGDvdD2TrCIAvzO1zzkbnc= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-614-q3J11dVWPC2dQBHBk48u9g-1; Sat, 10 May 2025 10:12:33 -0400 X-MC-Unique: q3J11dVWPC2dQBHBk48u9g-1 X-Mimecast-MFC-AGG-ID: q3J11dVWPC2dQBHBk48u9g_1746886352 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9041019560B0; Sat, 10 May 2025 14:12:32 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.224.58]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E7B921800371; Sat, 10 May 2025 14:12:30 +0000 (UTC) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede , Kieran Bingham Subject: [PATCH v2 2/8] libcamera: swstats_cpu: Drop patternSize_ documentation Date: Sat, 10 May 2025 16:12:14 +0200 Message-ID: <20250510141220.54872-3-hdegoede@redhat.com> In-Reply-To: <20250510141220.54872-1-hdegoede@redhat.com> References: <20250510141220.54872-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: E3CIK5_tgMZqm-AFfoi6-IlGmbZgGOJJ1LRJQ0L2mmo_1746886352 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" patternSize_ is a private variable and its meaning is already documented in the patternSize() getter documentation. Move the list of valid sizes to the patternSize() getter documentation and drop the patternSize_ documentation. While at it also add 1x1 as valid size for use with future support of single plane non Bayer input data. Reviewed-by: Kieran Bingham Reviewed-by: Milan Zamazal Signed-off-by: Hans de Goede --- src/libcamera/software_isp/swstats_cpu.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index a9a3e77a..5e4246a9 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -58,6 +58,8 @@ namespace libcamera { * also indicates if processLine2() should be called or not. * This may only be called after a successful configure() call. * + * Valid sizes are: 1x1, 2x2, 4x2 or 4x4. + * * \return The pattern size */ @@ -112,13 +114,6 @@ namespace libcamera { * \brief Statistics window, set by setWindow(), used every line */ -/** - * \var Size SwStatsCpu::patternSize_ - * \brief The size of the bayer pattern - * - * Valid sizes are: 2x2, 4x2 or 4x4. - */ - /** * \var unsigned int SwStatsCpu::xShift_ * \brief The offset of x, applied to window_.x for bayer variants From patchwork Sat May 10 14:12:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 23355 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 1DAB5C3226 for ; Sat, 10 May 2025 14:12:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CD99D68B61; Sat, 10 May 2025 16:12:41 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="h8VQG1XP"; 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 0AB1868B57 for ; Sat, 10 May 2025 16:12:39 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1746886358; 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=AbKkicayOj7sgCdQrbol3r40YsFZSRqcbYhMBoYvJko=; b=h8VQG1XPXiBZ322P4mneBDgyLiB0QZG9FQeh7gUJ3bjNIqSurpvcwcU19sXdpI/kMgDTwT GVMRztWtnR3CefbUxb92f4S4GGwQl8kbo6Hzp5xnOjeEw/bLnwoCav9m7LALdMOw4ANuyx pMZjrzV7DEk9F0+NMpr0Du35msKa50A= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-687-zzoDqCRkMQK6CKGP0QCtfg-1; Sat, 10 May 2025 10:12:35 -0400 X-MC-Unique: zzoDqCRkMQK6CKGP0QCtfg-1 X-Mimecast-MFC-AGG-ID: zzoDqCRkMQK6CKGP0QCtfg_1746886354 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B1C401956089; Sat, 10 May 2025 14:12:34 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.224.58]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1CCE91800371; Sat, 10 May 2025 14:12:32 +0000 (UTC) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede , Kieran Bingham Subject: [PATCH v2 3/8] libcamera: swstats_cpu: Move header to libcamera/internal/software_isp Date: Sat, 10 May 2025 16:12:15 +0200 Message-ID: <20250510141220.54872-4-hdegoede@redhat.com> In-Reply-To: <20250510141220.54872-1-hdegoede@redhat.com> References: <20250510141220.54872-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: HCfEzcB_-Aq3xRawNOqEsI8GY7eJ-vC_5gX22NN7nQY_1746886354 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" Move the swstats_cpu.h file to include/libcamera/internal/software_isp/ so that it can be used outside the src/libcamera/software_isp/ directory. Reviewed-by: Milan Zamazal Reviewed-by: Kieran Bingham Signed-off-by: Hans de Goede --- include/libcamera/internal/software_isp/meson.build | 1 + .../libcamera/internal}/software_isp/swstats_cpu.h | 0 src/libcamera/software_isp/debayer_cpu.h | 2 +- src/libcamera/software_isp/swstats_cpu.cpp | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) rename {src/libcamera => include/libcamera/internal}/software_isp/swstats_cpu.h (100%) diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build index 508ddddc..ea3f3f1c 100644 --- a/include/libcamera/internal/software_isp/meson.build +++ b/include/libcamera/internal/software_isp/meson.build @@ -4,4 +4,5 @@ libcamera_internal_headers += files([ 'debayer_params.h', 'software_isp.h', 'swisp_stats.h', + 'swstats_cpu.h', ]) diff --git a/src/libcamera/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h similarity index 100% rename from src/libcamera/software_isp/swstats_cpu.h rename to include/libcamera/internal/software_isp/swstats_cpu.h diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h index 926195e9..89a89893 100644 --- a/src/libcamera/software_isp/debayer_cpu.h +++ b/src/libcamera/software_isp/debayer_cpu.h @@ -18,9 +18,9 @@ #include #include "libcamera/internal/bayer_format.h" +#include "libcamera/internal/software_isp/swstats_cpu.h" #include "debayer.h" -#include "swstats_cpu.h" namespace libcamera { diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index 5e4246a9..aa5654dc 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -9,7 +9,7 @@ * CPU based software statistics implementation */ -#include "swstats_cpu.h" +#include "libcamera/internal/software_isp/swstats_cpu.h" #include From patchwork Sat May 10 14:12:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 23356 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 EE448C327D for ; Sat, 10 May 2025 14:12:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A85CC68B5F; Sat, 10 May 2025 16:12:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="iLZqGI01"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 60A7E68B5D for ; Sat, 10 May 2025 16:12:40 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1746886359; 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=hUXv5E/HhR7pLcuhlPqos9+R7/djUbRZoax68PXpM3g=; b=iLZqGI01GeB5DCuBSWJYu+shjC0MyVwcLFpOQdqrzM1sSytrAAHr3KDeQ4PEPBvUJ6qPgH kHatoVqN1JH690Qo4+4eft5YwNjHEanstf5+diw91dmQwFN7jWzR7qEoEhA+s0lEV1wGmq lJK5h9EhZ6zzgFFTgLgr3jcI2SIC1XA= Received: from mx-prod-mc-06.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-626-d0RI3OsyPCCopyphfUQOxw-1; Sat, 10 May 2025 10:12:37 -0400 X-MC-Unique: d0RI3OsyPCCopyphfUQOxw-1 X-Mimecast-MFC-AGG-ID: d0RI3OsyPCCopyphfUQOxw_1746886356 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C2C461800258; Sat, 10 May 2025 14:12:36 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.224.58]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 332B71800371; Sat, 10 May 2025 14:12:34 +0000 (UTC) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede , Kieran Bingham Subject: [PATCH v2 4/8] libcamera: software_isp: Move benchmark code to its own class Date: Sat, 10 May 2025 16:12:16 +0200 Message-ID: <20250510141220.54872-5-hdegoede@redhat.com> In-Reply-To: <20250510141220.54872-1-hdegoede@redhat.com> References: <20250510141220.54872-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: y94JgRPZ34tbjg0-e-H-6qZNJU5YQnYOm9zBQj5O8oY_1746886356 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" Move the code for the builtin benchmark to its own small Benchmark class. Reviewed-by: Kieran Bingham Reviewed-by: Milan Zamazal Signed-off-by: Hans de Goede --- Changes since the RFC: - Add doxygen documentation to for all the public methods --- .../internal/software_isp/benchmark.h | 36 +++++++ .../internal/software_isp/meson.build | 1 + src/libcamera/software_isp/benchmark.cpp | 93 +++++++++++++++++++ src/libcamera/software_isp/debayer_cpu.cpp | 36 +------ src/libcamera/software_isp/debayer_cpu.h | 7 +- src/libcamera/software_isp/meson.build | 1 + 6 files changed, 135 insertions(+), 39 deletions(-) create mode 100644 include/libcamera/internal/software_isp/benchmark.h create mode 100644 src/libcamera/software_isp/benchmark.cpp diff --git a/include/libcamera/internal/software_isp/benchmark.h b/include/libcamera/internal/software_isp/benchmark.h new file mode 100644 index 00000000..8af25015 --- /dev/null +++ b/include/libcamera/internal/software_isp/benchmark.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Red Hat Inc. + * + * Authors: + * Hans de Goede + * + * Simple builtin benchmark to measure software ISP processing times + */ + +#pragma once + +#include +#include + +namespace libcamera { + +class Benchmark +{ +public: + Benchmark(); + ~Benchmark(); + + void startFrame(void); + void finishFrame(void); + +private: + unsigned int measuredFrames_; + int64_t frameProcessTime_; + timespec frameStartTime_; + /* Skip 30 frames for things to stabilize then measure 30 frames */ + static constexpr unsigned int kFramesToSkip = 30; + static constexpr unsigned int kLastFrameToMeasure = 60; +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build index ea3f3f1c..df7c3b97 100644 --- a/include/libcamera/internal/software_isp/meson.build +++ b/include/libcamera/internal/software_isp/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 libcamera_internal_headers += files([ + 'benchmark.h', 'debayer_params.h', 'software_isp.h', 'swisp_stats.h', diff --git a/src/libcamera/software_isp/benchmark.cpp b/src/libcamera/software_isp/benchmark.cpp new file mode 100644 index 00000000..b3da3c41 --- /dev/null +++ b/src/libcamera/software_isp/benchmark.cpp @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Red Hat Inc. + * + * Authors: + * Hans de Goede + * + * Simple builtin benchmark to measure software ISP processing times + */ + +#include "libcamera/internal/software_isp/benchmark.h" + +#include + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Benchmark) + +/** + * \class Benchmark + * \brief Simple builtin benchmark + * + * Simple builtin benchmark to measure software ISP processing times. + */ + +/** + * \brief Constructs a Benchmark object + */ +Benchmark::Benchmark() + : measuredFrames_(0), frameProcessTime_(0) +{ +} + +Benchmark::~Benchmark() +{ +} + +static inline int64_t timeDiff(timespec &after, timespec &before) +{ + return (after.tv_sec - before.tv_sec) * 1000000000LL + + (int64_t)after.tv_nsec - (int64_t)before.tv_nsec; +} + +/** + * \brief Start measuring process time for a single frame + * + * Call this function before processing frame data to start measuring + * the process time for a frame. + */ +void Benchmark::startFrame(void) +{ + if (measuredFrames_ >= Benchmark::kLastFrameToMeasure) + return; + + frameStartTime_ = {}; + clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime_); +} + +/** + * \brief Finish measuring process time for a single frame + * + * Call this function after processing frame data to finish measuring + * the process time for a frame. + * + * This function will log frame processing time information after + * Benchmark::kLastFrameToMeasure frames have been processed. + */ +void Benchmark::finishFrame(void) +{ + if (measuredFrames_ >= Benchmark::kLastFrameToMeasure) + return; + + measuredFrames_++; + + if (measuredFrames_ <= Benchmark::kFramesToSkip) + return; + + timespec frameEndTime = {}; + clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime); + frameProcessTime_ += timeDiff(frameEndTime, frameStartTime_); + + if (measuredFrames_ == Benchmark::kLastFrameToMeasure) { + const unsigned int measuredFrames = Benchmark::kLastFrameToMeasure - + Benchmark::kFramesToSkip; + LOG(Benchmark, Info) + << "Processed " << measuredFrames + << " frames in " << frameProcessTime_ / 1000 << "us, " + << frameProcessTime_ / (1000 * measuredFrames) + << " us/frame"; + } +} + +} /* namespace libcamera */ diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp index 66f6038c..8d30bf4a 100644 --- a/src/libcamera/software_isp/debayer_cpu.cpp +++ b/src/libcamera/software_isp/debayer_cpu.cpp @@ -554,9 +554,6 @@ int DebayerCpu::configure(const StreamConfiguration &inputCfg, lineBuffers_[i].resize(lineBufferLength_); } - measuredFrames_ = 0; - frameProcessTime_ = 0; - return 0; } @@ -746,24 +743,9 @@ void DebayerCpu::process4(const uint8_t *src, uint8_t *dst) } } -namespace { - -inline int64_t timeDiff(timespec &after, timespec &before) -{ - return (after.tv_sec - before.tv_sec) * 1000000000LL + - (int64_t)after.tv_nsec - (int64_t)before.tv_nsec; -} - -} /* namespace */ - void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params) { - timespec frameStartTime; - - if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure) { - frameStartTime = {}; - clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime); - } + bench_.startFrame(); std::vector dmaSyncers; for (const FrameBuffer::Plane &plane : input->planes()) @@ -817,21 +799,7 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output dmaSyncers.clear(); /* Measure before emitting signals */ - if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure && - ++measuredFrames_ > DebayerCpu::kFramesToSkip) { - timespec frameEndTime = {}; - clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime); - frameProcessTime_ += timeDiff(frameEndTime, frameStartTime); - if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) { - const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure - - DebayerCpu::kFramesToSkip; - LOG(Debayer, Info) - << "Processed " << measuredFrames - << " frames in " << frameProcessTime_ / 1000 << "us, " - << frameProcessTime_ / (1000 * measuredFrames) - << " us/frame"; - } - } + bench_.finishFrame(); /* * Buffer ids are currently not used, so pass zeros as its parameter. diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h index 89a89893..182607cd 100644 --- a/src/libcamera/software_isp/debayer_cpu.h +++ b/src/libcamera/software_isp/debayer_cpu.h @@ -17,6 +17,7 @@ #include +#include "libcamera/internal/software_isp/benchmark.h" #include "libcamera/internal/bayer_format.h" #include "libcamera/internal/software_isp/swstats_cpu.h" @@ -160,11 +161,7 @@ private: unsigned int xShift_; /* Offset of 0/1 applied to window_.x */ bool enableInputMemcpy_; bool swapRedBlueGains_; - unsigned int measuredFrames_; - int64_t frameProcessTime_; - /* Skip 30 frames for things to stabilize then measure 30 frames */ - static constexpr unsigned int kFramesToSkip = 30; - static constexpr unsigned int kLastFrameToMeasure = 60; + Benchmark bench_; }; } /* namespace libcamera */ diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build index aac7eda7..59fa5f02 100644 --- a/src/libcamera/software_isp/meson.build +++ b/src/libcamera/software_isp/meson.build @@ -8,6 +8,7 @@ if not softisp_enabled endif libcamera_internal_sources += files([ + 'benchmark.cpp', 'debayer.cpp', 'debayer_cpu.cpp', 'software_isp.cpp', From patchwork Sat May 10 14:12:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 23357 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 7FC22C3226 for ; Sat, 10 May 2025 14:12:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3E30468B68; Sat, 10 May 2025 16:12:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="DtadYcBh"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D640B68B62 for ; Sat, 10 May 2025 16:12:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1746886361; 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=oXjQFWyQEO7ckjYtpecCE4rTR5BqusAWdvB1WYfoooc=; b=DtadYcBhLrTbI6Zx6JTER90hEskCHSBZZL6ReRyyw5A/KMFm7t9qFagsCaw1QaM5Ju1Wnw eiSaHfZ1YqAV/lpNIfGbGLnZUY9Lip6To7yoz85y0d07Q0NNsszcsjKK4MzLGQY6gD5oQV GEKMgTWFaD2+TSpf+VSz4Cd2O2QUmYY= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-650-4z98azqlP9K-2uW-6Z3tpQ-1; Sat, 10 May 2025 10:12:39 -0400 X-MC-Unique: 4z98azqlP9K-2uW-6Z3tpQ-1 X-Mimecast-MFC-AGG-ID: 4z98azqlP9K-2uW-6Z3tpQ_1746886358 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 963D41956086 for ; Sat, 10 May 2025 14:12:38 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.224.58]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 472841800359; Sat, 10 May 2025 14:12:37 +0000 (UTC) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede Subject: [PATCH v2 5/8] libcamera: swstats_cpu: Add processFrame() method Date: Sat, 10 May 2025 16:12:17 +0200 Message-ID: <20250510141220.54872-6-hdegoede@redhat.com> In-Reply-To: <20250510141220.54872-1-hdegoede@redhat.com> References: <20250510141220.54872-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: MXL75LiMOfoUXzYu8cqkcVCfurd8LAeC3IilfaTNso4_1746886358 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" Add a method to the SwstatsCpu class to process a whole Framebuffer in one go, rather then line by line. This is useful for gathering stats when debayering is not necessary or is not done on the CPU. Reviewed-by: Milan Zamazal Signed-off-by: Hans de Goede --- Changes since the RFC: - Make processFrame() call startFrame() and finishFrame() rather then making the caller do this - Add doxygen documentation for processFrame() --- .../internal/software_isp/swstats_cpu.h | 12 +++++ src/libcamera/software_isp/swstats_cpu.cpp | 51 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h index 26a2f462..fa47cec9 100644 --- a/include/libcamera/internal/software_isp/swstats_cpu.h +++ b/include/libcamera/internal/software_isp/swstats_cpu.h @@ -18,12 +18,16 @@ #include #include "libcamera/internal/bayer_format.h" +#include "libcamera/internal/framebuffer.h" #include "libcamera/internal/shared_mem_object.h" #include "libcamera/internal/software_isp/swisp_stats.h" +#include "benchmark.h" + namespace libcamera { class PixelFormat; +class MappedFrameBuffer; struct StreamConfiguration; class SwStatsCpu @@ -42,6 +46,7 @@ public: void setWindow(const Rectangle &window); void startFrame(); void finishFrame(uint32_t frame, uint32_t bufferId); + void processFrame(uint32_t frame, uint32_t bufferId, FrameBuffer *input); void processLine0(unsigned int y, const uint8_t *src[]) { @@ -65,6 +70,7 @@ public: private: using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]); + using processFrameFn = void (SwStatsCpu::*)(MappedFrameBuffer &in); int setupStandardBayerOrder(BayerFormat::Order order); /* Bayer 8 bpp unpacked */ @@ -77,6 +83,10 @@ private: void statsBGGR10PLine0(const uint8_t *src[]); void statsGBRG10PLine0(const uint8_t *src[]); + void processBayerFrame2(MappedFrameBuffer &in); + + processFrameFn processFrame_; + /* Variables set by configure(), used every line */ statsProcessFn stats0_; statsProcessFn stats2_; @@ -89,9 +99,11 @@ private: Size patternSize_; unsigned int xShift_; + unsigned int stride_; SharedMemObject sharedStats_; SwIspStats stats_; + Benchmark bench_; }; } /* namespace libcamera */ diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index aa5654dc..1ff15f5b 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -16,6 +16,7 @@ #include #include "libcamera/internal/bayer_format.h" +#include "libcamera/internal/mapped_framebuffer.h" namespace libcamera { @@ -360,11 +361,14 @@ int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order) */ int SwStatsCpu::configure(const StreamConfiguration &inputCfg) { + stride_ = inputCfg.stride; + BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputCfg.pixelFormat); if (bayerFormat.packing == BayerFormat::Packing::None && setupStandardBayerOrder(bayerFormat.order) == 0) { + processFrame_ = &SwStatsCpu::processBayerFrame2; switch (bayerFormat.bitDepth) { case 8: stats0_ = &SwStatsCpu::statsBGGR8Line0; @@ -385,6 +389,7 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg) /* Skip every 3th and 4th line, sample every other 2x2 block */ ySkipMask_ = 0x02; xShift_ = 0; + processFrame_ = &SwStatsCpu::processBayerFrame2; switch (bayerFormat.order) { case BayerFormat::BGGR: @@ -425,4 +430,50 @@ void SwStatsCpu::setWindow(const Rectangle &window) window_.height &= ~(patternSize_.height - 1); } +void SwStatsCpu::processBayerFrame2(MappedFrameBuffer &in) +{ + const uint8_t *src = in.planes()[0].data(); + const uint8_t *linePointers[3]; + + /* Adjust src for starting at window_.y */ + src += window_.y * stride_; + + for (unsigned int y = 0; y < window_.height; y += 2) { + if (y & ySkipMask_) { + src += stride_ * 2; + continue; + } + + /* linePointers[0] is not used by any stats0_ functions */ + linePointers[1] = src; + linePointers[2] = src + stride_; + (this->*stats0_)(linePointers); + src += stride_ * 2; + } +} + +/** + * \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] input The frame to process + * + * This may only be called after a successful setWindow() call. + */ +void SwStatsCpu::processFrame(uint32_t frame, uint32_t bufferId, FrameBuffer *input) +{ + bench_.startFrame(); + startFrame(); + + MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read); + if (!in.isValid()) { + LOG(SwStatsCpu, Error) << "mmap-ing buffer(s) failed"; + return; + } + + (this->*processFrame_)(in); + finishFrame(frame, bufferId); + bench_.finishFrame(); +} + } /* namespace libcamera */ From patchwork Sat May 10 14:12:18 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 23358 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 1F111C3226 for ; Sat, 10 May 2025 14:12:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C6CD768B6C; Sat, 10 May 2025 16:12:45 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ebNpRMkD"; 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 F340868B60 for ; Sat, 10 May 2025 16:12:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1746886362; 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=K/d/dsIeHcidRCjeberYPZnHFioqMOXJOIlc+Q5LkMM=; b=ebNpRMkD0iUALd653Ul71qlDRDbsgbWYX8hQTD2VhRK3bJfNwSIRN3/QOAg+4f1B9OiwjZ ievJMKHO0m7WbDccDWg5VEJd9Vu/xDts4xtCvFTa8XGmMReR7jKUBSX0i6Kj0TWJa9ci1t mTdOGUGozwB4/24PH4aqn9pK76T5I+0= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-341-st8LSZgWMuiiTbgpFvehZA-1; Sat, 10 May 2025 10:12:41 -0400 X-MC-Unique: st8LSZgWMuiiTbgpFvehZA-1 X-Mimecast-MFC-AGG-ID: st8LSZgWMuiiTbgpFvehZA_1746886360 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id BDAD91956062 for ; Sat, 10 May 2025 14:12:40 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.224.58]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 18CEA1800359; Sat, 10 May 2025 14:12:38 +0000 (UTC) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede Subject: [PATCH v2 6/8] libcamera: swstats_cpu: Add support for YUV420 Date: Sat, 10 May 2025 16:12:18 +0200 Message-ID: <20250510141220.54872-7-hdegoede@redhat.com> In-Reply-To: <20250510141220.54872-1-hdegoede@redhat.com> References: <20250510141220.54872-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: zqxOeLR2iKQi-qyNNStFsm-YE9aZEqZTrZ-cUW4hQA0_1746886360 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" Add support for processing YUV420 data. Signed-off-by: Hans de Goede --- .../internal/software_isp/swstats_cpu.h | 6 ++ src/libcamera/software_isp/swstats_cpu.cpp | 89 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h index fa47cec9..a043861c 100644 --- a/include/libcamera/internal/software_isp/swstats_cpu.h +++ b/include/libcamera/internal/software_isp/swstats_cpu.h @@ -71,6 +71,7 @@ public: private: using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]); using processFrameFn = void (SwStatsCpu::*)(MappedFrameBuffer &in); + using finishFrameFn = void (SwStatsCpu::*)(); int setupStandardBayerOrder(BayerFormat::Order order); /* Bayer 8 bpp unpacked */ @@ -82,10 +83,15 @@ private: /* Bayer 10 bpp packed */ void statsBGGR10PLine0(const uint8_t *src[]); void statsGBRG10PLine0(const uint8_t *src[]); + /* YUV420 3 planes */ + void statsYUV420Line0(const uint8_t *src[]); void processBayerFrame2(MappedFrameBuffer &in); + void processYUV420Frame(MappedFrameBuffer &in); + void finishYUV420Frame(); processFrameFn processFrame_; + finishFrameFn finishFrame_; /* Variables set by configure(), used every line */ statsProcessFn stats0_; diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index 1ff15f5b..e81c96a2 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -13,6 +13,7 @@ #include +#include #include #include "libcamera/internal/bayer_format.h" @@ -288,6 +289,40 @@ void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[]) SWSTATS_FINISH_LINE_STATS() } +void SwStatsCpu::statsYUV420Line0(const uint8_t *src[]) +{ + uint64_t sumY = 0; + uint64_t sumU = 0; + uint64_t sumV = 0; + uint8_t y, u, v; + + /* Adjust src[] for starting at window_.x */ + src[0] += window_.x; + src[1] += window_.x / 2; + src[2] += window_.x / 2; + + /* x += 4 sample every other 2x2 block */ + for (int x = 0; x < (int)window_.width; x += 4) { + /* + * Take y from the top left corner of the 2x2 block instead + * of averaging 4 y-s. + */ + y = src[0][x]; + u = src[1][x]; + v = src[2][x]; + + sumY += y; + sumU += u; + sumV += v; + + stats_.yHistogram[y * SwIspStats::kYHistogramSize / 256]++; + } + + stats_.sumR_ += sumY; + stats_.sumG_ += sumU; + stats_.sumB_ += sumV; +} + /** * \brief Reset state to start statistics gathering for a new frame * @@ -313,6 +348,9 @@ void SwStatsCpu::startFrame(void) */ void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId) { + if (finishFrame_) + (this->*finishFrame_)(); + *sharedStats_ = stats_; statsReady.emit(frame, bufferId); } @@ -362,6 +400,20 @@ int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order) int SwStatsCpu::configure(const StreamConfiguration &inputCfg) { stride_ = inputCfg.stride; + finishFrame_ = NULL; + + if (inputCfg.pixelFormat == formats::YUV420) { + patternSize_.height = 2; + patternSize_.width = 2; + /* Skip every 3th and 4th line, sample every other 2x2 block */ + ySkipMask_ = 0x02; + xShift_ = 0; + swapLines_ = false; + stats0_ = &SwStatsCpu::statsYUV420Line0; + processFrame_ = &SwStatsCpu::processYUV420Frame; + finishFrame_ = &SwStatsCpu::finishYUV420Frame; + return 0; + } BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputCfg.pixelFormat); @@ -430,6 +482,43 @@ void SwStatsCpu::setWindow(const Rectangle &window) window_.height &= ~(patternSize_.height - 1); } +void SwStatsCpu::processYUV420Frame(MappedFrameBuffer &in) +{ + const uint8_t *linePointers[3]; + + linePointers[0] = in.planes()[0].data(); + linePointers[1] = in.planes()[1].data(); + linePointers[2] = in.planes()[2].data(); + + /* Adjust linePointers for starting at window_.y */ + linePointers[0] += window_.y * stride_; + linePointers[1] += window_.y * stride_ / 4; + linePointers[2] += window_.y * stride_ / 4; + + for (unsigned int y = 0; y < window_.height; y += 2) { + if (!(y & ySkipMask_)) + (this->*stats0_)(linePointers); + + linePointers[0] += stride_ * 2; + linePointers[1] += stride_ / 2; + linePointers[2] += stride_ / 2; + } +} + +void SwStatsCpu::finishYUV420Frame() +{ + /* sumR_ / G_ / B_ contain Y / U / V sums convert this */ + double divider = (uint64_t)window_.width * window_.height * 256 / 16; + double Y = (double)stats_.sumR_ / divider; + /* U and V 0 - 255 values represent -128 - 127 range */ + double U = (double)stats_.sumG_ / divider - 0.5; + double V = (double)stats_.sumB_ / divider - 0.5; + + stats_.sumR_ = (Y + 1.140 * V) * divider; + stats_.sumG_ = (Y - 0.395 * U - 0.581 * V) * divider; + stats_.sumB_ = (Y + 2.032 * U) * divider; +} + void SwStatsCpu::processBayerFrame2(MappedFrameBuffer &in) { const uint8_t *src = in.planes()[0].data(); From patchwork Sat May 10 14:12:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 23359 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 AC4B8C3226 for ; Sat, 10 May 2025 14:12:48 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6CA0A68B6F; Sat, 10 May 2025 16:12:48 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="FzHkZDR1"; 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 0126A68B6E for ; Sat, 10 May 2025 16:12:45 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1746886365; 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=++/VEgWw8B32Yd9HvFe7+lB5JQrsALJ1AyoyckTnUDY=; b=FzHkZDR1cb49pBFshtVwybmNyYwVcXTpK9VS0ZY5P2wdFcWHFOFIDreUeytrgFtT9084zL f8XYc8L83fpHKGn11reafUPLH1EqKd+ey2T0R6niiGOCs2qVjJepn/BMcWsdgQeRpVH6KA DpRQ+ZQOP+EHDupJkZVsstlmOH+MYOw= Received: from mx-prod-mc-06.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-371-oPKHrd4POkiYi7nwJ7B86Q-1; Sat, 10 May 2025 10:12:43 -0400 X-MC-Unique: oPKHrd4POkiYi7nwJ7B86Q-1 X-Mimecast-MFC-AGG-ID: oPKHrd4POkiYi7nwJ7B86Q_1746886362 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 3682B180035F for ; Sat, 10 May 2025 14:12:42 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.224.58]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id DE9BE1800359; Sat, 10 May 2025 14:12:40 +0000 (UTC) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede Subject: [PATCH v2 7/8] libcamera: ipa_manager: createIPA: Allow matching by IPA name instead of by pipeline Date: Sat, 10 May 2025 16:12:19 +0200 Message-ID: <20250510141220.54872-8-hdegoede@redhat.com> In-Reply-To: <20250510141220.54872-1-hdegoede@redhat.com> References: <20250510141220.54872-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: onu6OcFFOPz2cAyaDukJp8CrIktC-WMXAdvi78SpCVQ_1746886362 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" Parts of the software ISP and the ipa_soft_simple.so IPA may be useful for more then 1 pipeline handler. Currently createIPA() / IPAManager::module() assume that there is a 1:1 relationship between pipeline handlers and IPAs and IPA matching is done based on matching the pipe to ipaModuleInfo.pipelineName[]. One way to allow using a single IPA with multiple pipelines would be to allow the IPA to declare itself compatible with more then one pipeline, turning ipaModuleInfo.pipelineName[] into e.g. a vector. But the way ipaModuleInfo is loaded as an ELF symbol requires it to be a simple flat C-struct. Instead add an optional ipaName argument to createIPA() which when set switches things over to matching ipaModuleInfo.name[] allowing pipelines to request a specific shared IPA module this way. Signed-off-by: Hans de Goede --- include/libcamera/internal/ipa_manager.h | 7 ++++--- include/libcamera/internal/ipa_module.h | 4 ++-- src/libcamera/ipa_manager.cpp | 6 ++++-- src/libcamera/ipa_module.cpp | 19 +++++++++++++------ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h index a0d448cf..af784c9c 100644 --- a/include/libcamera/internal/ipa_manager.h +++ b/include/libcamera/internal/ipa_manager.h @@ -34,11 +34,12 @@ public: template static std::unique_ptr createIPA(PipelineHandler *pipe, uint32_t minVersion, - uint32_t maxVersion) + uint32_t maxVersion, + const char *ipaName = NULL) { CameraManager *cm = pipe->cameraManager(); IPAManager *self = cm->_d()->ipaManager(); - IPAModule *m = self->module(pipe, minVersion, maxVersion); + IPAModule *m = self->module(pipe, minVersion, maxVersion, ipaName); if (!m) return nullptr; @@ -64,7 +65,7 @@ private: unsigned int addDir(const char *libDir, unsigned int maxDepth = 0); IPAModule *module(PipelineHandler *pipe, uint32_t minVersion, - uint32_t maxVersion); + uint32_t maxVersion, const char *ipaName); bool isSignatureValid(IPAModule *ipa) const; diff --git a/include/libcamera/internal/ipa_module.h b/include/libcamera/internal/ipa_module.h index 15f19492..e7b00fdb 100644 --- a/include/libcamera/internal/ipa_module.h +++ b/include/libcamera/internal/ipa_module.h @@ -36,8 +36,8 @@ public: IPAInterface *createInterface(); - bool match(PipelineHandler *pipe, - uint32_t minVersion, uint32_t maxVersion) const; + bool match(PipelineHandler *pipe, uint32_t minVersion, + uint32_t maxVersion, const char *ipaName = NULL) const; protected: std::string logPrefix() const override; diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 830750dc..2ef8b98e 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -240,12 +240,13 @@ unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth) * \param[in] pipe The pipeline handler * \param[in] minVersion Minimum acceptable version of IPA module * \param[in] maxVersion Maximum acceptable version of IPA module + * \param[in] ipaName If set match IPA module by this name instead of by pipe */ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion, - uint32_t maxVersion) + uint32_t maxVersion, const char *ipaName) { for (const auto &module : modules_) { - if (module->match(pipe, minVersion, maxVersion)) + if (module->match(pipe, minVersion, maxVersion, ipaName)) return module.get(); } @@ -258,6 +259,7 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion, * \param[in] pipe The pipeline handler that wants a matching IPA proxy * \param[in] minVersion Minimum acceptable version of IPA module * \param[in] maxVersion Maximum acceptable version of IPA module + * \param[in] ipaName If set match IPA module by this name instead of by pipe * * \return A newly created IPA proxy, or nullptr if no matching IPA module is * found or if the IPA proxy fails to initialize diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp index e6ea61e4..b7004b1c 100644 --- a/src/libcamera/ipa_module.cpp +++ b/src/libcamera/ipa_module.cpp @@ -466,18 +466,25 @@ IPAInterface *IPAModule::createInterface() * \param[in] pipe Pipeline handler to match with * \param[in] minVersion Minimum acceptable version of IPA module * \param[in] maxVersion Maximum acceptable version of IPA module + * \param[in] ipaName If set match IPA module by this name instead of by pipe * * This function checks if this IPA module matches the \a pipe pipeline handler, - * and the input version range. + * and the input version range. If \a ipaName is non-null then the IPA module + * name is matched against \a ipaName instead of matching \a pipe. * * \return True if the pipeline handler matches the IPA module, or false otherwise */ -bool IPAModule::match(PipelineHandler *pipe, - uint32_t minVersion, uint32_t maxVersion) const +bool IPAModule::match(PipelineHandler *pipe, uint32_t minVersion, + uint32_t maxVersion, const char *ipaName) const { - return info_.pipelineVersion >= minVersion && - info_.pipelineVersion <= maxVersion && - !strcmp(info_.pipelineName, pipe->name()); + if (info_.pipelineVersion < minVersion || + info_.pipelineVersion > maxVersion) + return false; + + if (ipaName) + return !strcmp(info_.name, ipaName); + else + return !strcmp(info_.pipelineName, pipe->name()); } std::string IPAModule::logPrefix() const From patchwork Sat May 10 14:12:20 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 23360 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 3D948C3226 for ; Sat, 10 May 2025 14:12:54 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EC22568C91; Sat, 10 May 2025 16:12:53 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="cfuJPzCW"; 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 07F2168C8E for ; Sat, 10 May 2025 16:12:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1746886370; 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=LsqNI+ZkMP6wl7CNTCsZUrK4ini8JqT9CVOxSW+Ks+A=; b=cfuJPzCWG0M2Zuhp+dri0OGe27APeGe1RNtjFqvJTgXozFX5j0Um/BJegrMYb7YcJTnAjX enSQi16L+wRFNey7d5ClGIcrPB80QAq/wjMatkbtdGBV7l2wI8fiKiz5agZsTOgvSUl8Tx jNGKnWP+RDJjwn67aeBwC01iEf2Em60= Received: from mx-prod-mc-06.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-7-mSfpAOUxO5ahfqeJgF2GxA-1; Sat, 10 May 2025 10:12:47 -0400 X-MC-Unique: mSfpAOUxO5ahfqeJgF2GxA-1 X-Mimecast-MFC-AGG-ID: mSfpAOUxO5ahfqeJgF2GxA_1746886364 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 64B5F180045B for ; Sat, 10 May 2025 14:12:44 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.224.58]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id AF2391800359; Sat, 10 May 2025 14:12:42 +0000 (UTC) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede Subject: [PATCH v2 8/8] libcamera: Add new atomisp pipeline handler Date: Sat, 10 May 2025 16:12:20 +0200 Message-ID: <20250510141220.54872-9-hdegoede@redhat.com> In-Reply-To: <20250510141220.54872-1-hdegoede@redhat.com> References: <20250510141220.54872-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: -rjG5YV0POfM_SKxXEHQVklx9HQQlwHJdFi30z8ZtME_1746886364 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" Add a basic atomisp pipeline handler which supports configuring the pipeline, capturing frames and selecting front/back sensor. The atomisp ISP needs some extra lines/columns when debayering and also has some max resolution limitations, this causes the available output resolutions to differ from the sensor resolutions. The atomisp driver's Android heritage means that it mostly works as a non media-controller centric v4l2 device, primarily controlled through its /dev/video# node. The driver takes care of setting up the pipeline itself propagating try / set fmt calls down from its single /dev/video# node to the selected sensor taking the necessary padding, etc. into account. Therefor things like getting the list of support formats / sizes and setFmt() calls are all done on the /dev/video# node instead of on subdevs, this avoids having to duplicate the padding, etc. logic in the pipeline handler. Since the statistics buffers which we get from the ISP2 are not documented this uses the swstats_cpu and simple-IPA from the swisp. At the moment only aec/agc is supported. awb support will be added in a follow-up patch. Signed-off-by: Hans de Goede --- meson.build | 1 + meson_options.txt | 1 + src/ipa/simple/data/uncalibrated_atomisp.yaml | 7 + src/libcamera/pipeline/atomisp/atomisp.cpp | 636 ++++++++++++++++++ src/libcamera/pipeline/atomisp/meson.build | 5 + src/libcamera/software_isp/meson.build | 2 +- 6 files changed, 651 insertions(+), 1 deletion(-) create mode 100644 src/ipa/simple/data/uncalibrated_atomisp.yaml create mode 100644 src/libcamera/pipeline/atomisp/atomisp.cpp create mode 100644 src/libcamera/pipeline/atomisp/meson.build diff --git a/meson.build b/meson.build index 9ba5e2ca..5c4981d8 100644 --- a/meson.build +++ b/meson.build @@ -211,6 +211,7 @@ wanted_pipelines = get_option('pipelines') arch_arm = ['arm', 'aarch64'] arch_x86 = ['x86', 'x86_64'] pipelines_support = { + 'atomisp': arch_x86, 'imx8-isi': arch_arm, 'ipu3': arch_x86, 'mali-c55': arch_arm, diff --git a/meson_options.txt b/meson_options.txt index 2104469e..c7051ee7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -47,6 +47,7 @@ option('pipelines', value : ['auto'], choices : [ 'all', + 'atomisp', 'auto', 'imx8-isi', 'ipu3', diff --git a/src/ipa/simple/data/uncalibrated_atomisp.yaml b/src/ipa/simple/data/uncalibrated_atomisp.yaml new file mode 100644 index 00000000..6dcc0295 --- /dev/null +++ b/src/ipa/simple/data/uncalibrated_atomisp.yaml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: CC0-1.0 +%YAML 1.1 +--- +version: 1 +algorithms: + - Agc: +... diff --git a/src/libcamera/pipeline/atomisp/atomisp.cpp b/src/libcamera/pipeline/atomisp/atomisp.cpp new file mode 100644 index 00000000..959c3f0d --- /dev/null +++ b/src/libcamera/pipeline/atomisp/atomisp.cpp @@ -0,0 +1,636 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * atomisp.cpp - Pipeline handler for atomisp devices + * + * The atomisp ISP needs some extra lines/columns when debayering and also has + * some max resolution limitations, this causes the available output resolutions + * to differ from the sensor resolutions. + * + * The atomisp driver's Android heritage means that it mostly works as a non + * media-controller centric v4l2 device, primarily controlled through its + * /dev/video# node. The driver takes care of setting up the pipeline itself + * propagating try / set fmt calls down from its single /dev/video# node to + * the selected sensor taking the necessary padding, etc. into account. + * + * Therefor things like getting the list of support formats / sizes and tryFmt() + * / setFmt() calls are all done on the /dev/video# node instead of on subdevs, + * this avoids having to duplicate the padding, etc. logic here. + * Note this requires enabling the ISP <-> CSI receiver for the current sensor + * even when only querying / trying formats. + * + * Copyright (C) 2024, Hans de Goede + * + * Partially based on simple.cpp and uvcvideo.cpp which are: + * Copyright (C) 2020, Laurent Pinchart + * Copyright (C) 2019, Martijn Braam + * Copyright (C) 2019, Google Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libcamera/internal/camera.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/delayed_controls.h" +#include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/ipa_manager.h" +#include "libcamera/internal/media_device.h" +#include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/software_isp/debayer_params.h" +#include "libcamera/internal/software_isp/swstats_cpu.h" +#include "libcamera/internal/v4l2_videodevice.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Atomisp) + +class AtomispCameraData : public Camera::Private +{ +public: + AtomispCameraData(PipelineHandler *pipe, V4L2VideoDevice *video) + : Camera::Private(pipe), video_(video) + { + } + + int init(MediaEntity *sensor); + void imageBufferReady(FrameBuffer *buffer); + void statsReady(uint32_t frame, uint32_t bufferId); + void setSensorControls(const ControlList &sensorControls); + + /* This is owned by AtomispPipelineHandler and shared by the cameras */ + V4L2VideoDevice *video_; + std::unique_ptr sensor_; + std::unique_ptr delayedCtrls_; + std::unique_ptr stats_; + std::unique_ptr ipa_; + SharedMemObject debayerParams_; + Stream stream_; + std::map> formats_; + MediaLink *csiReceiverIspLink_; +}; + +class AtomispCameraConfiguration : public CameraConfiguration +{ +public: + AtomispCameraConfiguration() + : CameraConfiguration() {} + + Status validate() override; +}; + +class AtomispPipelineHandler : public PipelineHandler +{ +public: + AtomispPipelineHandler(CameraManager *manager) + : PipelineHandler(manager) {} + + std::unique_ptr generateConfiguration(Camera *camera, + Span roles) override; + int configure(Camera *camera, CameraConfiguration *config) override; + + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + + int start(Camera *camera, const ControlList *controls) override; + void stopDevice(Camera *camera) override; + + int queueRequestDevice(Camera *camera, Request *request) override; + + bool match(DeviceEnumerator *enumerator) override; + +private: + bool acquireDevice(Camera *camera) override; + void releaseDevice(Camera *camera) override; + + AtomispCameraData *cameraData(Camera *camera) + { + return static_cast(camera->_d()); + } + + std::unique_ptr video_; +}; + +bool AtomispPipelineHandler::acquireDevice(Camera *camera) +{ + AtomispCameraData *data = cameraData(camera); + + /* atomisp can run only 1 sensor / camera at a time */ + if (data->video_->isOpen()) + return false; + + int ret = data->video_->open(); + if (ret != 0) + return false; + + ret = data->csiReceiverIspLink_->setEnabled(true); + if (ret) { + data->video_->close(); + return false; + } + + return true; +} + +void AtomispPipelineHandler::releaseDevice(Camera *camera) +{ + AtomispCameraData *data = cameraData(camera); + + data->csiReceiverIspLink_->setEnabled(false); + data->video_->close(); +} + +CameraConfiguration::Status AtomispCameraConfiguration::validate() +{ + Status status = Valid; + + if (config_.empty()) + return Invalid; + + if (orientation != Orientation::Rotate0) { + orientation = Orientation::Rotate0; + status = Adjusted; + } + + /* Cap the number of entries to the available streams. */ + if (config_.size() > 1) { + config_.resize(1); + status = Adjusted; + } + + StreamConfiguration &cfg = config_[0]; + const StreamFormats &formats = cfg.formats(); + const PixelFormat pixelFormat = cfg.pixelFormat; + const Size size = cfg.size; + + const std::vector pixelFormats = formats.pixelformats(); + auto iter = std::find(pixelFormats.begin(), pixelFormats.end(), pixelFormat); + if (iter == pixelFormats.end()) { + cfg.pixelFormat = pixelFormats.front(); + LOG(Atomisp, Debug) + << "Adjusting pixel format from " << pixelFormat + << " to " << cfg.pixelFormat; + status = Adjusted; + } + + const std::vector &formatSizes = formats.sizes(cfg.pixelFormat); + cfg.size = formatSizes.front(); + for (const Size &formatsSize : formatSizes) { + if (formatsSize > size) + break; + + cfg.size = formatsSize; + } + + if (cfg.size != size) { + LOG(Atomisp, Debug) + << "Adjusting size from " << size << " to " << cfg.size; + status = Adjusted; + } + + /* + * The atomisp has an internal pipeline length of 3 frames, + * it generates 3A statistics info 1 - 2 frames before generating + * the video buffer with the final image. + * + * We need to make sure this pipeline is fed with buffers all + * the time since the firmware simply asserts on buffer underruns + * after which the driver has to recover by stopping streaming, + * resetting the ISP and then starting the stream again. + * + * Use a buffercount of 8 so that we can have 1 - 3 buffers waiting + * on userspace while still having 2 reserve buffers owned by + * the kernel + 3 buffers owned by the ISP. + */ + cfg.bufferCount = 8; + + switch (cfg.pixelFormat) { + case formats::YUV420: + /* atomisp stride must be a multiple of 32 */ + cfg.stride = (cfg.size.width + 31) & ~31; + cfg.frameSize = cfg.stride * cfg.size.height * 3 / 2; + break; + default: + LOG(Atomisp, Error) + << "Unknown pixel-format " << cfg.pixelFormat; + return Invalid; + } + + if (cfg.colorSpace != ColorSpace::Rec709) { + cfg.colorSpace = ColorSpace::Rec709; + status = Adjusted; + } + + return status; +} + +std::unique_ptr +AtomispPipelineHandler::generateConfiguration(Camera *camera, + Span roles) +{ + AtomispCameraData *data = cameraData(camera); + std::unique_ptr config = + std::make_unique(); + + if (roles.empty()) + return config; + + StreamFormats formats(data->formats_); + StreamConfiguration cfg(formats); + + cfg.pixelFormat = formats.pixelformats().front(); + cfg.size = formats.sizes(cfg.pixelFormat).back(); + cfg.bufferCount = 4; + + config->addConfiguration(cfg); + + config->validate(); + + return config; +} + +int AtomispPipelineHandler::configure(Camera *camera, CameraConfiguration *config) +{ + AtomispCameraData *data = cameraData(camera); + StreamConfiguration &cfg = config->at(0); + + int ret = data->stats_->configure(cfg); + if (ret) + return ret; + + ipa::soft::IPAConfigInfo configInfo; + configInfo.sensorControls = data->sensor_->controls(); + + ret = data->ipa_->configure(configInfo); + if (ret) + return ret; + + V4L2PixelFormat fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat); + V4L2DeviceFormat format; + + format.size = cfg.size; + format.fourcc = fourcc; + ret = data->video_->setFormat(&format); + if (ret) + return ret; + + if (format.size != cfg.size) { + LOG(Atomisp, Error) + << "format mismatch req " << cfg.size << " got " << format.size; + return -EINVAL; + } + + if (format.fourcc != fourcc) { + LOG(Atomisp, Error) + << "format mismatch req " << fourcc << " got " << format.fourcc; + return -EINVAL; + } + + data->stats_->setWindow(Rectangle(cfg.size)); + + cfg.setStream(&data->stream_); + + std::unordered_map params = { + { V4L2_CID_ANALOGUE_GAIN, { 2, false } }, + { V4L2_CID_EXPOSURE, { 2, false } }, + }; + data->delayedCtrls_ = + std::make_unique(data->sensor_->device(), + params); + data->video_->frameStart.connect(data->delayedCtrls_.get(), + &DelayedControls::applyControls); + return 0; +} + +int AtomispPipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + AtomispCameraData *data = cameraData(camera); + unsigned int count = stream->configuration().bufferCount; + + return data->video_->exportBuffers(count, buffers); +} + +int AtomispPipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlList *controls) +{ + AtomispCameraData *data = cameraData(camera); + unsigned int count = data->stream_.configuration().bufferCount; + int ret; + + ret = data->video_->importBuffers(count); + if (ret) + return ret; + + video_->bufferReady.connect(data, &AtomispCameraData::imageBufferReady); + + ret = data->video_->streamOn(); + if (ret) { + video_->bufferReady.disconnect(data, &AtomispCameraData::imageBufferReady); + data->video_->releaseBuffers(); + return ret; + } + + ret = data->ipa_->start(); + if (ret) { + data->video_->streamOff(); + video_->bufferReady.disconnect(data, &AtomispCameraData::imageBufferReady); + data->video_->releaseBuffers(); + return ret; + } + + return 0; +} + +void AtomispPipelineHandler::stopDevice(Camera *camera) +{ + AtomispCameraData *data = cameraData(camera); + + data->video_->streamOff(); + video_->bufferReady.disconnect(data, &AtomispCameraData::imageBufferReady); + data->video_->releaseBuffers(); + data->ipa_->stop(); +} + +int AtomispPipelineHandler::queueRequestDevice(Camera *camera, Request *request) +{ + AtomispCameraData *data = cameraData(camera); + FrameBuffer *buffer = request->findBuffer(&data->stream_); + if (!buffer) { + LOG(Atomisp, Error) + << "Attempt to queue request with invalid stream"; + + return -ENOENT; + } + + int ret = data->video_->queueBuffer(buffer); + if (ret) + return ret; + + data->ipa_->queueRequest(request->sequence(), request->controls()); + return 0; +} + +/* ----------------------------------------------------------------------------- + * Match and Setup + */ +bool AtomispPipelineHandler::match(DeviceEnumerator *enumerator) +{ + std::vector sensors; + MediaEntity *videoEntity = NULL; + DeviceMatch dm("atomisp-isp2"); + MediaDevice *media; + + media = acquireMediaDevice(enumerator, dm); + if (!media) + return false; + + for (MediaEntity *entity : media->entities()) { + switch (entity->function()) { + case MEDIA_ENT_F_CAM_SENSOR: + sensors.push_back(entity); + break; + case MEDIA_ENT_F_IO_V4L: + videoEntity = entity; + break; + } + } + + if (!videoEntity) { + LOG(Atomisp, Error) << "Could not find the video device"; + return false; + } + if (sensors.empty()) { + LOG(Atomisp, Error) << "No sensor found"; + return false; + } + + /* Create and open the video device. */ + video_ = std::make_unique(videoEntity); + int ret = video_->open(); + if (ret) + return false; + + /* Create and register a camera for each sensor */ + bool registered = false; + for (MediaEntity *sensor : sensors) { + std::unique_ptr data = + std::make_unique(this, video_.get()); + + if (data->init(sensor)) + continue; + + const std::string &id = data->sensor_->id(); + std::set streams{ &data->stream_ }; + std::shared_ptr camera = + Camera::create(std::move(data), id, streams); + registerCamera(std::move(camera)); + registered = true; + } + + /* + * atomisp cameras share a single /dev/video# node. The shared node + * gets opened from acquireDevice() to allow only one camera to be + * acquired at a time. + * This also works around a kernel bug (which needs to be fixed) where + * the node needs to be closed for the ISP to runtime-suspend. + */ + video_->close(); + + return registered; +} + +int AtomispCameraData::init(MediaEntity *sensor) +{ + MediaEntity *source, *sink, *csi_receiver = NULL; + const MediaPad *source_pad; + int source_pad_idx = 0; + + sensor_ = CameraSensorFactoryBase::create(sensor); + if (!sensor_) + return -ENODEV; + + debayerParams_ = SharedMemObject("debayer_params"); + if (!debayerParams_) { + LOG(Atomisp, Error) << "Failed to create shared memory for parameters"; + return -ENOMEM; + } + + stats_ = std::make_unique(); + if (!stats_->isValid()) { + LOG(Atomisp, Error) << "Failed to create SwStatsCpu object"; + return -ENOMEM; + } + + ipa_ = IPAManager::createIPA(pipe(), 0, 0, "simple"); + if (!ipa_) { + LOG(Atomisp, Error) << "Creating IPA failed"; + return -ENOMEM; + } + + /* + * The API tuning file is made from the sensor name. If the tuning file + * isn't found, fall back to the 'uncalibrated' file. + */ + std::string ipaTuningFile = + ipa_->configurationFile(sensor_->model() + "_atomisp.yaml", + "uncalibrated_atomisp.yaml"); + + IPACameraSensorInfo sensorInfo{}; + int ret = sensor_->sensorInfo(&sensorInfo); + if (ret) { + LOG(Atomisp, Error) << "Camera sensor information not available"; + return -ENODEV; + } + + /* Passing CCM parameters to the ISP is not support (yet?) */ + bool ccmEnabled = false; + + ret = ipa_->init(IPASettings{ ipaTuningFile, sensor_->model() }, + stats_->getStatsFD(), + debayerParams_.fd(), + sensorInfo, + sensor_->controls(), + &controlInfo_, + &ccmEnabled); + if (ret) { + LOG(Atomisp, Error) << "IPA init failed"; + return ret; + } + + source = sensor; + for (int i = 0; i < 2; i++) { + source_pad = source->getPadByIndex(source_pad_idx); + if (source_pad == nullptr) { + LOG(Atomisp, Error) + << source << " doesn't have pad " << source_pad_idx; + return -ENODEV; + } + + sink = source_pad->links()[0]->sink()->entity(); + switch (sink->function()) { + case MEDIA_ENT_F_VID_IF_BRIDGE: + /* Found the CSI2 receiver */ + csi_receiver = sink; + break; + case MEDIA_ENT_F_PROC_VIDEO_ISP: + /* + * Sensor with builtin ISP, e.g. MT9M114. + * CSI receiver is downstream of this entity. + */ + source = sink; + source_pad_idx = 1; + continue; + default: + LOG(Atomisp, Error) + << sink << " has unexpected function " << sink->function(); + return -ENODEV; + } + } + if (!csi_receiver) { + LOG(Atomisp, Error) + << "Camera " << sensor_->model() << " cannot find CSI receiver"; + return -ENODEV; + } + + source_pad = csi_receiver->getPadByIndex(1); + if (source_pad == nullptr) { + LOG(Atomisp, Error) + << "CSI receiver for " << sensor_->model() << " doesn't have pad1"; + return -ENODEV; + } + + csiReceiverIspLink_ = source_pad->links()[0]; + + ret = csiReceiverIspLink_->setEnabled(true); + if (ret) + return ret; + + /* + * The atomisp supports many different output formats but SwStatsCpu, + * used for 3A due to atomisp statistics being undocumented, + * only supports a few formats. + */ + std::vector supported({ formats::YUV420 }); + + for (const auto &format : video_->formats()) { + PixelFormat pixelFormat = format.first.toPixelFormat(); + + if (std::find(supported.begin(), supported.end(), pixelFormat) == supported.end()) + continue; + + formats_[pixelFormat] = format.second; + } + + csiReceiverIspLink_->setEnabled(false); + + if (formats_.empty()) { + LOG(Atomisp, Error) + << "Camera " << sensor_->model() + << " doesn't expose any supported format"; + return -EINVAL; + } + + properties_ = sensor_->properties(); + + stats_->statsReady.connect(this, &AtomispCameraData::statsReady); + ipa_->setSensorControls.connect(this, &AtomispCameraData::setSensorControls); + + return 0; +} + +void AtomispCameraData::imageBufferReady(FrameBuffer *buffer) +{ + Request *request = buffer->request(); + + if (buffer->metadata().status == FrameMetadata::FrameSuccess) { + ipa_->computeParams(request->sequence()); + + /* + * Buffer ids are currently not used, so pass zero as buffer id. + * + * \todo Pass real bufferId once stats buffer passing is changed. + */ + stats_->processFrame(request->sequence(), 0, buffer); + + request->metadata().set(controls::SensorTimestamp, + buffer->metadata().timestamp); + } + + pipe()->completeBuffer(request, buffer); + pipe()->completeRequest(request); +} + +void AtomispCameraData::statsReady(uint32_t frame, uint32_t bufferId) +{ + ipa_->processStats(frame, bufferId, delayedCtrls_->get(frame)); +} + +void AtomispCameraData::setSensorControls(const ControlList &sensorControls) +{ + delayedCtrls_->push(sensorControls); + ControlList ctrls(sensorControls); + sensor_->setControls(&ctrls); +} + +REGISTER_PIPELINE_HANDLER(AtomispPipelineHandler, "atomisp") + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/atomisp/meson.build b/src/libcamera/pipeline/atomisp/meson.build new file mode 100644 index 00000000..179a35ef --- /dev/null +++ b/src/libcamera/pipeline/atomisp/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: CC0-1.0 + +libcamera_internal_sources += files([ + 'atomisp.cpp', +]) diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build index 59fa5f02..81c4c255 100644 --- a/src/libcamera/software_isp/meson.build +++ b/src/libcamera/software_isp/meson.build @@ -1,6 +1,6 @@ # SPDX-License-Identifier: CC0-1.0 -softisp_enabled = pipelines.contains('simple') +softisp_enabled = pipelines.contains('simple') or pipelines.contains('atomisp') summary({'SoftISP support' : softisp_enabled}, section : 'Configuration') if not softisp_enabled