[{"id":37901,"web_url":"https://patchwork.libcamera.org/comment/37901/","msgid":"<176916296200.302817.14543717412534260633@localhost>","date":"2026-01-23T10:09:22","subject":"Re: [PATCH v6 04/16] 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":"Hi Kieran,\n\nQuoting Kieran Bingham (2026-01-21 18:37:23)\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> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> ---\n>  src/ipa/libipa/fixedpoint.cpp | 93 +++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++\n>  2 files changed, 167 insertions(+)\n> \n> diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n> index 6b698fc5d680..c9d04e31e4df 100644\n> --- a/src/ipa/libipa/fixedpoint.cpp\n> +++ b/src/ipa/libipa/fixedpoint.cpp\n> @@ -37,6 +37,99 @@ 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\nNit: I saw in the log that quantized is preferred over quantised.\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> + * 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\nSo it took me a while to accept that it is a great idea to represent a\nsigned fixed point as signed int. In all the hardware/register\ninterfaces I remember, the registers were always represented as unsigned\ntypes. So I tried to break it...\n\nuint16_t myReg;\nusing T=Q<6,10>;\nT q(T::TraitsType::min);\nmyReg = q.quantized();\n\n... and failed miserably :-)\n\nSo all in all I think this is working great.\n\nReviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n\nCheers,\nStefan\n\n\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 48a9757f9554..33d1f4af4792 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(T) * 8, \"FixedPointQTraits: too many bits for type T\");\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 T bitMask = (bits < sizeof(T) * 8)\n> +                                  ? static_cast<T>((UT{1} << bits) - 1)\n> +                                  : static_cast<T>(~UT{0});\n> +\n> +public:\n> +       using QuantizedType = T;\n> +\n> +       static constexpr T qMin = std::is_signed_v<T>\n> +                               ? static_cast<T>(-(UT{1} << (bits - 1)))\n> +                               : static_cast<T>(0);\n> +\n> +       static constexpr T qMax = std::is_signed_v<T>\n> +                               ? static_cast<T>((UT{1} << (bits - 1)) - 1)\n> +                               : bitMask;\n> +\n> +       static constexpr float toFloat(QuantizedType q)\n> +       {\n> +               return fixedToFloatingPoint<I, F, float, QuantizedType>(q);\n> +       }\n> +\n> +       static constexpr float min = fixedToFloatingPoint<I, F, float>(qMin);\n> +       static constexpr float max = fixedToFloatingPoint<I, F, float>(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, QuantizedType, 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> 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 ECE60BDCBF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 23 Jan 2026 10:09:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F3DBF61FC4;\n\tFri, 23 Jan 2026 11:09:26 +0100 (CET)","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 D343A61F61\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 23 Jan 2026 11:09:24 +0100 (CET)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:1d92:9f27:5dd1:dc89])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 91103103D; \n\tFri, 23 Jan 2026 11:08:51 +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=\"neSMlpS9\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769162931;\n\tbh=itNbF465yhPG7y+reF+pT5jQfkH/xcv/sZEYtaIBUw4=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=neSMlpS9Ib13uM2F7CPvOd4/Ci1gczPZ+b3Gi2dTKBQWMeihfDB98OvfJYxXTDGOy\n\txp1MGemFpHt+6NKUffnO+Ez0Z+eUS9fqaVLSJGPYQ2FGt+dU2UMDxRk57x/bAUaaCs\n\tBxffwF58hb3pc1bioaArsuKTirs5FOkn06q2RZrM=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260121173737.376113-5-kieran.bingham@ideasonboard.com>","References":"<20260121173737.376113-1-kieran.bingham@ideasonboard.com>\n\t<20260121173737.376113-5-kieran.bingham@ideasonboard.com>","Subject":"Re: [PATCH v6 04/16] 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 <libcamera-devel@lists.libcamera.org>","Date":"Fri, 23 Jan 2026 11:09:22 +0100","Message-ID":"<176916296200.302817.14543717412534260633@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":37928,"web_url":"https://patchwork.libcamera.org/comment/37928/","msgid":"<20260124014240.GM215800@killaraus>","date":"2026-01-24T01:42:40","subject":"Re: [PATCH v6 04/16] 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, Jan 23, 2026 at 11:09:22AM +0100, Stefan Klug wrote:\n> Quoting Kieran Bingham (2026-01-21 18:37:23)\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> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > ---\n> >  src/ipa/libipa/fixedpoint.cpp | 93 +++++++++++++++++++++++++++++++++++\n> >  src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++\n> >  2 files changed, 167 insertions(+)\n> > \n> > diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n> > index 6b698fc5d680..c9d04e31e4df 100644\n> > --- a/src/ipa/libipa/fixedpoint.cpp\n> > +++ b/src/ipa/libipa/fixedpoint.cpp\n> > @@ -37,6 +37,99 @@ 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> Nit: I saw in the log that quantized is preferred over quantised.\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> > + * 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> So it took me a while to accept that it is a great idea to represent a\n> signed fixed point as signed int. In all the hardware/register\n> interfaces I remember, the registers were always represented as unsigned\n> types. So I tried to break it...\n> \n> uint16_t myReg;\n> using T=Q<6,10>;\n> T q(T::TraitsType::min);\n> myReg = q.quantized();\n> \n> ... and failed miserably :-)\n> \n> So all in all I think this is working great.\n\n\tuint32_t myReg;\n\tusing T=Q<4,4>;\n\tT q(T::TraitsType::min);\n\tmyReg = q.quantized();\n\n\tstd::cout << \"q: \" << q << \", myReg: \" << utils::hex(myReg) << std::endl;\n\nproduces\n\nq: [0x80:-8], myReg: 0xffffff80\n\nI would expect myReg to be 0x80.\n\n> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> \n> Cheers,\n> Stefan\n> \n> \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 48a9757f9554..33d1f4af4792 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(T) * 8, \"FixedPointQTraits: too many bits for type T\");\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 T bitMask = (bits < sizeof(T) * 8)\n> > +                                  ? static_cast<T>((UT{1} << bits) - 1)\n> > +                                  : static_cast<T>(~UT{0});\n> > +\n> > +public:\n> > +       using QuantizedType = T;\n> > +\n> > +       static constexpr T qMin = std::is_signed_v<T>\n> > +                               ? static_cast<T>(-(UT{1} << (bits - 1)))\n> > +                               : static_cast<T>(0);\n> > +\n> > +       static constexpr T qMax = std::is_signed_v<T>\n> > +                               ? static_cast<T>((UT{1} << (bits - 1)) - 1)\n> > +                               : bitMask;\n> > +\n> > +       static constexpr float toFloat(QuantizedType q)\n> > +       {\n> > +               return fixedToFloatingPoint<I, F, float, QuantizedType>(q);\n> > +       }\n> > +\n> > +       static constexpr float min = fixedToFloatingPoint<I, F, float>(qMin);\n> > +       static constexpr float max = fixedToFloatingPoint<I, F, float>(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, QuantizedType, 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 */","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 890B6BDCBF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 24 Jan 2026 01:42:44 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D33B661FBB;\n\tSat, 24 Jan 2026 02:42:43 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B6F6661F84\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 24 Jan 2026 02:42:41 +0100 (CET)","from pendragon.ideasonboard.com\n\t(2001-14ba-703d-e500--2a1.rev.dnainternet.fi\n\t[IPv6:2001:14ba:703d:e500::2a1])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id BC01D448;\n\tSat, 24 Jan 2026 02:42:07 +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=\"I5Ku+fmC\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769218927;\n\tbh=u5cpkVS4yv2j2nLvsOAQCO+zlGNTursW51tUxXJudkA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=I5Ku+fmC7l9D1JUmpI5n3615w+qDcQK4AU66USuGio2nGOZMWAg4TGly42WZbdQn8\n\t6SAYnsPR+pSgiEfE9h9Sczdb5vweh2L7YfOMMJ4TdeYQAnXYbSbSGT32NcNSF92HlS\n\tobwHdVGy0vw2wdaPOMvMcLSxHieLlcsk1hK/hqyc=","Date":"Sat, 24 Jan 2026 03:42:40 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>,\n\tIsaac Scott <isaac.scott@ideasonboard.com>","Subject":"Re: [PATCH v6 04/16] ipa: libipa: Provide fixed point quantized\n\ttraits","Message-ID":"<20260124014240.GM215800@killaraus>","References":"<20260121173737.376113-1-kieran.bingham@ideasonboard.com>\n\t<20260121173737.376113-5-kieran.bingham@ideasonboard.com>\n\t<176916296200.302817.14543717412534260633@localhost>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<176916296200.302817.14543717412534260633@localhost>","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":37933,"web_url":"https://patchwork.libcamera.org/comment/37933/","msgid":"<176941666731.302817.12404253310433078537@localhost>","date":"2026-01-26T08:37:47","subject":"Re: [PATCH v6 04/16] 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 Laurent Pinchart (2026-01-24 02:42:40)\n> On Fri, Jan 23, 2026 at 11:09:22AM +0100, Stefan Klug wrote:\n> > Quoting Kieran Bingham (2026-01-21 18:37:23)\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> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > ---\n> > >  src/ipa/libipa/fixedpoint.cpp | 93 +++++++++++++++++++++++++++++++++++\n> > >  src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++\n> > >  2 files changed, 167 insertions(+)\n> > > \n> > > diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n> > > index 6b698fc5d680..c9d04e31e4df 100644\n> > > --- a/src/ipa/libipa/fixedpoint.cpp\n> > > +++ b/src/ipa/libipa/fixedpoint.cpp\n> > > @@ -37,6 +37,99 @@ 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> > Nit: I saw in the log that quantized is preferred over quantised.\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> > > + * 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> > So it took me a while to accept that it is a great idea to represent a\n> > signed fixed point as signed int. In all the hardware/register\n> > interfaces I remember, the registers were always represented as unsigned\n> > types. So I tried to break it...\n> > \n> > uint16_t myReg;\n> > using T=Q<6,10>;\n> > T q(T::TraitsType::min);\n> > myReg = q.quantized();\n> > \n> > ... and failed miserably :-)\n> > \n> > So all in all I think this is working great.\n> \n>         uint32_t myReg;\n>         using T=Q<4,4>;\n>         T q(T::TraitsType::min);\n>         myReg = q.quantized();\n> \n>         std::cout << \"q: \" << q << \", myReg: \" << utils::hex(myReg) << std::endl;\n> \n> produces\n> \n> q: [0x80:-8], myReg: 0xffffff80\n> \n> I would expect myReg to be 0x80.\n\nEeek why didn't I see that case. Yes, that needs to be fixed.\n\nFor me that raises the question again if quantized() should always\nreturn an unsigned value as there is imho no real use case for a signed\nrepresentation of the quantized type but a high risk for confusion.\n\nBest regards,\nStefan\n\n> \n> > Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> > \n> > Cheers,\n> > Stefan\n> > \n> > \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 48a9757f9554..33d1f4af4792 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(T) * 8, \"FixedPointQTraits: too many bits for type T\");\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 T bitMask = (bits < sizeof(T) * 8)\n> > > +                                  ? static_cast<T>((UT{1} << bits) - 1)\n> > > +                                  : static_cast<T>(~UT{0});\n> > > +\n> > > +public:\n> > > +       using QuantizedType = T;\n> > > +\n> > > +       static constexpr T qMin = std::is_signed_v<T>\n> > > +                               ? static_cast<T>(-(UT{1} << (bits - 1)))\n> > > +                               : static_cast<T>(0);\n> > > +\n> > > +       static constexpr T qMax = std::is_signed_v<T>\n> > > +                               ? static_cast<T>((UT{1} << (bits - 1)) - 1)\n> > > +                               : bitMask;\n> > > +\n> > > +       static constexpr float toFloat(QuantizedType q)\n> > > +       {\n> > > +               return fixedToFloatingPoint<I, F, float, QuantizedType>(q);\n> > > +       }\n> > > +\n> > > +       static constexpr float min = fixedToFloatingPoint<I, F, float>(qMin);\n> > > +       static constexpr float max = fixedToFloatingPoint<I, F, float>(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, QuantizedType, 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 3C0F0C3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 26 Jan 2026 08:37:52 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7F36D61FC9;\n\tMon, 26 Jan 2026 09:37:51 +0100 (CET)","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 4B34C61A35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 26 Jan 2026 09:37:50 +0100 (CET)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:2c78:9e26:19df:a9c4])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id ADB9C557;\n\tMon, 26 Jan 2026 09:37:14 +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=\"uSbrTPnh\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769416634;\n\tbh=4AfFaAssg91SjXYC58bgZVx9AbZDpXOtpQySlNDBwNQ=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=uSbrTPnhtt88B30nqtJhdxzMO6rTrucKMLXYbGUt+HkWD2EAe2mkn9Rfv3O703qns\n\tH3sgn91bochNRblzP5Tz1m+4Al2PdN/KfX331Okk8X7jnP63c7FgRB4LU4dBdGtqHL\n\t6FBOxOLldIuJxONvfJ5dUOV6aLQAhZgAAVuPKD+w=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260124014240.GM215800@killaraus>","References":"<20260121173737.376113-1-kieran.bingham@ideasonboard.com>\n\t<20260121173737.376113-5-kieran.bingham@ideasonboard.com>\n\t<176916296200.302817.14543717412534260633@localhost>\n\t<20260124014240.GM215800@killaraus>","Subject":"Re: [PATCH v6 04/16] ipa: libipa: Provide fixed point quantized\n\ttraits","From":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>,\n\tIsaac Scott <isaac.scott@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Mon, 26 Jan 2026 09:37:47 +0100","Message-ID":"<176941666731.302817.12404253310433078537@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":37936,"web_url":"https://patchwork.libcamera.org/comment/37936/","msgid":"<20260126095711.GA593812@killaraus>","date":"2026-01-26T09:57:11","subject":"Re: [PATCH v6 04/16] 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 Mon, Jan 26, 2026 at 09:37:47AM +0100, Stefan Klug wrote:\n> Quoting Laurent Pinchart (2026-01-24 02:42:40)\n> > On Fri, Jan 23, 2026 at 11:09:22AM +0100, Stefan Klug wrote:\n> > > Quoting Kieran Bingham (2026-01-21 18:37:23)\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> > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > ---\n> > > >  src/ipa/libipa/fixedpoint.cpp | 93 +++++++++++++++++++++++++++++++++++\n> > > >  src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++\n> > > >  2 files changed, 167 insertions(+)\n> > > > \n> > > > diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n> > > > index 6b698fc5d680..c9d04e31e4df 100644\n> > > > --- a/src/ipa/libipa/fixedpoint.cpp\n> > > > +++ b/src/ipa/libipa/fixedpoint.cpp\n> > > > @@ -37,6 +37,99 @@ 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> > > Nit: I saw in the log that quantized is preferred over quantised.\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> > > > + * 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> > > So it took me a while to accept that it is a great idea to represent a\n> > > signed fixed point as signed int. In all the hardware/register\n> > > interfaces I remember, the registers were always represented as unsigned\n> > > types. So I tried to break it...\n> > > \n> > > uint16_t myReg;\n> > > using T=Q<6,10>;\n> > > T q(T::TraitsType::min);\n> > > myReg = q.quantized();\n> > > \n> > > ... and failed miserably :-)\n> > > \n> > > So all in all I think this is working great.\n> > \n> >         uint32_t myReg;\n> >         using T=Q<4,4>;\n> >         T q(T::TraitsType::min);\n> >         myReg = q.quantized();\n> > \n> >         std::cout << \"q: \" << q << \", myReg: \" << utils::hex(myReg) << std::endl;\n> > \n> > produces\n> > \n> > q: [0x80:-8], myReg: 0xffffff80\n> > \n> > I would expect myReg to be 0x80.\n> \n> Eeek why didn't I see that case. Yes, that needs to be fixed.\n> \n> For me that raises the question again if quantized() should always\n> return an unsigned value as there is imho no real use case for a signed\n> representation of the quantized type but a high risk for confusion.\n\nI would make it unsigned unconditionally. It happens that Q handles\nquantized values represented as 2's complement, directly usable for\narithmetics on all the CPU we support, but we know that there are other\nhardware representations (Jacopo mentioned that the C55 uses\nsign/magnitude for some registers). The quantized values should be\nconsidered as a bitfield, not as a numerical value.\n\n> > > Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> > > \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 48a9757f9554..33d1f4af4792 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(T) * 8, \"FixedPointQTraits: too many bits for type T\");\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 T bitMask = (bits < sizeof(T) * 8)\n> > > > +                                  ? static_cast<T>((UT{1} << bits) - 1)\n> > > > +                                  : static_cast<T>(~UT{0});\n> > > > +\n> > > > +public:\n> > > > +       using QuantizedType = T;\n> > > > +\n> > > > +       static constexpr T qMin = std::is_signed_v<T>\n> > > > +                               ? static_cast<T>(-(UT{1} << (bits - 1)))\n> > > > +                               : static_cast<T>(0);\n> > > > +\n> > > > +       static constexpr T qMax = std::is_signed_v<T>\n> > > > +                               ? static_cast<T>((UT{1} << (bits - 1)) - 1)\n> > > > +                               : bitMask;\n> > > > +\n> > > > +       static constexpr float toFloat(QuantizedType q)\n> > > > +       {\n> > > > +               return fixedToFloatingPoint<I, F, float, QuantizedType>(q);\n> > > > +       }\n> > > > +\n> > > > +       static constexpr float min = fixedToFloatingPoint<I, F, float>(qMin);\n> > > > +       static constexpr float max = fixedToFloatingPoint<I, F, float>(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, QuantizedType, 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 */","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 9E78DC3220\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 26 Jan 2026 09:57:16 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7441261FC5;\n\tMon, 26 Jan 2026 10:57:15 +0100 (CET)","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 B970761A35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 26 Jan 2026 10:57:13 +0100 (CET)","from pendragon.ideasonboard.com\n\t(2001-14ba-703d-e500--2a1.rev.dnainternet.fi\n\t[IPv6:2001:14ba:703d:e500::2a1])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id E05C022F;\n\tMon, 26 Jan 2026 10:56:37 +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=\"iIlKkGDO\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769421398;\n\tbh=cvEJNsUT4Scx0UjdqxYSdub+msy8YSkLuJ+dcalh5+c=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=iIlKkGDOMB5ZIft7ytkrkfhUz2U6Irb4yBqJNCb2U+5G8o2VQ+e44x5beecN+ZH+i\n\thCeUAD56O/a9N8o59Tn5Oo8GIrnwuWoHXngvmoZ8OwoLgWFY7MeYnLiVgCR1iuO/HH\n\tBRDnfH8YtQtA0s59B15kRiQ1+qPreDIIro0R1pdU=","Date":"Mon, 26 Jan 2026 11:57:11 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>,\n\tIsaac Scott <isaac.scott@ideasonboard.com>","Subject":"Re: [PATCH v6 04/16] ipa: libipa: Provide fixed point quantized\n\ttraits","Message-ID":"<20260126095711.GA593812@killaraus>","References":"<20260121173737.376113-1-kieran.bingham@ideasonboard.com>\n\t<20260121173737.376113-5-kieran.bingham@ideasonboard.com>\n\t<176916296200.302817.14543717412534260633@localhost>\n\t<20260124014240.GM215800@killaraus>\n\t<176941666731.302817.12404253310433078537@localhost>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<176941666731.302817.12404253310433078537@localhost>","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":37945,"web_url":"https://patchwork.libcamera.org/comment/37945/","msgid":"<d4896097-99f3-49e0-85e5-58a26d216f51@ideasonboard.com>","date":"2026-01-26T14:21:15","subject":"Re: [PATCH v6 04/16] ipa: libipa: Provide fixed point quantized\n\ttraits","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 01. 26. 10:57 keltezéssel, Laurent Pinchart írta:\n> On Mon, Jan 26, 2026 at 09:37:47AM +0100, Stefan Klug wrote:\n>> Quoting Laurent Pinchart (2026-01-24 02:42:40)\n>>> On Fri, Jan 23, 2026 at 11:09:22AM +0100, Stefan Klug wrote:\n>>>> Quoting Kieran Bingham (2026-01-21 18:37:23)\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>>>>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>>>>> ---\n>>>>>   src/ipa/libipa/fixedpoint.cpp | 93 +++++++++++++++++++++++++++++++++++\n>>>>>   src/ipa/libipa/fixedpoint.h   | 74 ++++++++++++++++++++++++++++\n>>>>>   2 files changed, 167 insertions(+)\n>>>>>\n>>>>> diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp\n>>>>> index 6b698fc5d680..c9d04e31e4df 100644\n>>>>> --- a/src/ipa/libipa/fixedpoint.cpp\n>>>>> +++ b/src/ipa/libipa/fixedpoint.cpp\n>>>>> @@ -37,6 +37,99 @@ 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>>>> Nit: I saw in the log that quantized is preferred over quantised.\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>>>>> + * 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>>>> So it took me a while to accept that it is a great idea to represent a\n>>>> signed fixed point as signed int. In all the hardware/register\n>>>> interfaces I remember, the registers were always represented as unsigned\n>>>> types. So I tried to break it...\n>>>>\n>>>> uint16_t myReg;\n>>>> using T=Q<6,10>;\n>>>> T q(T::TraitsType::min);\n>>>> myReg = q.quantized();\n>>>>\n>>>> ... and failed miserably :-)\n>>>>\n>>>> So all in all I think this is working great.\n>>>\n>>>          uint32_t myReg;\n>>>          using T=Q<4,4>;\n>>>          T q(T::TraitsType::min);\n>>>          myReg = q.quantized();\n>>>\n>>>          std::cout << \"q: \" << q << \", myReg: \" << utils::hex(myReg) << std::endl;\n>>>\n>>> produces\n>>>\n>>> q: [0x80:-8], myReg: 0xffffff80\n>>>\n>>> I would expect myReg to be 0x80.\n>>\n>> Eeek why didn't I see that case. Yes, that needs to be fixed.\n\nThat cannot easily be fixed without making things fully unsigned\ndue to how signed -> unsigned conversion is defined.\n\n\n>>\n>> For me that raises the question again if quantized() should always\n>> return an unsigned value as there is imho no real use case for a signed\n>> representation of the quantized type but a high risk for confusion.\n> \n> I would make it unsigned unconditionally. It happens that Q handles\n> quantized values represented as 2's complement, directly usable for\n> arithmetics on all the CPU we support, but we know that there are other\n> hardware representations (Jacopo mentioned that the C55 uses\n> sign/magnitude for some registers). The quantized values should be\n> considered as a bitfield, not as a numerical value.\n\nEven if one does not assume any particular representation, there is the\n\"advantage\" that converting between different signed types will \"keep\" the\nvalue, and that the sign will reflect the \"real\" sign.\n\nBut on the other hand, it is probably the choice of least surprise is to\nkeep everything unsigned.\n\n\n> \n>>>> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n>>>>\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 48a9757f9554..33d1f4af4792 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(T) * 8, \"FixedPointQTraits: too many bits for type T\");\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 T bitMask = (bits < sizeof(T) * 8)\n>>>>> +                                  ? static_cast<T>((UT{1} << bits) - 1)\n>>>>> +                                  : static_cast<T>(~UT{0});\n>>>>> +\n>>>>> +public:\n>>>>> +       using QuantizedType = T;\n>>>>> +\n>>>>> +       static constexpr T qMin = std::is_signed_v<T>\n>>>>> +                               ? static_cast<T>(-(UT{1} << (bits - 1)))\n>>>>> +                               : static_cast<T>(0);\n>>>>> +\n>>>>> +       static constexpr T qMax = std::is_signed_v<T>\n>>>>> +                               ? static_cast<T>((UT{1} << (bits - 1)) - 1)\n>>>>> +                               : bitMask;\n>>>>> +\n>>>>> +       static constexpr float toFloat(QuantizedType q)\n>>>>> +       {\n>>>>> +               return fixedToFloatingPoint<I, F, float, QuantizedType>(q);\n>>>>> +       }\n>>>>> +\n>>>>> +       static constexpr float min = fixedToFloatingPoint<I, F, float>(qMin);\n>>>>> +       static constexpr float max = fixedToFloatingPoint<I, F, float>(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, QuantizedType, 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>","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 6CA06C3220\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 26 Jan 2026 14:21:23 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6C26461FC5;\n\tMon, 26 Jan 2026 15:21:22 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0FDBF61A35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 26 Jan 2026 15:21:21 +0100 (CET)","from [192.168.33.36] (185.221.142.123.nat.pool.zt.hu\n\t[185.221.142.123])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 531E022F;\n\tMon, 26 Jan 2026 15:20:43 +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=\"MT9jRjXh\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769437245;\n\tbh=UIuSYlV3ccRyaxUBWh3RHFDDQLbR8DQhOTjMfaF9GwM=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=MT9jRjXhTWJbv8UZxQrZ9XuZg7QcEqeFwrSkRMcRMSMGSD4akbCaFz8ffadMmQY0q\n\tKI0IWh9PusPJst4qXoUggGaQ2AkCNKf/13FhSvLUX9ZFWP0y+FBWHF7Q5+Z72YVC7h\n\tFmHkwVb8CV+i4OBD2qCtp0N3Ypp09EAAWVWIJq/4=","Message-ID":"<d4896097-99f3-49e0-85e5-58a26d216f51@ideasonboard.com>","Date":"Mon, 26 Jan 2026 15:21:15 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v6 04/16] ipa: libipa: Provide fixed point quantized\n\ttraits","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tStefan Klug <stefan.klug@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>,\n\tIsaac Scott <isaac.scott@ideasonboard.com>","References":"<20260121173737.376113-1-kieran.bingham@ideasonboard.com>\n\t<20260121173737.376113-5-kieran.bingham@ideasonboard.com>\n\t<176916296200.302817.14543717412534260633@localhost>\n\t<20260124014240.GM215800@killaraus>\n\t<176941666731.302817.12404253310433078537@localhost>\n\t<20260126095711.GA593812@killaraus>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260126095711.GA593812@killaraus>","Content-Type":"text/plain; charset=UTF-8; format=flowed","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>"}}]