[{"id":38227,"web_url":"https://patchwork.libcamera.org/comment/38227/","msgid":"<177149122215.607498.8898483476138360551@neptunite.rasen.tech>","date":"2026-02-19T08:53:42","subject":"Re: [PATCH v7 04/15] ipa: libipa: Provide fixed point quantized\n\ttraits","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Quoting Kieran Bingham (2026-02-14 01:57:43)\n> Extend the new Quantized type infrastructure by providing a\n> FixedPointQTraits template.\n> \n> This allows construction of fixed point types with a Quantized storage\n> that allows easy reading of both the underlying quantized type value and\n> a floating point representation of that same value.\n> \n> Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>\n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n> \n> ---\n> v4:\n> - Assert that the given type has enough bits for the usage\n> - Use unsigned types for calculating qmin/qmax\n> - Reorder toFloat/fromFloat and min/max for future inlining\n> - Make toFloat and fromFloat constexpr\n> \n> v5:\n> - Make UT, Bits and Bitmask private (and remove doxygen)\n> - Remove constexpr from fromFloat which uses std::round (only constexpr\n>   in C++23)\n> - static_assert that min<max when converted\n> - Provide new Q and UQ automatic width types (Thanks Barnabás)\n> - Convert types to shortened Q/UQ automatic widths\n> - Use automatic width Q/UQ for 12,4\n> - change qmin->qMin qmax->qMax Bits->bits BitMask->bitMask\n> - Remove typedefs for Q1_7 etc\n> \n> v6:\n> - Use 'quantized' over 'quantised'\n> - Document sign is based on T and number of bits includes sign bit\n> \n> - Document that fromFloat also clamps between [min, max]\n> \n> - Remove 64 bit support. We have 32 bit assumptions on fromFloat\n> \n> - Restrict to 24 bits, to stay compatible with float types\n> \n> v7:\n> - Use unsigned storage and ensure we don't have sign extension issues.\n> ---\n>  src/ipa/libipa/fixedpoint.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++++++\n>  2 files changed, 172 insertions(+)\n> \n> diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n> index 6b698fc5d680..caa9ce0fc1ec 100644\n> --- a/src/ipa/libipa/fixedpoint.cpp\n> +++ b/src/ipa/libipa/fixedpoint.cpp\n> @@ -37,6 +37,104 @@ namespace ipa {\n>   * \\return The converted value\n>   */\n>  \n> +/**\n> + * \\struct libcamera::ipa::FixedPointQTraits\n> + * \\brief Traits type implementing fixed-point quantisation conversions\n> + *\n> + * The FixedPointQTraits structure defines a policy for mapping floating-point\n> + * values to and from fixed-point integer representations. It is parameterised\n> + * by the number of integer bits \\a I, fractional bits \\a F, and the integral\n> + * storage type \\a T. The traits are used with Quantized<Traits> to create a\n> + * quantized type that stores both the fixed-point representation and the\n> + * corresponding floating-point value.\n> + *\n> + * The signedness of the type is determined by the signedness of \\a T. For\n> + * signed types, the number of integer bits in \\a I includes the sign bit.\n> + *\n> + * Storage is determined by the total number of bits \\a (I + F) and is\n> + * automatically selected, but the internal storage type is always an unsigned\n> + * integer to guarantee against sign extension when storing quantized values\n> + * in registers.\n> + *\n> + * The trait exposes compile-time constants describing the bit layout, limits,\n> + * and scaling factors used in the fixed-point representation.\n> + *\n> + * \\tparam I Number of integer bits\n> + * \\tparam F Number of fractional bits\n> + * \\tparam T Integral type used to store the quantized value\n> + */\n> +\n> +/**\n> + * \\typedef FixedPointQTraits::QuantizedType\n> + * \\brief The integral storage type used for the fixed-point representation\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::qMin\n> + * \\brief Minimum representable quantized integer value\n> + *\n> + * This corresponds to the most negative value for signed formats or zero for\n> + * unsigned formats.\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::qMax\n> + * \\brief Maximum representable quantized integer value\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::min\n> + * \\brief Minimum representable floating-point value corresponding to qMin\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::max\n> + * \\brief Maximum representable floating-point value corresponding to qMax\n> + */\n> +\n> +/**\n> + * \\fn FixedPointQTraits::fromFloat(float v)\n> + * \\brief Convert a floating-point value to a fixed-point integer\n> + * \\param[in] v The floating-point value to be converted\n> + * \\return The quantized fixed-point integer representation\n> + *\n> + * The conversion first clamps the floating-point input \\a v to the range [min,\n> + * max] and then rounds it to the nearest integer according to the scaling\n> + * factor defined by the number of fractional bits F.\n> + */\n> +\n> +/**\n> + * \\fn FixedPointQTraits::toFloat(QuantizedType q)\n> + * \\brief Convert a fixed-point integer to a floating-point value\n> + * \\param[in] q The fixed-point integer value to be converted\n> + * \\return The corresponding floating-point value\n> + *\n> + * The conversion sign-extends the integer value if required and divides by the\n> + * scaling factor defined by the number of fractional bits F.\n> + */\n> +\n> +/**\n> + * \\typedef Q\n> + * \\brief Define a signed fixed-point quantized type with automatic storage width\n> + * \\tparam I The number of integer bits\n> + * \\tparam F The number of fractional bits\n> + *\n> + * This alias defines a signed fixed-point quantized type using the\n> + * \\ref FixedPointQTraits trait and a suitable signed integer storage type\n> + * automatically selected based on the total number of bits \\a (I + F).\n> + */\n> +\n> +/**\n> + * \\typedef UQ\n> + * \\brief Define an unsigned fixed-point quantized type with automatic storage width\n> + * \\tparam I The number of integer bits\n> + * \\tparam F The number of fractional bits\n> + *\n> + * This alias defines an unsigned fixed-point quantized type using the\n> + * \\ref FixedPointQTraits trait and a suitable unsigned integer storage type\n> + * automatically selected based on the total number of bits \\a (I + F).\n> + */\n> +\n>  } /* namespace ipa */\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h\n> index aeb9bce3269b..b6b611df7fc3 100644\n> --- a/src/ipa/libipa/fixedpoint.h\n> +++ b/src/ipa/libipa/fixedpoint.h\n> @@ -10,6 +10,8 @@\n>  #include <cmath>\n>  #include <type_traits>\n>  \n> +#include \"quantized.h\"\n> +\n>  namespace libcamera {\n>  \n>  namespace ipa {\n> @@ -63,6 +65,78 @@ constexpr R fixedToFloatingPoint(T number)\n>         return static_cast<R>(t) / static_cast<R>(1 << F);\n>  }\n>  \n> +template<unsigned int I, unsigned int F, typename T>\n> +struct FixedPointQTraits {\n> +private:\n> +       static_assert(std::is_integral_v<T>, \"FixedPointQTraits: T must be integral\");\n> +       using UT = std::make_unsigned_t<T>;\n> +\n> +       static constexpr unsigned int bits = I + F;\n> +       static_assert(bits <= sizeof(UT) * 8, \"FixedPointQTraits: too many bits for type UT\");\n> +\n> +       /*\n> +        * If fixed point storage is required with more than 24 bits, consider\n> +        * updating this implementation to use double-precision floating point.\n> +        */\n> +       static_assert(bits <= 24, \"Floating point precision may be insufficient for more than 24 bits\");\n> +\n> +       static constexpr UT bitMask = bits < sizeof(UT) * 8\n> +                                             ? (UT{ 1 } << bits) - 1\n> +                                             : ~UT{ 0 };\n> +\n> +public:\n> +       using QuantizedType = UT;\n> +\n> +       static constexpr UT qMin = std::is_signed_v<T>\n> +                                          ? -(UT{ 1 } << (bits - 1))\n> +                                          : 0;\n> +\n> +       static constexpr UT qMax = std::is_signed_v<T>\n> +                                          ? (UT{ 1 } << (bits - 1)) - 1\n> +                                          : bitMask;\n> +\n> +       static constexpr float toFloat(QuantizedType q)\n> +       {\n> +               return fixedToFloatingPoint<I, F, float, T>(q);\n> +       }\n> +\n> +       static constexpr float min = fixedToFloatingPoint<I, F, float>(static_cast<T>(qMin));\n> +       static constexpr float max = fixedToFloatingPoint<I, F, float>(static_cast<T>(qMax));\n> +\n> +       static_assert(min < max, \"FixedPointQTraits: Minimum must be less than maximum\");\n> +\n> +       /* Conversion functions required by Quantized<Traits> */\n> +       static QuantizedType fromFloat(float v)\n> +       {\n> +               v = std::clamp(v, min, max);\n> +               return floatingToFixedPoint<I, F, T, float>(v);\n> +       }\n> +};\n> +\n> +namespace details {\n> +\n> +template<unsigned int Bits>\n> +constexpr auto qtype()\n> +{\n> +       static_assert(Bits <= 32,\n> +                     \"Unsupported number of bits for quantized type\");\n> +\n> +       if constexpr (Bits <= 8)\n> +               return int8_t();\n> +       else if constexpr (Bits <= 16)\n> +               return int16_t();\n> +       else if constexpr (Bits <= 32)\n> +               return int32_t();\n> +}\n> +\n> +} /* namespace details */\n> +\n> +template<unsigned int I, unsigned int F>\n> +using Q = Quantized<FixedPointQTraits<I, F, decltype(details::qtype<I + F>())>>;\n> +\n> +template<unsigned int I, unsigned int F>\n> +using UQ = Quantized<FixedPointQTraits<I, F, std::make_unsigned_t<decltype(details::qtype<I + F>())>>>;\n> +\n>  } /* namespace ipa */\n>  \n>  } /* namespace libcamera */\n> \n> -- \n> 2.52.0\n>","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 6C738C31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Feb 2026 08:53:49 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1F9F762230;\n\tThu, 19 Feb 2026 09:53:49 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0DA0E62010\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 09:53:48 +0100 (CET)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:3150:3f17:6415:4c60])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id B1531379;\n\tThu, 19 Feb 2026 09:52:54 +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=\"CbMfUfC/\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771491175;\n\tbh=yB4TF3vZqFMrYKSybdSuyvrI9YVxBJiVjNFYtnbd9FQ=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=CbMfUfC/73+XYmd00QcOZqSe0fTJlYx0iehe6IbERkCZAxhKpL1aALtK2riyx1svi\n\tiRPkjtZo02NYNT8Rmx3l57dU6hvA9pmF9YuQdEfN4YWdOvYQTQqCnRVdI8OPzLZLMg\n\tN4oCWWR6s4eaF0e9Mvs7dPAsrGzbW7zIDCB+Uh2k=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260213-kbingham-quantizers-v7-4-1626b9aaabf1@ideasonboard.com>","References":"<20260213-kbingham-quantizers-v7-0-1626b9aaabf1@ideasonboard.com>\n\t<20260213-kbingham-quantizers-v7-4-1626b9aaabf1@ideasonboard.com>","Subject":"Re: [PATCH v7 04/15] ipa: libipa: Provide fixed point quantized\n\ttraits","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tIsaac Scott <isaac.scott@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Thu, 19 Feb 2026 17:53:42 +0900","Message-ID":"<177149122215.607498.8898483476138360551@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","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>"}},{"id":38237,"web_url":"https://patchwork.libcamera.org/comment/38237/","msgid":"<177149654502.3673516.5981622601505624822@localhost>","date":"2026-02-19T10:22:25","subject":"Re: [PATCH v7 04/15] ipa: libipa: Provide fixed point quantized\n\ttraits","submitter":{"id":184,"url":"https://patchwork.libcamera.org/api/people/184/","name":"Stefan Klug","email":"stefan.klug@ideasonboard.com"},"content":"Quoting Kieran Bingham (2026-02-13 17:57:43)\n> Extend the new Quantized type infrastructure by providing a\n> FixedPointQTraits template.\n> \n> This allows construction of fixed point types with a Quantized storage\n> that allows easy reading of both the underlying quantized type value and\n> a floating point representation of that same value.\n> \n> Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>\n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nNo with the internals set to unsigned there is even more reason to\nReviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n\nCheers,\nStefan\n\n> \n> ---\n> v4:\n> - Assert that the given type has enough bits for the usage\n> - Use unsigned types for calculating qmin/qmax\n> - Reorder toFloat/fromFloat and min/max for future inlining\n> - Make toFloat and fromFloat constexpr\n> \n> v5:\n> - Make UT, Bits and Bitmask private (and remove doxygen)\n> - Remove constexpr from fromFloat which uses std::round (only constexpr\n>   in C++23)\n> - static_assert that min<max when converted\n> - Provide new Q and UQ automatic width types (Thanks Barnabás)\n> - Convert types to shortened Q/UQ automatic widths\n> - Use automatic width Q/UQ for 12,4\n> - change qmin->qMin qmax->qMax Bits->bits BitMask->bitMask\n> - Remove typedefs for Q1_7 etc\n> \n> v6:\n> - Use 'quantized' over 'quantised'\n> - Document sign is based on T and number of bits includes sign bit\n> \n> - Document that fromFloat also clamps between [min, max]\n> \n> - Remove 64 bit support. We have 32 bit assumptions on fromFloat\n> \n> - Restrict to 24 bits, to stay compatible with float types\n> \n> v7:\n> - Use unsigned storage and ensure we don't have sign extension issues.\n> ---\n>  src/ipa/libipa/fixedpoint.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++++++\n>  2 files changed, 172 insertions(+)\n> \n> diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n> index 6b698fc5d680..caa9ce0fc1ec 100644\n> --- a/src/ipa/libipa/fixedpoint.cpp\n> +++ b/src/ipa/libipa/fixedpoint.cpp\n> @@ -37,6 +37,104 @@ namespace ipa {\n>   * \\return The converted value\n>   */\n>  \n> +/**\n> + * \\struct libcamera::ipa::FixedPointQTraits\n> + * \\brief Traits type implementing fixed-point quantisation conversions\n> + *\n> + * The FixedPointQTraits structure defines a policy for mapping floating-point\n> + * values to and from fixed-point integer representations. It is parameterised\n> + * by the number of integer bits \\a I, fractional bits \\a F, and the integral\n> + * storage type \\a T. The traits are used with Quantized<Traits> to create a\n> + * quantized type that stores both the fixed-point representation and the\n> + * corresponding floating-point value.\n> + *\n> + * The signedness of the type is determined by the signedness of \\a T. For\n> + * signed types, the number of integer bits in \\a I includes the sign bit.\n> + *\n> + * Storage is determined by the total number of bits \\a (I + F) and is\n> + * automatically selected, but the internal storage type is always an unsigned\n> + * integer to guarantee against sign extension when storing quantized values\n> + * in registers.\n> + *\n> + * The trait exposes compile-time constants describing the bit layout, limits,\n> + * and scaling factors used in the fixed-point representation.\n> + *\n> + * \\tparam I Number of integer bits\n> + * \\tparam F Number of fractional bits\n> + * \\tparam T Integral type used to store the quantized value\n> + */\n> +\n> +/**\n> + * \\typedef FixedPointQTraits::QuantizedType\n> + * \\brief The integral storage type used for the fixed-point representation\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::qMin\n> + * \\brief Minimum representable quantized integer value\n> + *\n> + * This corresponds to the most negative value for signed formats or zero for\n> + * unsigned formats.\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::qMax\n> + * \\brief Maximum representable quantized integer value\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::min\n> + * \\brief Minimum representable floating-point value corresponding to qMin\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::max\n> + * \\brief Maximum representable floating-point value corresponding to qMax\n> + */\n> +\n> +/**\n> + * \\fn FixedPointQTraits::fromFloat(float v)\n> + * \\brief Convert a floating-point value to a fixed-point integer\n> + * \\param[in] v The floating-point value to be converted\n> + * \\return The quantized fixed-point integer representation\n> + *\n> + * The conversion first clamps the floating-point input \\a v to the range [min,\n> + * max] and then rounds it to the nearest integer according to the scaling\n> + * factor defined by the number of fractional bits F.\n> + */\n> +\n> +/**\n> + * \\fn FixedPointQTraits::toFloat(QuantizedType q)\n> + * \\brief Convert a fixed-point integer to a floating-point value\n> + * \\param[in] q The fixed-point integer value to be converted\n> + * \\return The corresponding floating-point value\n> + *\n> + * The conversion sign-extends the integer value if required and divides by the\n> + * scaling factor defined by the number of fractional bits F.\n> + */\n> +\n> +/**\n> + * \\typedef Q\n> + * \\brief Define a signed fixed-point quantized type with automatic storage width\n> + * \\tparam I The number of integer bits\n> + * \\tparam F The number of fractional bits\n> + *\n> + * This alias defines a signed fixed-point quantized type using the\n> + * \\ref FixedPointQTraits trait and a suitable signed integer storage type\n> + * automatically selected based on the total number of bits \\a (I + F).\n> + */\n> +\n> +/**\n> + * \\typedef UQ\n> + * \\brief Define an unsigned fixed-point quantized type with automatic storage width\n> + * \\tparam I The number of integer bits\n> + * \\tparam F The number of fractional bits\n> + *\n> + * This alias defines an unsigned fixed-point quantized type using the\n> + * \\ref FixedPointQTraits trait and a suitable unsigned integer storage type\n> + * automatically selected based on the total number of bits \\a (I + F).\n> + */\n> +\n>  } /* namespace ipa */\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h\n> index aeb9bce3269b..b6b611df7fc3 100644\n> --- a/src/ipa/libipa/fixedpoint.h\n> +++ b/src/ipa/libipa/fixedpoint.h\n> @@ -10,6 +10,8 @@\n>  #include <cmath>\n>  #include <type_traits>\n>  \n> +#include \"quantized.h\"\n> +\n>  namespace libcamera {\n>  \n>  namespace ipa {\n> @@ -63,6 +65,78 @@ constexpr R fixedToFloatingPoint(T number)\n>         return static_cast<R>(t) / static_cast<R>(1 << F);\n>  }\n>  \n> +template<unsigned int I, unsigned int F, typename T>\n> +struct FixedPointQTraits {\n> +private:\n> +       static_assert(std::is_integral_v<T>, \"FixedPointQTraits: T must be integral\");\n> +       using UT = std::make_unsigned_t<T>;\n> +\n> +       static constexpr unsigned int bits = I + F;\n> +       static_assert(bits <= sizeof(UT) * 8, \"FixedPointQTraits: too many bits for type UT\");\n> +\n> +       /*\n> +        * If fixed point storage is required with more than 24 bits, consider\n> +        * updating this implementation to use double-precision floating point.\n> +        */\n> +       static_assert(bits <= 24, \"Floating point precision may be insufficient for more than 24 bits\");\n> +\n> +       static constexpr UT bitMask = bits < sizeof(UT) * 8\n> +                                             ? (UT{ 1 } << bits) - 1\n> +                                             : ~UT{ 0 };\n> +\n> +public:\n> +       using QuantizedType = UT;\n> +\n> +       static constexpr UT qMin = std::is_signed_v<T>\n> +                                          ? -(UT{ 1 } << (bits - 1))\n> +                                          : 0;\n> +\n> +       static constexpr UT qMax = std::is_signed_v<T>\n> +                                          ? (UT{ 1 } << (bits - 1)) - 1\n> +                                          : bitMask;\n> +\n> +       static constexpr float toFloat(QuantizedType q)\n> +       {\n> +               return fixedToFloatingPoint<I, F, float, T>(q);\n> +       }\n> +\n> +       static constexpr float min = fixedToFloatingPoint<I, F, float>(static_cast<T>(qMin));\n> +       static constexpr float max = fixedToFloatingPoint<I, F, float>(static_cast<T>(qMax));\n> +\n> +       static_assert(min < max, \"FixedPointQTraits: Minimum must be less than maximum\");\n> +\n> +       /* Conversion functions required by Quantized<Traits> */\n> +       static QuantizedType fromFloat(float v)\n> +       {\n> +               v = std::clamp(v, min, max);\n> +               return floatingToFixedPoint<I, F, T, float>(v);\n> +       }\n> +};\n> +\n> +namespace details {\n> +\n> +template<unsigned int Bits>\n> +constexpr auto qtype()\n> +{\n> +       static_assert(Bits <= 32,\n> +                     \"Unsupported number of bits for quantized type\");\n> +\n> +       if constexpr (Bits <= 8)\n> +               return int8_t();\n> +       else if constexpr (Bits <= 16)\n> +               return int16_t();\n> +       else if constexpr (Bits <= 32)\n> +               return int32_t();\n> +}\n> +\n> +} /* namespace details */\n> +\n> +template<unsigned int I, unsigned int F>\n> +using Q = Quantized<FixedPointQTraits<I, F, decltype(details::qtype<I + F>())>>;\n> +\n> +template<unsigned int I, unsigned int F>\n> +using UQ = Quantized<FixedPointQTraits<I, F, std::make_unsigned_t<decltype(details::qtype<I + F>())>>>;\n> +\n>  } /* namespace ipa */\n>  \n>  } /* namespace libcamera */\n> \n> -- \n> 2.52.0\n>","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 22FE6C0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Feb 2026 10:22:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 045C362238;\n\tThu, 19 Feb 2026 11:22:30 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4774062010\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 11:22:28 +0100 (CET)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:6ed3:b29f:c3e2:dd5f])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 15CAC4D3;\n\tThu, 19 Feb 2026 11:21:35 +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=\"gm7LVW/z\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771496495;\n\tbh=Xbhix+/+UjecgaWMoj/mz+QInc0+2EQjp/0NvpPSEn4=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=gm7LVW/z1OdYwAMa0huJcg9jw1fdnNwpwHGdsohiKTR1L8ZG81ScVmKl9DvL2r1Kt\n\toGWmGiQLdUHwWHaITBwFxY/tXpBengpyVQXjcSlf0TMMiZzI1NUpQDg2oBXduphw9A\n\tUliLb51vA5mzvmPYPppiDdrLOaehat4BSdPDDljg=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260213-kbingham-quantizers-v7-4-1626b9aaabf1@ideasonboard.com>","References":"<20260213-kbingham-quantizers-v7-0-1626b9aaabf1@ideasonboard.com>\n\t<20260213-kbingham-quantizers-v7-4-1626b9aaabf1@ideasonboard.com>","Subject":"Re: [PATCH v7 04/15] ipa: libipa: Provide fixed point quantized\n\ttraits","From":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tIsaac Scott <isaac.scott@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Thu, 19 Feb 2026 11:22:25 +0100","Message-ID":"<177149654502.3673516.5981622601505624822@localhost>","User-Agent":"alot/0.12.dev8+g2c003385c862.d20250602","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>"}},{"id":38247,"web_url":"https://patchwork.libcamera.org/comment/38247/","msgid":"<20260219124758.GK520738@killaraus.ideasonboard.com>","date":"2026-02-19T12:47:58","subject":"Re: [PATCH v7 04/15] ipa: libipa: Provide fixed point quantized\n\ttraits","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Fri, Feb 13, 2026 at 04:57:43PM +0000, Kieran Bingham wrote:\n> Extend the new Quantized type infrastructure by providing a\n> FixedPointQTraits template.\n> \n> This allows construction of fixed point types with a Quantized storage\n> that allows easy reading of both the underlying quantized type value and\n> a floating point representation of that same value.\n> \n> Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>\n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> ---\n> v4:\n> - Assert that the given type has enough bits for the usage\n> - Use unsigned types for calculating qmin/qmax\n> - Reorder toFloat/fromFloat and min/max for future inlining\n> - Make toFloat and fromFloat constexpr\n> \n> v5:\n> - Make UT, Bits and Bitmask private (and remove doxygen)\n> - Remove constexpr from fromFloat which uses std::round (only constexpr\n>   in C++23)\n> - static_assert that min<max when converted\n> - Provide new Q and UQ automatic width types (Thanks Barnabás)\n> - Convert types to shortened Q/UQ automatic widths\n> - Use automatic width Q/UQ for 12,4\n> - change qmin->qMin qmax->qMax Bits->bits BitMask->bitMask\n> - Remove typedefs for Q1_7 etc\n> \n> v6:\n> - Use 'quantized' over 'quantised'\n> - Document sign is based on T and number of bits includes sign bit\n> \n> - Document that fromFloat also clamps between [min, max]\n> \n> - Remove 64 bit support. We have 32 bit assumptions on fromFloat\n> \n> - Restrict to 24 bits, to stay compatible with float types\n> \n> v7:\n> - Use unsigned storage and ensure we don't have sign extension issues.\n> ---\n>  src/ipa/libipa/fixedpoint.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++++++\n>  2 files changed, 172 insertions(+)\n> \n> diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n> index 6b698fc5d680..caa9ce0fc1ec 100644\n> --- a/src/ipa/libipa/fixedpoint.cpp\n> +++ b/src/ipa/libipa/fixedpoint.cpp\n> @@ -37,6 +37,104 @@ namespace ipa {\n>   * \\return The converted value\n>   */\n>  \n> +/**\n> + * \\struct libcamera::ipa::FixedPointQTraits\n> + * \\brief Traits type implementing fixed-point quantisation conversions\n\ns/quantisation/quantization/ (for consistency, not because it's the\nright flavour of English :-))\n\n> + *\n> + * The FixedPointQTraits structure defines a policy for mapping floating-point\n> + * values to and from fixed-point integer representations. It is parameterised\n\ns/fixed-point/fixed-point 2's complement/\n\n(just noticed we never mention this anywhere)\n\n> + * by the number of integer bits \\a I, fractional bits \\a F, and the integral\n> + * storage type \\a T. The traits are used with Quantized<Traits> to create a\n> + * quantized type that stores both the fixed-point representation and the\n> + * corresponding floating-point value.\n> + *\n> + * The signedness of the type is determined by the signedness of \\a T. For\n> + * signed types, the number of integer bits in \\a I includes the sign bit.\n> + *\n> + * Storage is determined by the total number of bits \\a (I + F) and is\n> + * automatically selected, but the internal storage type is always an unsigned\n> + * integer to guarantee against sign extension when storing quantized values\n> + * in registers.\n> + *\n> + * The trait exposes compile-time constants describing the bit layout, limits,\n> + * and scaling factors used in the fixed-point representation.\n> + *\n> + * \\tparam I Number of integer bits\n> + * \\tparam F Number of fractional bits\n> + * \\tparam T Integral type used to store the quantized value\n> + */\n> +\n> +/**\n> + * \\typedef FixedPointQTraits::QuantizedType\n> + * \\brief The integral storage type used for the fixed-point representation\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::qMin\n> + * \\brief Minimum representable quantized integer value\n> + *\n> + * This corresponds to the most negative value for signed formats or zero for\n> + * unsigned formats.\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::qMax\n> + * \\brief Maximum representable quantized integer value\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::min\n> + * \\brief Minimum representable floating-point value corresponding to qMin\n> + */\n> +\n> +/**\n> + * \\var FixedPointQTraits::max\n> + * \\brief Maximum representable floating-point value corresponding to qMax\n> + */\n> +\n> +/**\n> + * \\fn FixedPointQTraits::fromFloat(float v)\n> + * \\brief Convert a floating-point value to a fixed-point integer\n> + * \\param[in] v The floating-point value to be converted\n> + * \\return The quantized fixed-point integer representation\n> + *\n> + * The conversion first clamps the floating-point input \\a v to the range [min,\n> + * max] and then rounds it to the nearest integer according to the scaling\n\ns/integer/fixed-point value/ maybe ? Rounding to the nearest integer\nsounds likes it rounds to an integer value.\n\n> + * factor defined by the number of fractional bits F.\n> + */\n> +\n> +/**\n> + * \\fn FixedPointQTraits::toFloat(QuantizedType q)\n> + * \\brief Convert a fixed-point integer to a floating-point value\n> + * \\param[in] q The fixed-point integer value to be converted\n> + * \\return The corresponding floating-point value\n> + *\n> + * The conversion sign-extends the integer value if required and divides by the\n> + * scaling factor defined by the number of fractional bits F.\n> + */\n> +\n> +/**\n> + * \\typedef Q\n> + * \\brief Define a signed fixed-point quantized type with automatic storage width\n> + * \\tparam I The number of integer bits\n> + * \\tparam F The number of fractional bits\n> + *\n> + * This alias defines a signed fixed-point quantized type using the\n> + * \\ref FixedPointQTraits trait and a suitable signed integer storage type\n> + * automatically selected based on the total number of bits \\a (I + F).\n> + */\n> +\n> +/**\n> + * \\typedef UQ\n> + * \\brief Define an unsigned fixed-point quantized type with automatic storage width\n> + * \\tparam I The number of integer bits\n> + * \\tparam F The number of fractional bits\n> + *\n> + * This alias defines an unsigned fixed-point quantized type using the\n> + * \\ref FixedPointQTraits trait and a suitable unsigned integer storage type\n> + * automatically selected based on the total number of bits \\a (I + F).\n> + */\n> +\n>  } /* namespace ipa */\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h\n> index aeb9bce3269b..b6b611df7fc3 100644\n> --- a/src/ipa/libipa/fixedpoint.h\n> +++ b/src/ipa/libipa/fixedpoint.h\n> @@ -10,6 +10,8 @@\n>  #include <cmath>\n>  #include <type_traits>\n>  \n> +#include \"quantized.h\"\n> +\n>  namespace libcamera {\n>  \n>  namespace ipa {\n> @@ -63,6 +65,78 @@ constexpr R fixedToFloatingPoint(T number)\n>  \treturn static_cast<R>(t) / static_cast<R>(1 << F);\n>  }\n>  \n> +template<unsigned int I, unsigned int F, typename T>\n> +struct FixedPointQTraits {\n> +private:\n> +\tstatic_assert(std::is_integral_v<T>, \"FixedPointQTraits: T must be integral\");\n> +\tusing UT = std::make_unsigned_t<T>;\n> +\n> +\tstatic constexpr unsigned int bits = I + F;\n> +\tstatic_assert(bits <= sizeof(UT) * 8, \"FixedPointQTraits: too many bits for type UT\");\n> +\n> +\t/*\n> +\t * If fixed point storage is required with more than 24 bits, consider\n> +\t * updating this implementation to use double-precision floating point.\n> +\t */\n> +\tstatic_assert(bits <= 24, \"Floating point precision may be insufficient for more than 24 bits\");\n> +\n> +\tstatic constexpr UT bitMask = bits < sizeof(UT) * 8\n> +\t\t\t\t\t      ? (UT{ 1 } << bits) - 1\n> +\t\t\t\t\t      : ~UT{ 0 };\n\nWeird indentation.\n\n\tstatic constexpr UT bitMask = bits < sizeof(UT) * 8\n\t\t\t\t    ? (UT{ 1 } << bits) - 1\n\t\t\t\t    : ~UT{ 0 };\n\nSame below.\n\n> +\n> +public:\n> +\tusing QuantizedType = UT;\n> +\n> +\tstatic constexpr UT qMin = std::is_signed_v<T>\n> +\t\t\t\t\t   ? -(UT{ 1 } << (bits - 1))\n> +\t\t\t\t\t   : 0;\n> +\n> +\tstatic constexpr UT qMax = std::is_signed_v<T>\n> +\t\t\t\t\t   ? (UT{ 1 } << (bits - 1)) - 1\n> +\t\t\t\t\t   : bitMask;\n> +\n> +\tstatic constexpr float toFloat(QuantizedType q)\n> +\t{\n> +\t\treturn fixedToFloatingPoint<I, F, float, T>(q);\n> +\t}\n> +\n> +\tstatic constexpr float min = fixedToFloatingPoint<I, F, float>(static_cast<T>(qMin));\n> +\tstatic constexpr float max = fixedToFloatingPoint<I, F, float>(static_cast<T>(qMax));\n> +\n> +\tstatic_assert(min < max, \"FixedPointQTraits: Minimum must be less than maximum\");\n> +\n> +\t/* Conversion functions required by Quantized<Traits> */\n> +\tstatic QuantizedType fromFloat(float v)\n\nYou're dropped the constexpr here, was that intentional ?\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> +\t{\n> +\t\tv = std::clamp(v, min, max);\n> +\t\treturn floatingToFixedPoint<I, F, T, float>(v);\n> +\t}\n> +};\n> +\n> +namespace details {\n> +\n> +template<unsigned int Bits>\n> +constexpr auto qtype()\n> +{\n> +\tstatic_assert(Bits <= 32,\n> +\t\t      \"Unsupported number of bits for quantized type\");\n> +\n> +\tif constexpr (Bits <= 8)\n> +\t\treturn int8_t();\n> +\telse if constexpr (Bits <= 16)\n> +\t\treturn int16_t();\n> +\telse if constexpr (Bits <= 32)\n> +\t\treturn int32_t();\n> +}\n> +\n> +} /* namespace details */\n> +\n> +template<unsigned int I, unsigned int F>\n> +using Q = Quantized<FixedPointQTraits<I, F, decltype(details::qtype<I + F>())>>;\n> +\n> +template<unsigned int I, unsigned int F>\n> +using UQ = Quantized<FixedPointQTraits<I, F, std::make_unsigned_t<decltype(details::qtype<I + F>())>>>;\n> +\n>  } /* namespace ipa */\n>  \n>  } /* namespace libcamera */","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 AABA6C31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Feb 2026 12:48:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DA3226224B;\n\tThu, 19 Feb 2026 13:48:04 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 837EB620C9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 13:48:02 +0100 (CET)","from killaraus.ideasonboard.com (unknown [83.245.237.175])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id E259B4D3;\n\tThu, 19 Feb 2026 13:47:08 +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=\"XUygDA+l\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771505229;\n\tbh=dKdKhmRSfCB1FHTg1T69mUQa1GB9l+ix3aU10BGDeKA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=XUygDA+l+dnFsL5AiSIAogZBhgwp05WnaorCLzqRxZqeK7w774bGu3bMUJzRDpUfZ\n\t6pxw+bQ9uj2PHucMrSfQIRjZhR0A42qblr4H1noZ3H0XzfOAqvdVqKLMrp0nBtf2lN\n\tn5gKTCcWee27o84ugDumdmGeqJpGlmOYBo6JdArY=","Date":"Thu, 19 Feb 2026 13:47:58 +0100","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tIsaac Scott <isaac.scott@ideasonboard.com>","Subject":"Re: [PATCH v7 04/15] ipa: libipa: Provide fixed point quantized\n\ttraits","Message-ID":"<20260219124758.GK520738@killaraus.ideasonboard.com>","References":"<20260213-kbingham-quantizers-v7-0-1626b9aaabf1@ideasonboard.com>\n\t<20260213-kbingham-quantizers-v7-4-1626b9aaabf1@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20260213-kbingham-quantizers-v7-4-1626b9aaabf1@ideasonboard.com>","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>"}},{"id":38249,"web_url":"https://patchwork.libcamera.org/comment/38249/","msgid":"<177150727015.2767776.4036863792365555438@ping.linuxembedded.co.uk>","date":"2026-02-19T13:21:10","subject":"Re: [PATCH v7 04/15] ipa: libipa: Provide fixed point quantized\n\ttraits","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Laurent Pinchart (2026-02-19 12:47:58)\n> On Fri, Feb 13, 2026 at 04:57:43PM +0000, Kieran Bingham wrote:\n> > Extend the new Quantized type infrastructure by providing a\n> > FixedPointQTraits template.\n> > \n> > This allows construction of fixed point types with a Quantized storage\n> > that allows easy reading of both the underlying quantized type value and\n> > a floating point representation of that same value.\n> > \n> > Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>\n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > \n> > ---\n> > v4:\n> > - Assert that the given type has enough bits for the usage\n> > - Use unsigned types for calculating qmin/qmax\n> > - Reorder toFloat/fromFloat and min/max for future inlining\n> > - Make toFloat and fromFloat constexpr\n> > \n> > v5:\n> > - Make UT, Bits and Bitmask private (and remove doxygen)\n> > - Remove constexpr from fromFloat which uses std::round (only constexpr\n> >   in C++23)\n> > - static_assert that min<max when converted\n> > - Provide new Q and UQ automatic width types (Thanks Barnabás)\n> > - Convert types to shortened Q/UQ automatic widths\n> > - Use automatic width Q/UQ for 12,4\n> > - change qmin->qMin qmax->qMax Bits->bits BitMask->bitMask\n> > - Remove typedefs for Q1_7 etc\n> > \n> > v6:\n> > - Use 'quantized' over 'quantised'\n> > - Document sign is based on T and number of bits includes sign bit\n> > \n> > - Document that fromFloat also clamps between [min, max]\n> > \n> > - Remove 64 bit support. We have 32 bit assumptions on fromFloat\n> > \n> > - Restrict to 24 bits, to stay compatible with float types\n> > \n> > v7:\n> > - Use unsigned storage and ensure we don't have sign extension issues.\n> > ---\n> >  src/ipa/libipa/fixedpoint.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++\n> >  src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++++++\n> >  2 files changed, 172 insertions(+)\n> > \n> > diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n> > index 6b698fc5d680..caa9ce0fc1ec 100644\n> > --- a/src/ipa/libipa/fixedpoint.cpp\n> > +++ b/src/ipa/libipa/fixedpoint.cpp\n> > @@ -37,6 +37,104 @@ namespace ipa {\n> >   * \\return The converted value\n> >   */\n> >  \n> > +/**\n> > + * \\struct libcamera::ipa::FixedPointQTraits\n> > + * \\brief Traits type implementing fixed-point quantisation conversions\n> \n> s/quantisation/quantization/ (for consistency, not because it's the\n> right flavour of English :-))\n\nI know - I hate it ... my fingers type english ... but somehow I ended\nup mostly using that other thing...\n\nI'm not changing everything now ;-)\n\n\n> \n> > + *\n> > + * The FixedPointQTraits structure defines a policy for mapping floating-point\n> > + * values to and from fixed-point integer representations. It is parameterised\n> \n> s/fixed-point/fixed-point 2's complement/\n> \n> (just noticed we never mention this anywhere)\n\nUpdated,\n\n> \n> > + * by the number of integer bits \\a I, fractional bits \\a F, and the integral\n> > + * storage type \\a T. The traits are used with Quantized<Traits> to create a\n> > + * quantized type that stores both the fixed-point representation and the\n> > + * corresponding floating-point value.\n> > + *\n> > + * The signedness of the type is determined by the signedness of \\a T. For\n> > + * signed types, the number of integer bits in \\a I includes the sign bit.\n> > + *\n> > + * Storage is determined by the total number of bits \\a (I + F) and is\n> > + * automatically selected, but the internal storage type is always an unsigned\n> > + * integer to guarantee against sign extension when storing quantized values\n> > + * in registers.\n> > + *\n> > + * The trait exposes compile-time constants describing the bit layout, limits,\n> > + * and scaling factors used in the fixed-point representation.\n> > + *\n> > + * \\tparam I Number of integer bits\n> > + * \\tparam F Number of fractional bits\n> > + * \\tparam T Integral type used to store the quantized value\n> > + */\n> > +\n> > +/**\n> > + * \\typedef FixedPointQTraits::QuantizedType\n> > + * \\brief The integral storage type used for the fixed-point representation\n> > + */\n> > +\n> > +/**\n> > + * \\var FixedPointQTraits::qMin\n> > + * \\brief Minimum representable quantized integer value\n> > + *\n> > + * This corresponds to the most negative value for signed formats or zero for\n> > + * unsigned formats.\n> > + */\n> > +\n> > +/**\n> > + * \\var FixedPointQTraits::qMax\n> > + * \\brief Maximum representable quantized integer value\n> > + */\n> > +\n> > +/**\n> > + * \\var FixedPointQTraits::min\n> > + * \\brief Minimum representable floating-point value corresponding to qMin\n> > + */\n> > +\n> > +/**\n> > + * \\var FixedPointQTraits::max\n> > + * \\brief Maximum representable floating-point value corresponding to qMax\n> > + */\n> > +\n> > +/**\n> > + * \\fn FixedPointQTraits::fromFloat(float v)\n> > + * \\brief Convert a floating-point value to a fixed-point integer\n> > + * \\param[in] v The floating-point value to be converted\n> > + * \\return The quantized fixed-point integer representation\n> > + *\n> > + * The conversion first clamps the floating-point input \\a v to the range [min,\n> > + * max] and then rounds it to the nearest integer according to the scaling\n> \n> s/integer/fixed-point value/ maybe ? Rounding to the nearest integer\n> sounds likes it rounds to an integer value.\n\nAck,\n\n\n> \n> > + * factor defined by the number of fractional bits F.\n> > + */\n> > +\n> > +/**\n> > + * \\fn FixedPointQTraits::toFloat(QuantizedType q)\n> > + * \\brief Convert a fixed-point integer to a floating-point value\n> > + * \\param[in] q The fixed-point integer value to be converted\n> > + * \\return The corresponding floating-point value\n> > + *\n> > + * The conversion sign-extends the integer value if required and divides by the\n> > + * scaling factor defined by the number of fractional bits F.\n> > + */\n> > +\n> > +/**\n> > + * \\typedef Q\n> > + * \\brief Define a signed fixed-point quantized type with automatic storage width\n> > + * \\tparam I The number of integer bits\n> > + * \\tparam F The number of fractional bits\n> > + *\n> > + * This alias defines a signed fixed-point quantized type using the\n> > + * \\ref FixedPointQTraits trait and a suitable signed integer storage type\n> > + * automatically selected based on the total number of bits \\a (I + F).\n> > + */\n> > +\n> > +/**\n> > + * \\typedef UQ\n> > + * \\brief Define an unsigned fixed-point quantized type with automatic storage width\n> > + * \\tparam I The number of integer bits\n> > + * \\tparam F The number of fractional bits\n> > + *\n> > + * This alias defines an unsigned fixed-point quantized type using the\n> > + * \\ref FixedPointQTraits trait and a suitable unsigned integer storage type\n> > + * automatically selected based on the total number of bits \\a (I + F).\n> > + */\n> > +\n> >  } /* namespace ipa */\n> >  \n> >  } /* namespace libcamera */\n> > diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h\n> > index aeb9bce3269b..b6b611df7fc3 100644\n> > --- a/src/ipa/libipa/fixedpoint.h\n> > +++ b/src/ipa/libipa/fixedpoint.h\n> > @@ -10,6 +10,8 @@\n> >  #include <cmath>\n> >  #include <type_traits>\n> >  \n> > +#include \"quantized.h\"\n> > +\n> >  namespace libcamera {\n> >  \n> >  namespace ipa {\n> > @@ -63,6 +65,78 @@ constexpr R fixedToFloatingPoint(T number)\n> >       return static_cast<R>(t) / static_cast<R>(1 << F);\n> >  }\n> >  \n> > +template<unsigned int I, unsigned int F, typename T>\n> > +struct FixedPointQTraits {\n> > +private:\n> > +     static_assert(std::is_integral_v<T>, \"FixedPointQTraits: T must be integral\");\n> > +     using UT = std::make_unsigned_t<T>;\n> > +\n> > +     static constexpr unsigned int bits = I + F;\n> > +     static_assert(bits <= sizeof(UT) * 8, \"FixedPointQTraits: too many bits for type UT\");\n> > +\n> > +     /*\n> > +      * If fixed point storage is required with more than 24 bits, consider\n> > +      * updating this implementation to use double-precision floating point.\n> > +      */\n> > +     static_assert(bits <= 24, \"Floating point precision may be insufficient for more than 24 bits\");\n> > +\n> > +     static constexpr UT bitMask = bits < sizeof(UT) * 8\n> > +                                           ? (UT{ 1 } << bits) - 1\n> > +                                           : ~UT{ 0 };\n> \n> Weird indentation.\n> \n>         static constexpr UT bitMask = bits < sizeof(UT) * 8\n>                                     ? (UT{ 1 } << bits) - 1\n>                                     : ~UT{ 0 };\n> \n> Same below.\n\nI caved and just followed the formatter style. I wanted checkpatch\nclean.\n\n\nChanged back ... we need to fund a 3 year research project on how to\nmake clang-format know how we like to format things....\n\n> \n> > +\n> > +public:\n> > +     using QuantizedType = UT;\n> > +\n> > +     static constexpr UT qMin = std::is_signed_v<T>\n> > +                                        ? -(UT{ 1 } << (bits - 1))\n> > +                                        : 0;\n> > +\n> > +     static constexpr UT qMax = std::is_signed_v<T>\n> > +                                        ? (UT{ 1 } << (bits - 1)) - 1\n> > +                                        : bitMask;\n> > +\n> > +     static constexpr float toFloat(QuantizedType q)\n> > +     {\n> > +             return fixedToFloatingPoint<I, F, float, T>(q);\n> > +     }\n> > +\n> > +     static constexpr float min = fixedToFloatingPoint<I, F, float>(static_cast<T>(qMin));\n> > +     static constexpr float max = fixedToFloatingPoint<I, F, float>(static_cast<T>(qMax));\n> > +\n> > +     static_assert(min < max, \"FixedPointQTraits: Minimum must be less than maximum\");\n> > +\n> > +     /* Conversion functions required by Quantized<Traits> */\n> > +     static QuantizedType fromFloat(float v)\n> \n> You're dropped the constexpr here, was that intentional ?\n\nI recall that being from a review from Barnabas ? digging.\n\n\nV4:\n - Make toFloat and fromFloat constexpr\n\nv5:\n - Remove constexpr from fromFloat which uses std::round (only constexpr\n  in C++23)\n\nSo - yes, intentional.\n\n> \n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> \n> > +     {\n> > +             v = std::clamp(v, min, max);\n> > +             return floatingToFixedPoint<I, F, T, float>(v);\n> > +     }\n> > +};\n> > +\n> > +namespace details {\n> > +\n> > +template<unsigned int Bits>\n> > +constexpr auto qtype()\n> > +{\n> > +     static_assert(Bits <= 32,\n> > +                   \"Unsupported number of bits for quantized type\");\n> > +\n> > +     if constexpr (Bits <= 8)\n> > +             return int8_t();\n> > +     else if constexpr (Bits <= 16)\n> > +             return int16_t();\n> > +     else if constexpr (Bits <= 32)\n> > +             return int32_t();\n> > +}\n> > +\n> > +} /* namespace details */\n> > +\n> > +template<unsigned int I, unsigned int F>\n> > +using Q = Quantized<FixedPointQTraits<I, F, decltype(details::qtype<I + F>())>>;\n> > +\n> > +template<unsigned int I, unsigned int F>\n> > +using UQ = Quantized<FixedPointQTraits<I, F, std::make_unsigned_t<decltype(details::qtype<I + F>())>>>;\n> > +\n> >  } /* namespace ipa */\n> >  \n> >  } /* namespace libcamera */\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","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 134CBC31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Feb 2026 13:21:16 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 648D362249;\n\tThu, 19 Feb 2026 14:21:15 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5118D62243\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 14:21:13 +0100 (CET)","from monstersaurus.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2A15C673;\n\tThu, 19 Feb 2026 14:20:20 +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=\"c7wVEy6p\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771507220;\n\tbh=BQiBPxne2r/x7rDkzPHymLjpD69QwHkyTkx2SlYFoUk=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=c7wVEy6poaa+hHB2Cg8TuWnBaJjyJs9aDysaFeZWF8hVAh92CnE8264OoUPLSyfFB\n\tDd23K7zR8sC0mebSnEBE86IMGZzHTdEmUVxDuja7wCIvUNIu1EsDKBvLM3LnsuOO73\n\tO/5yVHp8YB/HCekv2EqJyqsc96aAg4XQT6pEB8aY=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260219124758.GK520738@killaraus.ideasonboard.com>","References":"<20260213-kbingham-quantizers-v7-0-1626b9aaabf1@ideasonboard.com>\n\t<20260213-kbingham-quantizers-v7-4-1626b9aaabf1@ideasonboard.com>\n\t<20260219124758.GK520738@killaraus.ideasonboard.com>","Subject":"Re: [PATCH v7 04/15] ipa: libipa: Provide fixed point quantized\n\ttraits","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tIsaac Scott <isaac.scott@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Thu, 19 Feb 2026 13:21:10 +0000","Message-ID":"<177150727015.2767776.4036863792365555438@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","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>"}}]