From patchwork Thu Oct 29 15:20:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28710 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 ACABFC324C for ; Thu, 29 Oct 2026 15:21:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 651B0690CD; Thu, 29 Oct 2026 16:21:59 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="p3Puh2nB"; dkim-atps=neutral Received: from mail-vs1-xe29.google.com (mail-vs1-xe29.google.com [IPv6:2607:f8b0:4864:20::e29]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4F3DF690A0 for ; Thu, 29 Oct 2026 16:21:58 +0100 (CET) Received: by mail-vs1-xe29.google.com with SMTP id ada2fe7eead31-7cc53938a8fso1414325137.2 for ; Thu, 29 Oct 2026 08:21:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793287317; x=1793892117; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=15OpypEOMfzejTaMux0ThDcUk4K2DawSq0n9ubUEwWY=; b=p3Puh2nBWTMmKGAlqXaae+zqFbmGA29XBpRP0JuJK7ryneuNv3RgkicOe6Beao1Ya2 RJ04U9f/e13884MRLRcI5qUB7dvePVyZ6IKEPYNH25RyBzmu81Qv5p1scKJSSITOmOd3 /VGkiGBbkxr/xh5o+4HezA2vbnaBdDlMECciCdYXwT1BbU5bz9ml2oMssIb70Oa2Ud0/ jAEXJNk9RXc9OUnPpdk4yrO2ff735eN675XKgF75yUh3xRkdUY1YOeo6hpxZrfoKiTFG EIe3optw/H+RINJNuny2U9DXhCx9siayCHjrZfnvnEXGaCqiZbgAgxrCjZH37InqRe4Z mvWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793287317; x=1793892117; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=15OpypEOMfzejTaMux0ThDcUk4K2DawSq0n9ubUEwWY=; b=Phs2Pq+4eYWzUv3BQn8iTkfw9q4VacKtUXjjDcnLb5jAPyeAsauBu+42FSmKGA0ZzZ wtp+yHK7oN9dQ/fPQe9RJ0F8HY7GTHXA+X1YSOK4C/uXNe4b6qF9i5Pk6ZvrpgN+HxbX bAXpL3jYSUVqSLhniKpilpsd5rCVO9k+nEguk75WaVSGb34HLvXpfqhzRhr22soOHBtW qITX5GcfhvKc78TKhYdLsZ4oKtTlUQSVKlrDrrpk8ud0tZmQiGhq9EwEoZQ37l0M/QRE ch6ZDI9cyUEDQnBFPGHsujKlHQjLjmMx5aD70MC40+oguy2EBq4U1tsVWxCtVmO4uGw8 ywNw== X-Gm-Message-State: AFq9FYJVF0O6qLtxkghbCZi7LdxahnCoI4cP6Iwqs6zR9WY8RtDfgkwb AcNe4No7LLtH814NM5nePYcYQMOLHBDaHwgZzWTn+yNX80XVfz4ieg2GMp+wcjUGrac= X-Gm-Gg: AYBFou2P4JHdwTe2bHvjDH0Irmr9CSLRi+/WY3iscNhXhI8OWWaLdAFjoOJ/d2KMkW5 Ry/dMGVay2UDvq7Wwr19bOEh4fiKGXCxl8mTpysMJWwhjZeuLD2glfH0Mc76Z3maSfuAEk/VDJx Pmikch64vek50+dBiENzgAd7gR3aYPFEKJ1R+Onm06Uhjs1D7nz/wu1vla99u7N+4rQYFDRTPjF 7cXXsrv56EXg0+1u1okpuBMIp52Qszzh5YWhOAXuzdTAguhWz8zpxlTyZZ0KgNOul/TIeK7NRxi S2xFItfzKBqRpIWDtUHVaXWSduC4532Ays1TpS5EsB/MJ9HnWJ6QP+sOlRrNfWiVUEWpSR97WeG 0P/A0bVpncsEQLeCIalVl456gjWx82Uspx0DvnQfKZkAF70Mbams+PU7bYXZf59fpq/BY9DhusF j8xoQHkSQuJyLC/Qnbf+c9n/OWqlit73nenvfrL0GDCHWFzbu6CyiVv+Nzzn0gjMqOGOL1eEt/g vEv6LPnoHvklmARU7bWbgRqZYBCVwTTubKmF2+DD9d6jFUh5XWUDLXK3n5VIWxLeELyvl8cMNRI rhSGSfQ/3rL8wkfmY6I2xQSgPQQ0P4Up8d/b0YUrO8EEfx/BL6Ya2z2b X-Received: by 2002:a05:6102:6043:b0:7cb:bc5e:7d9f with SMTP id ada2fe7eead31-7d03a417b55mr2675409137.11.1793287316992; Thu, 29 Oct 2026 08:21:56 -0700 (PDT) Received: from localhost ([144.121.52.163]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-991f74e5e22sm206563241.4.2026.10.29.08.21.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Oct 2026 08:21:56 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: kieran.bingham@ideasonboard.com, bod.linux@nxsw.ie, laurent.pinchart@ideasonboard.com, stefan.klug@ideasonboard.com, martincarvalho@gmail.com Subject: [PATCH v5 07/11] tests: Check the configured CPU image crop Date: Thu, 29 Oct 2026 11:20:54 -0400 Message-ID: <20261029152058.896209-8-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261029152058.896209-1-halfline@gmail.com> References: <20261029152058.896209-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode This commit checks the crop returned by CPU debayering for supported Bayer input formats and both RGB channel orders. Unpacked kernels may shift the first rendered pixel by one column depending on the Bayer pattern and output channel order. The reported image region must include that shift, while the specialized packed kernels retain their unshifted origin. The configuration-only cases cover all four standard Bayer orders at 8, 10 and 12 bits, including packed RAW10 and RAW12. Reusing the debayer across configurations also checks that changing formats clears a previous phase shift. Signed-off-by: Ray Strode --- test/meson.build | 1 + test/software_isp/debayer-crop.cpp | 102 +++++++++++++++++++++++++++++ test/software_isp/meson.build | 20 ++++++ 3 files changed, 123 insertions(+) create mode 100644 test/software_isp/debayer-crop.cpp create mode 100644 test/software_isp/meson.build diff --git a/test/meson.build b/test/meson.build index 5027b7b1..fc72d105 100644 --- a/test/meson.build +++ b/test/meson.build @@ -8,60 +8,61 @@ endif test_enabled = true # When ASan is enabled, find the path to the ASan runtime needed by multiple # tests. This currently works with gcc only, as clang uses different file names # depending on the compiler version and target architecture. asan_enabled = false asan_runtime_missing = false if get_option('b_sanitize').contains('address') asan_enabled = true if cc.get_id() == 'gcc' asan_runtime = run_command(cc, '-print-file-name=libasan.so', check : true).stdout().strip() else asan_runtime_missing = true endif endif subdir('libtest') subdir('camera') subdir('controls') subdir('gstreamer') subdir('ipa') subdir('ipc') subdir('log') subdir('media_device') subdir('process') subdir('py') subdir('serialization') +subdir('software_isp') subdir('stream') subdir('v4l2_compat') subdir('v4l2_subdevice') subdir('v4l2_videodevice') public_tests = [ {'name': 'color-space', 'sources': ['color-space.cpp']}, {'name': 'geometry', 'sources': ['geometry.cpp']}, {'name': 'public-api', 'sources': ['public-api.cpp']}, {'name': 'signal', 'sources': ['signal.cpp']}, {'name': 'transform', 'sources': ['transform.cpp']}, ] internal_tests = [ {'name': 'bayer-format', 'sources': ['bayer-format.cpp']}, {'name': 'byte-stream-buffer', 'sources': ['byte-stream-buffer.cpp']}, {'name': 'camera-sensor', 'sources': ['camera-sensor.cpp']}, {'name': 'debayer-geometry', 'sources': ['debayer-geometry.cpp']}, {'name': 'delayed_controls', 'sources': ['delayed_controls.cpp']}, {'name': 'event', 'sources': ['event.cpp']}, {'name': 'event-dispatcher', 'sources': ['event-dispatcher.cpp']}, {'name': 'event-thread', 'sources': ['event-thread.cpp']}, {'name': 'file', 'sources': ['file.cpp']}, {'name': 'flags', 'sources': ['flags.cpp']}, {'name': 'hotplug-cameras', 'sources': ['hotplug-cameras.cpp']}, {'name': 'matrix', 'sources': ['matrix.cpp']}, {'name': 'message', 'sources': ['message.cpp']}, {'name': 'object', 'sources': ['object.cpp']}, {'name': 'object-delete', 'sources': ['object-delete.cpp']}, {'name': 'object-invoke', 'sources': ['object-invoke.cpp']}, diff --git a/test/software_isp/debayer-crop.cpp b/test/software_isp/debayer-crop.cpp new file mode 100644 index 00000000..4f9f8b9a --- /dev/null +++ b/test/software_isp/debayer-crop.cpp @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026, Red Hat Inc. + * + * CPU debayer crop configuration tests + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libcamera/internal/formats.h" +#include "libcamera/internal/software_isp/swstats_cpu.h" + +#include "debayer_cpu.h" +#include "test.h" + +using namespace libcamera; + +class DebayerCropTest : public Test +{ +protected: + int run() override + { + CameraManager manager; + auto stats = std::make_unique(manager); + if (!stats->isValid()) + return TestFail; + + DebayerCpu debayer(std::move(stats), manager); + static const struct { + PixelFormat input; + int rgbShift; + int bgrShift; + } cases[] = { + { formats::SBGGR8, 0, 1 }, + { formats::SGBRG8, 1, 0 }, + { formats::SGRBG8, 0, 1 }, + { formats::SRGGB8, 1, 0 }, + { formats::SBGGR10, 0, 1 }, + { formats::SGBRG10, 1, 0 }, + { formats::SGRBG10, 0, 1 }, + { formats::SRGGB10, 1, 0 }, + { formats::SBGGR12, 0, 1 }, + { formats::SGBRG12, 1, 0 }, + { formats::SGRBG12, 0, 1 }, + { formats::SRGGB12, 1, 0 }, + /* Packed kernels use each Bayer order without a phase shift. */ + { formats::SBGGR10_CSI2P, 0, 0 }, + { formats::SGBRG10_CSI2P, 0, 0 }, + { formats::SGRBG10_CSI2P, 0, 0 }, + { formats::SRGGB10_CSI2P, 0, 0 }, + { formats::SBGGR12_CSI2P, 0, 0 }, + { formats::SGBRG12_CSI2P, 0, 0 }, + { formats::SGRBG12_CSI2P, 0, 0 }, + { formats::SRGGB12_CSI2P, 0, 0 }, + }; + + for (const auto &entry : cases) { + StreamConfiguration input; + input.pixelFormat = entry.input; + input.size = { 1928, 1092 }; + input.stride = PixelFormatInfo::info(entry.input).stride(input.size.width, 0); + + for (PixelFormat format : { formats::RGB888, formats::BGR888, + formats::XRGB8888, formats::XBGR8888 }) { + StreamConfiguration output; + output.pixelFormat = format; + output.size = { 1280, 720 }; + std::tie(output.stride, output.frameSize) = + debayer.strideAndFrameSize(format, output.size); + const std::vector> outputs{ output }; + if (debayer.configure(input, outputs, false, false)) { + std::cerr << "Unable to configure " << entry.input << " -> " << format + << std::endl; + return TestFail; + } + + int shift = (format == formats::RGB888 || format == formats::XRGB8888) + ? entry.rgbShift + : entry.bgrShift; + Rectangle expected(324 + shift, 186, 1280, 720); + if (debayer.inputCrop() != expected) { + std::cerr << "Crop for " << entry.input << " -> " << format << " is " + << debayer.inputCrop() << ", expected " << expected << std::endl; + return TestFail; + } + } + } + + return TestPass; + } +}; + +TEST_REGISTER(DebayerCropTest) diff --git a/test/software_isp/meson.build b/test/software_isp/meson.build new file mode 100644 index 00000000..ebabde59 --- /dev/null +++ b/test/software_isp/meson.build @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: CC0-1.0 + +if not softisp_enabled + subdir_done() +endif + +software_isp_tests = [ + {'name': 'debayer-crop', 'sources': ['debayer-crop.cpp']}, +] + +foreach test : software_isp_tests + exe = executable(test['name'], test['sources'], + dependencies : libcamera_private, + implicit_include_directories : false, + link_with : test_libraries, + include_directories : [test_includes_internal, + '../../src/libcamera/software_isp/']) + + test(test['name'], exe, suite : 'software_isp') +endforeach