Patch Detail
Show a patch.
GET /api/1.1/patches/24846/?format=api
{ "id": 24846, "url": "https://patchwork.libcamera.org/api/1.1/patches/24846/?format=api", "web_url": "https://patchwork.libcamera.org/patch/24846/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20251028-exposure-limits-v2-8-a8b5a318323e@ideasonboard.com>", "date": "2025-10-28T09:31:54", "name": "[v2,08/10] ipa: libipa: agc: Initialize a sensible frame duration", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "52cfb3f15743c62d1e886be9938b362f84bc0962", "submitter": { "id": 143, "url": "https://patchwork.libcamera.org/api/1.1/people/143/?format=api", "name": "Jacopo Mondi", "email": "jacopo.mondi@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/24846/mbox/", "series": [ { "id": 5536, "url": "https://patchwork.libcamera.org/api/1.1/series/5536/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5536", "date": "2025-10-28T09:31:46", "name": "libipa: agc: Calculate exposure limits", "version": 2, "mbox": "https://patchwork.libcamera.org/series/5536/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/24846/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/24846/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 1B41BBE080\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 28 Oct 2025 09:32:27 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 14933607F3;\n\tTue, 28 Oct 2025 10:32:26 +0100 (CET)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 08616607B6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 28 Oct 2025 10:32:12 +0100 (CET)", "from [192.168.0.172] (mob-5-90-58-13.net.vodafone.it [5.90.58.13])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 48722176B; \n\tTue, 28 Oct 2025 10:30:23 +0100 (CET)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"AoBAfqZR\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1761643823;\n\tbh=7tTe6peYOLaAek4d+BsUdN3DnG0br7JpENOXWV164eQ=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=AoBAfqZRYqkj0pvDvNX09cz48UPzcPOwPoqmCn23IHvcwaqsrkqsdUw1XcGqvqaTq\n\tT0hZCB6N+6wMDqFQ2ySb0kVxvMUzdo/fvnuVoioDvo2vWGjTwcd7+0pvFINc21G332\n\tyyTxTl93SW3iKyxUintaJLIWQqZLvo6UGjXVeBj4=", "From": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>", "Date": "Tue, 28 Oct 2025 10:31:54 +0100", "Subject": "[PATCH v2 08/10] ipa: libipa: agc: Initialize a sensible frame\n\tduration", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20251028-exposure-limits-v2-8-a8b5a318323e@ideasonboard.com>", "References": "<20251028-exposure-limits-v2-0-a8b5a318323e@ideasonboard.com>", "In-Reply-To": "<20251028-exposure-limits-v2-0-a8b5a318323e@ideasonboard.com>", "To": "=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>,\n\tRobert Mader <robert.mader@collabora.com>, \n\tlibcamera-devel@lists.libcamera.org", "Cc": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>", "X-Mailer": "b4 0.14.2", "X-Developer-Signature": "v=1; a=openpgp-sha256; l=12310;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=7tTe6peYOLaAek4d+BsUdN3DnG0br7JpENOXWV164eQ=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpAI2WRutu0JDhEazAyVR3QUgWr4vZWUXN2IXrR\n\tWretAoppAKJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaQCNlgAKCRByNAaPFqFW\n\tPDM8D/4pyBxucuD/xUDwOSlBwPdLeQvTWy0rjcKWdIAbIP+Rgunr99R4cnRwWs2MAtju3v+n9HW\n\t+H46/toTvDIlWp0ccn/ZH+Gx3m6aX+Cs3vFXc6F/HXxKmx57z8ojRJSbYnIJx36RISh4d0jgUi/\n\tesLss72dZb0tFRfKwvorTLAF6H5+o3fbDET1tFobIz4tso7LD6qA5Gon4hkcucGMiL20iAubzzk\n\t4RW71jPYwIaZtttEPX0H4VeThb1En++ivOVgtjGnbrIjWO8xcN33wCVICbp3Rt94x/leeYNkXbc\n\tSkpvVGRN8MoU6a9F/ixsgq2pdaVijNoJys3ccPXhswZo8q/nTlCcQ4g1Xpw+OXbkXwWuAE5f66y\n\tdXjbh7tq1/JPXzXgbv517chYRNK0Vb/K+FCVjJ4uw4921zXxZp9xNxyKy4JGXD8eY+AoDqOoTOJ\n\tl+hMzL8RagubC/w+UqY7fIz19jqR9gT3q4zxAKIahcTA3QekinlpP6ovbuFXSq8TCrDYM9hs1ks\n\tP+7ANKfO1oDo8tNfzpLYvVjsINWHHr52cDigQ4qRxI3vGN4giY66vwKf20wvsGKg+442fd9P/+f\n\tLHuvoq1Uynpmx+OMRttoBwzBSJoBcURbwV3hqZI+EwvFyTUasBQknnHiOpK6wslkpRN/zCGmgKy\n\tERte+Kujuhn8NOg==", "X-Developer-Key": "i=jacopo.mondi@ideasonboard.com; a=openpgp;\n\tfpr=72392EDC88144A65C701EA9BA5826A2587AD026B", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "The AGC algorithm implemented by AgcMeanLuminance splits the desired\nexposure between shutter time, analogue and digital gains. It tries to\nmaximize the shutter time up to the specified limits and then increases\nthe gains.\n\nWhen initializing the algorithm at configure() time the IPA modules\nhave access to the Camera's absolute limits, which might include\na very long maximum frame duration. If such duration is used to clamp\nthe maximum exposure, the algorithm will maximize the shutter time,\nresulting in a very low frame rate.\n\nTry to pick a slighlty saner default of 30, 15 or 10 fps to limit the\nmaximum shutter time with to provide a reasonable out-of-the-box\nbehaviour for users. The new frame duration limit calculated by the AGC\nalgorithm is passed back to the IPA module that shall use it to compute\nthe right blankings to configure the camera with.\n\nAt the moment, only the RkISP1 IPA actually controls the frame duration,\nand so it is the only IPA that needs to use the frame duration limit\ncomputed by the AGC algorithm.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/ipu3/algorithms/agc.cpp | 4 +++-\n src/ipa/libipa/agc_mean_luminance.cpp | 20 ++++++++++++++------\n src/ipa/libipa/agc_mean_luminance.h | 3 ++-\n src/ipa/libipa/exposure_mode_helper.cpp | 21 +++++++++++++++++----\n src/ipa/libipa/exposure_mode_helper.h | 7 ++++---\n src/ipa/mali-c55/algorithms/agc.cpp | 4 +++-\n src/ipa/rkisp1/algorithms/agc.cpp | 13 +++++++++----\n 7 files changed, 52 insertions(+), 20 deletions(-)", "diff": "diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp\nindex 3a9cde22323719d73ac125179dbfcd5158b352ea..51aa4e51841ca8c6363c0211819a7d14cc3baec6 100644\n--- a/src/ipa/ipu3/algorithms/agc.cpp\n+++ b/src/ipa/ipu3/algorithms/agc.cpp\n@@ -120,13 +120,15 @@ int Agc::configure(IPAContext &context,\n \tAgcMeanLuminance::AgcSensorConfiguration sensorConfig;\n \tsensorConfig.lineDuration = context.configuration.sensor.lineDuration;\n \tsensorConfig.minExposureTime = minExposureTime_;\n+\tsensorConfig.minFrameDuration =\n+\t\tstd::chrono::microseconds(frameDurationLimits.min().get<int64_t>());\n \tsensorConfig.maxFrameDuration =\n \t\tstd::chrono::microseconds(frameDurationLimits.max().get<int64_t>());\n \tsensorConfig.maxExposureTime = maxExposureTime_;\n \tsensorConfig.minAnalogueGain = minAnalogueGain_;\n \tsensorConfig.maxAnalogueGain = maxAnalogueGain_;\n \n-\tAgcMeanLuminance::configure(sensorConfig, context.camHelper.get());\n+\tAgcMeanLuminance::configure(&sensorConfig, context.camHelper.get());\n \n \t/* \\todo Update AGC limits when FrameDurationLimits is passed in */\n \ndiff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp\nindex 1098865519ca9ca2679331edd9441720366c717d..36bdf76dca45c837d5ef4eb03244a43d1262c675 100644\n--- a/src/ipa/libipa/agc_mean_luminance.cpp\n+++ b/src/ipa/libipa/agc_mean_luminance.cpp\n@@ -121,6 +121,11 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95;\n * \\brief The sensor maximum exposure time in microseconds\n */\n \n+/**\n+ * \\var AgcMeanLuminance::AgcSensorConfiguration::minFrameDuration\n+ * \\brief The sensor minimum exposure time in microseconds\n+ */\n+\n /**\n * \\var AgcMeanLuminance::AgcSensorConfiguration::maxFrameDuration\n * \\brief The sensor maximum frame duration in microseconds\n@@ -355,21 +360,24 @@ int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData)\n \n /**\n * \\brief Configure the exposure mode helpers\n- * \\param[in] config The sensor configuration\n+ * \\param[inout] config The sensor configuration\n * \\param[in] sensorHelper The sensor helper\n *\n * This function configures the exposure mode helpers by providing them the\n * sensor configuration parameters and the sensor helper, so they can correctly\n * take quantization effects into account.\n+ *\n+ * The maximum frame duration passed in as a member of \\a config is updated to\n+ * the AGC algorithm startup value.\n */\n-void AgcMeanLuminance::configure(const AgcSensorConfiguration &config,\n+void AgcMeanLuminance::configure(AgcSensorConfiguration *config,\n \t\t\t\t const CameraSensorHelper *sensorHelper)\n {\n \tfor (auto &[id, helper] : exposureModeHelpers_)\n-\t\thelper->configure(config.lineDuration,\n-\t\t\t\t config.minExposureTime, config.maxExposureTime,\n-\t\t\t\t config.maxFrameDuration,\n-\t\t\t\t config.minAnalogueGain, config.maxAnalogueGain,\n+\t\thelper->configure(config->lineDuration,\n+\t\t\t\t config->minExposureTime, config->maxExposureTime,\n+\t\t\t\t config->minFrameDuration, &config->maxFrameDuration,\n+\t\t\t\t config->minAnalogueGain, config->maxAnalogueGain,\n \t\t\t\t sensorHelper);\n }\n \ndiff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h\nindex 0f7a4f53188d8d2759076c37d186efc088ddd279..b00772760035a477152d638809ac71fcbc66cb68 100644\n--- a/src/ipa/libipa/agc_mean_luminance.h\n+++ b/src/ipa/libipa/agc_mean_luminance.h\n@@ -46,12 +46,13 @@ public:\n \t\tutils::Duration lineDuration;\n \t\tutils::Duration minExposureTime;\n \t\tutils::Duration maxExposureTime;\n+\t\tutils::Duration minFrameDuration;\n \t\tutils::Duration maxFrameDuration;\n \t\tdouble minAnalogueGain;\n \t\tdouble maxAnalogueGain;\n \t};\n \n-\tvoid configure(const AgcSensorConfiguration &config,\n+\tvoid configure(AgcSensorConfiguration *config,\n \t\t const CameraSensorHelper *sensorHelper);\n \tint parseTuningData(const YamlObject &tuningData);\n \ndiff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp\nindex d1d94cf46d5ef2c077508fd94dfe9715fdbc6f03..46ba535c67d92bd384095b250b925cdc0da40b38 100644\n--- a/src/ipa/libipa/exposure_mode_helper.cpp\n+++ b/src/ipa/libipa/exposure_mode_helper.cpp\n@@ -111,7 +111,8 @@ void ExposureModeHelper::setMaxExposure(utils::Duration minExposureTime,\n * \\param[in] lineDuration The current line length of the sensor\n * \\param[in] minExposureTime The minimum exposure time supported\n * \\param[in] maxExposureTime The maximum exposure time supported\n- * \\param[in] maxFrameDuration The maximum frame duration\n+ * \\param[in] minFrameDuration The minimum frame duration\n+ * \\param[inout] maxFrameDuration The maximum frame duration\n * \\param[in] minGain The minimum analogue gain supported\n * \\param[in] maxGain The maximum analogue gain supported\n * \\param[in] sensorHelper The sensor helper\n@@ -128,17 +129,29 @@ void ExposureModeHelper::setMaxExposure(utils::Duration minExposureTime,\n void ExposureModeHelper::configure(utils::Duration lineDuration,\n \t\t\t\t utils::Duration minExposureTime,\n \t\t\t\t utils::Duration maxExposureTime,\n-\t\t\t\t utils::Duration maxFrameDuration,\n+\t\t\t\t utils::Duration minFrameDuration,\n+\t\t\t\t utils::Duration *maxFrameDuration,\n \t\t\t\t double minGain, double maxGain,\n \t\t\t\t const CameraSensorHelper *sensorHelper)\n {\n \tlineDuration_ = lineDuration;\n-\tmaxFrameDuration_ = maxFrameDuration;\n \tminGain_ = minGain;\n \tmaxGain_ = maxGain;\n \tsensorHelper_ = sensorHelper;\n \n-\tsetMaxExposure(minExposureTime, maxExposureTime, maxFrameDuration);\n+\tstatic constexpr utils::Duration duration30fps = 33.333 * 1ms;\n+\tstatic constexpr utils::Duration duration15fps = 66.666 * 1ms;\n+\tstatic constexpr utils::Duration duration10fps = 100.00 * 1ms;\n+\tutils::Duration frameDuration = minFrameDuration < duration30fps\n+\t\t\t\t ? duration30fps\n+\t\t\t\t : minFrameDuration < duration15fps\n+\t\t\t\t\t? duration15fps\n+\t\t\t\t\t : minFrameDuration < duration10fps\n+\t\t\t\t\t ? duration10fps : minFrameDuration;\n+\tframeDuration = std::min(frameDuration, *maxFrameDuration);\n+\tsetMaxExposure(minExposureTime, maxExposureTime, frameDuration);\n+\n+\tmaxFrameDuration_ = *maxFrameDuration = frameDuration;\n }\n \n /**\ndiff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h\nindex b1de96d4a3a13e3771ff2162f11201e5f9253265..492d2787f75945fb487b351a60f13756caec66e0 100644\n--- a/src/ipa/libipa/exposure_mode_helper.h\n+++ b/src/ipa/libipa/exposure_mode_helper.h\n@@ -27,11 +27,12 @@ public:\n \t~ExposureModeHelper() = default;\n \n \tvoid configure(utils::Duration lineLength, utils::Duration minExposureTime,\n-\t\t utils::Duration maxExposureTime, utils::Duration maxFrameDuration,\n-\t\t double minGain, double maxGain,\n+\t\t utils::Duration maxExposureTime, utils::Duration minFrameDuration,\n+\t\t utils::Duration *maxFrameDuration, double minGain, double maxGain,\n \t\t const CameraSensorHelper *sensorHelper);\n \tvoid setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime,\n-\t\t utils::Duration maxFrameDuration, double minGain, double maxGain);\n+\t\t utils::Duration maxFrameDuration,\n+\t\t double minGain, double maxGain);\n \n \tstd::tuple<utils::Duration, double, double, double>\n \tsplitExposure(utils::Duration exposure) const;\ndiff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp\nindex 5bfb4ab17ecb3814a10baec0f619267772ed8b3f..4e2fa4386be621c63d992b75d3d9efc525c3d69c 100644\n--- a/src/ipa/mali-c55/algorithms/agc.cpp\n+++ b/src/ipa/mali-c55/algorithms/agc.cpp\n@@ -175,12 +175,14 @@ int Agc::configure(IPAContext &context,\n \tsensorConfig.lineDuration = context.configuration.sensor.lineDuration;\n \tsensorConfig.minExposureTime = context.configuration.agc.minShutterSpeed;\n \tsensorConfig.maxExposureTime = context.configuration.agc.maxShutterSpeed;\n+\tsensorConfig.minFrameDuration =\n+\t\tstd::chrono::microseconds(frameDurationLimits.min().get<int64_t>());\n \tsensorConfig.maxFrameDuration =\n \t\tstd::chrono::microseconds(frameDurationLimits.max().get<int64_t>());\n \tsensorConfig.minAnalogueGain = context.configuration.agc.minAnalogueGain;\n \tsensorConfig.maxAnalogueGain = context.configuration.agc.maxAnalogueGain;\n \n-\tAgcMeanLuminance::configure(sensorConfig, context.camHelper.get());\n+\tAgcMeanLuminance::configure(&sensorConfig, context.camHelper.get());\n \n \t/* \\todo Update AGC limits when FrameDurationLimits is passed in */\n \ndiff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp\nindex 96189254885c47d29b92f3fb40f0e86d10e2a7fc..a983efbecbd84fa9e4af8b9642fbdda124ba0b9f 100644\n--- a/src/ipa/rkisp1/algorithms/agc.cpp\n+++ b/src/ipa/rkisp1/algorithms/agc.cpp\n@@ -190,10 +190,13 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)\n \tcontext.activeState.agc.meteringMode =\n \t\tstatic_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);\n \n-\t/* Limit the frame duration to match current initialisation */\n+\t/*\n+\t * Initialize frame duration with the camera limits and update it to\n+\t * the value computed by AgcMeanLuminance::configure().\n+\t */\n \tControlInfo &frameDurationLimits = context.ctrlMap[&controls::FrameDurationLimits];\n \tcontext.activeState.agc.minFrameDuration = std::chrono::microseconds(frameDurationLimits.min().get<int64_t>());\n-\tcontext.activeState.agc.maxFrameDuration = std::chrono::microseconds(frameDurationLimits.max().get<int64_t>());\n+\tutils::Duration maxFrameDuration = std::chrono::microseconds(frameDurationLimits.max().get<int64_t>());\n \n \tcontext.configuration.agc.measureWindow.h_offs = 0;\n \tcontext.configuration.agc.measureWindow.v_offs = 0;\n@@ -204,12 +207,14 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)\n \tsensorConfig.lineDuration = context.configuration.sensor.lineDuration;\n \tsensorConfig.minExposureTime = context.configuration.sensor.minExposureTime;\n \tsensorConfig.maxExposureTime = context.configuration.sensor.maxExposureTime;\n-\tsensorConfig.maxFrameDuration = context.activeState.agc.maxFrameDuration;\n+\tsensorConfig.minFrameDuration = context.activeState.agc.minFrameDuration;\n+\tsensorConfig.maxFrameDuration = maxFrameDuration;\n \tsensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain;\n \tsensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain;\n \n-\tAgcMeanLuminance::configure(sensorConfig, context.camHelper.get());\n+\tAgcMeanLuminance::configure(&sensorConfig, context.camHelper.get());\n \n+\tcontext.activeState.agc.maxFrameDuration = sensorConfig.maxFrameDuration;\n \tcontext.activeState.agc.automatic.yTarget = effectiveYTarget();\n \n \tresetFrameCount();\n", "prefixes": [ "v2", "08/10" ] }