From patchwork Thu Oct 29 13:26:27 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28697 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 D12A4C3261 for ; Thu, 29 Oct 2026 13:27:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 80555690C6; Thu, 29 Oct 2026 14:27:15 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="rMftM209"; dkim-atps=neutral Received: from mail-vs1-xe2d.google.com (mail-vs1-xe2d.google.com [IPv6:2607:f8b0:4864:20::e2d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 483FF68E07 for ; Thu, 29 Oct 2026 14:27:13 +0100 (CET) Received: by mail-vs1-xe2d.google.com with SMTP id ada2fe7eead31-7cb6dc850c7so683517137.0 for ; Thu, 29 Oct 2026 06:27:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793280432; x=1793885232; 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=rMftM209/kkx/zq/9SXISDiIXLhizPaw+zoJPEA9mVHBeDCCTEPXcT6sfb7g5AgPI8 d4s3B9oV6ZbDc6r2I1fR8wCPP0y6U3q0GEbqWtWx5k9aOYUNZhdSJQaDHgNNR0IWnyBD nUSI+tlxQc7ifu52z/+BSJvwfAfmYTCtP9cf4Gp1e3fjt3rZOec/H4c+klNW3yMOArlV KsX2kmRdHWqjyWXeM1fc9WTZm58Cqk1SKmXG40BxjKRxoq9iBNSwofVnFCGrI32qrsgq 2cGjHJDyBHc9iK3mmIoHaFYwfWHE/RJbLxXPAo1mBY2UFC5OG0ruQOAEEnTOVG47jKRo +VUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793280432; x=1793885232; 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=P+UsTxUah5BS3ddlGE6yxLnY6bA5e8mKRDOnQMR3dvNp+hJSLiCee2tbSPia4whIMj qF3dtMTnt767Ab0WY9+W0aKpGoEv/TfsG9lmSXK4D7nwA/jCqFeRf8Qz7o9inlCbu6gC YELkUbmYwWJpEk5QDJLfFDrnn00e1Rdwf0BB7AViuv6YH4tjWB6JRpVHzaGF/6TohlBE eM0rDgu0Ikmn1eu6fAkSp87/le3INU1CgPgq2vfnTTK4p1ooq8T1cY2dV3IFBG5jec6C UUc3bqM8CPiMjIQTgdzinLMt+Bdlp12b54ZiDHNUMiF5A/apZaexVo580JnNqrOR0q6G Y70A== X-Gm-Message-State: AFq9FYKZjIcM5OEd6d+pCZDl7CGjTHs6rZ0mgcBZzXveo0nsN77PIt+x 0h2FVbu20fhY2cS2rlSNvcYlOUio56Jjx+jYzaXXbiqVwSfVHIJwIv21DsDCCVx3c5o= X-Gm-Gg: AYBFou0nONF8cR383vGQBuADg9QFu3/BK0XTBiQ0X+AeaPScXHxc01fUtdVXG4Y8Xr6 1lXkAO76MFvLYKTbTD8yjX7g+QM9EzdthYAAS4l6Wut1J9WzH6+/jZJQC+UHLi29UtZaZdEprq2 tDcfIpXjz7DNRgYFgqE8iqG+C5uNdrupv4skR08gcfjI41TEP7SXeiNVaT1HWODawVHMYXyIe2D QHnVDCAOOJq6S2g6g/NSSwAX0X5mba5tghq12mG/MbwnrjkJzKeJClDBs7joXsap1sFNK1W4ar0 6Q2MknfkzsKxnNatAMVddXN/XeOE3i8sKAIvw9nzakFcFyIXX0ru/gePjn1bnLPA+8GpvSv1yAq VZcjqTt+naoVJg1HmVM1/rMDMPn2outKgotlnarAmI67wNwETcv7QFWmi9H7FuMR8jWx76bBXUl PHCQzZ3a4bwyfi50t82rwpNWyNLLtn5RTRaEhOLSULcz9OCTlH9zh3VYS9RXN8ZkqH1sig0aPTQ yLb0dhUHyH+KD55SFfTFsz2ykSdMEAZJf/W+LFm0TS2UuH5FBEU4n+/ylVrFPS9kalMPBqN8vwC qzetouiE/XkdMWm18+IJDb4ftMQOpkbuDWHsYV0UmUFySLtp4ar7kC0gOQ== X-Received: by 2002:a05:6102:418d:b0:7cc:1a41:20e7 with SMTP id ada2fe7eead31-7d03a504abdmr2055263137.7.1793280432027; Thu, 29 Oct 2026 06:27:12 -0700 (PDT) Received: from localhost ([144.121.52.163]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-99125931983sm9476046241.2.2026.10.29.06.27.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Oct 2026 06:27:11 -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 v4 5/9] tests: Check the configured CPU image crop Date: Thu, 29 Oct 2026 09:26:27 -0400 Message-ID: <20261029132631.370507-6-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261029132631.370507-1-halfline@gmail.com> References: <20261016202818.1405645-1-halfline@gmail.com> <20261029132631.370507-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