{"id":27464,"url":"https://patchwork.libcamera.org/api/patches/27464/?format=json","web_url":"https://patchwork.libcamera.org/patch/27464/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260723154327.1357866-15-barnabas.pocze@ideasonboard.com>","date":"2026-07-23T15:42:57","name":"[RFC,v2,14/43] ipa: libipa: agc_mean_luminance: calculateNewEv(): Collect params","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"ae956217221cbb0076c6aa5e38aa7239feace6d3","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/?format=json","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/27464/mbox/","series":[{"id":6075,"url":"https://patchwork.libcamera.org/api/series/6075/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=6075","date":"2026-07-23T15:42:43","name":"ipa: libipa: agc rework","version":2,"mbox":"https://patchwork.libcamera.org/series/6075/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/27464/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27464/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 8F16FC3301\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 23 Jul 2026 15:43:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 16F2267F20;\n\tThu, 23 Jul 2026 17:43:54 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 012FA67EC7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 23 Jul 2026 17:43:33 +0200 (CEST)","from pb-laptop.local (185.182.215.156.nat.pool.zt.hu\n\t[185.182.215.156])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 221CA1C37\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 23 Jul 2026 17:42:33 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"JgtLzCLC\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1784821353;\n\tbh=0guaNOTTnXwSaZhS+tOKH3iYPgTi94jBtetlKj0Ptcw=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=JgtLzCLCt8geo4TIbtBYLFQFumZcSjFCjinIzP9SDdxuY1ZFWYn0dm/30tw3MvABS\n\tLbN28ei/gaNdrYdyKjxge7RViT+CBdlNBjYMoxisaFwjKBTD6tgAGxAsbn+7RqnmzC\n\tMSbB483K6AED4TDIf+hGPSZumW0RKZMM5MuTCCg0=","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Subject":"[RFC PATCH v2 14/43] ipa: libipa: agc_mean_luminance:\n\tcalculateNewEv(): Collect params","Date":"Thu, 23 Jul 2026 17:42:57 +0200","Message-ID":"<20260723154327.1357866-15-barnabas.pocze@ideasonboard.com>","X-Mailer":"git-send-email 2.55.0","In-Reply-To":"<20260723154327.1357866-1-barnabas.pocze@ideasonboard.com>","References":"<20260723154327.1357866-1-barnabas.pocze@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","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":"Add a new type that contains all parameters for the `calculateNewEv()`\nfunction.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/ipa/ipu3/algorithms/agc.cpp       | 28 +++++++++--------\n src/ipa/libipa/agc_mean_luminance.cpp | 44 ++++++++++++++++-----------\n src/ipa/libipa/agc_mean_luminance.h   | 12 ++++++--\n src/ipa/mali-c55/algorithms/agc.cpp   | 12 +++++---\n src/ipa/rkisp1/algorithms/agc.cpp     | 18 ++++++-----\n 5 files changed, 68 insertions(+), 46 deletions(-)","diff":"diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp\nindex a919418d69..ac5b6e7513 100644\n--- a/src/ipa/ipu3/algorithms/agc.cpp\n+++ b/src/ipa/ipu3/algorithms/agc.cpp\n@@ -236,22 +236,24 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \t\t\t\t     * frameContext.sensor.exposure;\n \tdouble analogueGain = frameContext.sensor.gain;\n \tutils::Duration effectiveExposureValue = exposureTime * analogueGain;\n-\tAgcTraits agcTraits{\n-\t\trgbTriples_,\n-\t\t{{\n-\t\t\tcontext.activeState.awb.gains.red,\n-\t\t\tcontext.activeState.awb.gains.blue,\n-\t\t\tcontext.activeState.awb.gains.green,\n-\t\t}},\n-\t\tbdsGrid_,\n-\t};\n \n \tutils::Duration newExposureTime;\n \tdouble aGain, qGain, dGain;\n-\tstd::tie(newExposureTime, aGain, qGain, dGain) =\n-\t\tagc_.calculateNewEv(context.activeState.agc.constraintMode,\n-\t\t\t\t    context.activeState.agc.exposureMode, hist,\n-\t\t\t\t    effectiveExposureValue, agcTraits);\n+\tstd::tie(newExposureTime, aGain, qGain, dGain) = agc_.calculateNewEv({\n+\t\t.traits = AgcTraits{\n+\t\t\trgbTriples_,\n+\t\t\t{{\n+\t\t\t\tcontext.activeState.awb.gains.red,\n+\t\t\t\tcontext.activeState.awb.gains.blue,\n+\t\t\t\tcontext.activeState.awb.gains.green,\n+\t\t\t}},\n+\t\t\tbdsGrid_,\n+\t\t},\n+\t\t.yHist = hist,\n+\t\t.effectiveExposureValue = effectiveExposureValue,\n+\t\t.constraintModeIndex = context.activeState.agc.constraintMode,\n+\t\t.exposureModeIndex = context.activeState.agc.exposureMode,\n+\t});\n \n \tLOG(IPU3Agc, Debug)\n \t\t<< \"Divided up exposure time, analogue gain and digital gain are \"\ndiff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp\nindex 31636159a2..083d67eb70 100644\n--- a/src/ipa/libipa/agc_mean_luminance.cpp\n+++ b/src/ipa/libipa/agc_mean_luminance.cpp\n@@ -648,16 +648,30 @@ utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue)\n \treturn filteredExposure_;\n }\n \n+/**\n+ * \\struct AgcMeanLuminance::Params\n+ * \\brief Collection of parameters for the mean luminance AGC algorithm\n+ *\n+ * \\var AgcMeanLuminance::Params::traits\n+ * \\brief The traits object implementing the necessary functions\n+ *\n+ * \\var AgcMeanLuminance::Params::yHist\n+ * \\brief A histogram from the ISP statistics to use in constraining the calculated gain\n+ *\n+ * \\var AgcMeanLuminance::Params::effectiveExposureValue\n+ * \\brief The EV applied to the frame from which the statistics in use derive\n+ *\n+ * \\var AgcMeanLuminance::Params::constraintModeIndex\n+ * \\brief The index of the constraint mode to use\n+ *\n+ * \\var AgcMeanLuminance::Params::exposureModeIndex\n+ * \\brief The index of the exposure mode to use\n+ */\n+\n /**\n  * \\brief Calculate the new exposure value and split it between exposure time\n  * and gain\n- * \\param[in] constraintModeIndex The index of the current constraint mode\n- * \\param[in] exposureModeIndex The index of the current exposure mode\n- * \\param[in] yHist A Histogram from the ISP statistics to use in constraining\n- * the calculated gain\n- * \\param[in] effectiveExposureValue The EV applied to the frame from which the\n- * statistics in use derive\n- * \\param[in] traits The traits object implementing the necessary functions\n+ * \\param[in] params The set of parameters for the calculation\n  *\n  * Calculate a new exposure value to try to obtain the target. The calculated\n  * exposure value is filtered to prevent rapid changes from frame to frame, and\n@@ -667,20 +681,16 @@ utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue)\n  * gain\n  */\n std::tuple<utils::Duration, double, double, double>\n-AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex,\n-\t\t\t\t uint32_t exposureModeIndex,\n-\t\t\t\t const Histogram &yHist,\n-\t\t\t\t utils::Duration effectiveExposureValue,\n-\t\t\t\t const Traits &traits)\n+AgcMeanLuminance::calculateNewEv(const Params &params)\n {\n \t/*\n \t * The pipeline handler should validate that we have received an allowed\n \t * value for AeExposureMode.\n \t */\n \tExposureModeHelper &exposureModeHelper =\n-\t\texposureModeHelpers_.at(exposureModeIndex);\n+\t\texposureModeHelpers_.at(params.exposureModeIndex);\n \n-\tif (effectiveExposureValue == 0s) {\n+\tif (params.effectiveExposureValue == 0s) {\n \t\tLOG(AgcMeanLuminance, Error)\n \t\t\t<< \"Effective exposure value is 0. This is a bug in AGC \"\n \t\t\t   \"and must be fixed for proper operation.\";\n@@ -692,8 +702,8 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex,\n \t\treturn exposureModeHelper.splitExposure(10ms);\n \t}\n \n-\tdouble gain = estimateInitialGain(traits);\n-\tgain = constraintClampGain(constraintModeIndex, yHist, gain);\n+\tdouble gain = estimateInitialGain(params.traits);\n+\tgain = constraintClampGain(params.constraintModeIndex, params.yHist, gain);\n \n \t/*\n \t * We don't check whether we're already close to the target, because\n@@ -702,7 +712,7 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex,\n \t * pass through the splitExposure() function.\n \t */\n \n-\tutils::Duration newExposureValue = effectiveExposureValue * gain;\n+\tutils::Duration newExposureValue = params.effectiveExposureValue * gain;\n \n \t/*\n \t * We filter the exposure value to make sure changes are not too jarring\ndiff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h\nindex d5425cd20b..eb9d6fec2b 100644\n--- a/src/ipa/libipa/agc_mean_luminance.h\n+++ b/src/ipa/libipa/agc_mean_luminance.h\n@@ -79,10 +79,16 @@ public:\n \t\treturn controls_;\n \t}\n \n+\tstruct Params {\n+\t\tconst Traits &traits;\n+\t\tconst Histogram &yHist;\n+\t\tutils::Duration effectiveExposureValue;\n+\t\tuint32_t constraintModeIndex;\n+\t\tuint32_t exposureModeIndex;\n+\t};\n+\n \tstd::tuple<utils::Duration, double, double, double>\n-\tcalculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex,\n-\t\t       const Histogram &yHist, utils::Duration effectiveExposureValue,\n-\t\t       const Traits &traits);\n+\tcalculateNewEv(const Params &params);\n \n \tdouble effectiveYTarget() const;\n \ndiff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp\nindex 29b7c66159..fb97b87c6e 100644\n--- a/src/ipa/mali-c55/algorithms/agc.cpp\n+++ b/src/ipa/mali-c55/algorithms/agc.cpp\n@@ -331,14 +331,16 @@ void Agc::process(IPAContext &context,\n \tdouble analogueGain = frameContext.agc.sensorGain;\n \tutils::Duration currentShutter = exposure * configuration.sensor.lineDuration;\n \tutils::Duration effectiveExposureValue = currentShutter * analogueGain;\n-\tAgcTraits agcTraits(statistics_);\n \n \tutils::Duration shutterTime;\n \tdouble aGain, qGain, dGain;\n-\tstd::tie(shutterTime, aGain, qGain, dGain) =\n-\t\tagc_.calculateNewEv(activeState.agc.constraintMode,\n-\t\t\t\t    activeState.agc.exposureMode, statistics_.yHist,\n-\t\t\t\t    effectiveExposureValue, agcTraits);\n+\tstd::tie(shutterTime, aGain, qGain, dGain) = agc_.calculateNewEv({\n+\t\t.traits = AgcTraits(statistics_),\n+\t\t.yHist = statistics_.yHist,\n+\t\t.effectiveExposureValue = effectiveExposureValue,\n+\t\t.constraintModeIndex = activeState.agc.constraintMode,\n+\t\t.exposureModeIndex = activeState.agc.exposureMode,\n+\t});\n \n \tLOG(MaliC55Agc, Debug)\n \t\t<< \"Divided up shutter, analogue gain and digital gain are \"\ndiff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp\nindex 129f5be2a5..8b4bcbd71b 100644\n--- a/src/ipa/rkisp1/algorithms/agc.cpp\n+++ b/src/ipa/rkisp1/algorithms/agc.cpp\n@@ -716,20 +716,22 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \t/* The lower 4 bits are fractional and meant to be discarded. */\n \tHistogram hist({ params->hist.hist_bins, context.hw.numHistogramBins },\n \t\t       [](uint32_t x) { return x >> 4; });\n-\tAgcTraits agcTraits{\n-\t\t{ params->ae.exp_mean, context.hw.numAeCells },\n-\t\tmeteringModes_.at(frameContext.agc.meteringMode),\n-\t};\n \n \tagc_.setExposureCompensation(pow(2.0, frameContext.agc.exposureValue));\n \tagc_.setLux(frameContext.lux.lux);\n \n \tutils::Duration newExposureTime;\n \tdouble aGain, qGain, dGain;\n-\tstd::tie(newExposureTime, aGain, qGain, dGain) =\n-\t\tagc_.calculateNewEv(frameContext.agc.constraintMode,\n-\t\t\t\t    frameContext.agc.exposureMode,\n-\t\t\t\t    hist, effectiveExposureValue, agcTraits);\n+\tstd::tie(newExposureTime, aGain, qGain, dGain) = agc_.calculateNewEv({\n+\t\t.traits = AgcTraits{\n+\t\t\t{ params->ae.exp_mean, context.hw.numAeCells },\n+\t\t\tmeteringModes_.at(frameContext.agc.meteringMode),\n+\t\t},\n+\t\t.yHist = hist,\n+\t\t.effectiveExposureValue = effectiveExposureValue,\n+\t\t.constraintModeIndex = frameContext.agc.constraintMode,\n+\t\t.exposureModeIndex = frameContext.agc.exposureMode,\n+\t});\n \n \tLOG(RkISP1Agc, Debug)\n \t\t<< \"Divided up exposure time, analogue gain, quantization gain\"\n","prefixes":["RFC","v2","14/43"]}