[{"id":29083,"web_url":"https://patchwork.libcamera.org/comment/29083/","msgid":"<20240327135129.GD4721@pendragon.ideasonboard.com>","date":"2024-03-27T13:51:29","subject":"Re: [PATCH v6 06/18] libcamera: software_isp: Add SwStatsCpu class","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Milan and Hans,\n\nThank you for the patch.\n\nI understand all this is work in progress, so I won't comment on all the\nimprovements we could add in the future, but focus on merging this as\nquickly as possible (aiming for one last new version for the series to\naddress simple issues only).\n\nOn Tue, Mar 19, 2024 at 01:35:53PM +0100, Milan Zamazal wrote:\n> From: Hans de Goede <hdegoede@redhat.com>\n> \n> Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.\n> \n> This implementation offers a configure function + functions to gather\n> statistics on a line by line basis. This allows CPU based software\n> debayering to call into interlace debayering and statistics gathering\n\nI think you meant interleave, not interlace.\n\n> on a line by line bases while the input data is still hot in the cache.\n\ns/bases/basis/\n\n> \n> This implementation also allows specifying a window over which to gather\n> statistics instead of processing the whole frame.\n> \n> Doxygen documentation by Dennis Bonke.\n> \n> Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s\n> Tested-by: Pavel Machek <pavel@ucw.cz>\n> Reviewed-by: Pavel Machek <pavel@ucw.cz>\n> Reviewed-by: Milan Zamazal <mzamazal@redhat.com>\n> Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>\n> Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>\n> Co-developed-by: Pavel Machek <pavel@ucw.cz>\n> Signed-off-by: Pavel Machek <pavel@ucw.cz>\n> Co-developed-by: Dennis Bonke <admin@dennisbonke.com>\n> Signed-off-by: Dennis Bonke <admin@dennisbonke.com>\n> Co-developed-by: Marttico <g.martti@gmail.com>\n> Signed-off-by: Marttico <g.martti@gmail.com>\n> Co-developed-by: Toon Langendam <t.langendam@gmail.com>\n> Signed-off-by: Toon Langendam <t.langendam@gmail.com>\n> Signed-off-by: Hans de Goede <hdegoede@redhat.com>\n> ---\n>  include/libcamera/internal/meson.build        |   1 +\n>  .../internal/software_isp/meson.build         |   5 +\n>  .../internal/software_isp/swisp_stats.h       |  38 ++++\n>  src/libcamera/meson.build                     |   1 +\n>  src/libcamera/software_isp/meson.build        |  12 +\n>  src/libcamera/software_isp/swstats_cpu.cpp    | 208 ++++++++++++++++++\n>  src/libcamera/software_isp/swstats_cpu.h      | 159 +++++++++++++\n>  7 files changed, 424 insertions(+)\n>  create mode 100644 include/libcamera/internal/software_isp/meson.build\n>  create mode 100644 include/libcamera/internal/software_isp/swisp_stats.h\n>  create mode 100644 src/libcamera/software_isp/meson.build\n>  create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp\n>  create mode 100644 src/libcamera/software_isp/swstats_cpu.h\n> \n> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n> index 5807dfd9..160fdc37 100644\n> --- a/include/libcamera/internal/meson.build\n> +++ b/include/libcamera/internal/meson.build\n> @@ -50,3 +50,4 @@ libcamera_internal_headers = files([\n>  ])\n>  \n>  subdir('converter')\n> +subdir('software_isp')\n> diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build\n> new file mode 100644\n> index 00000000..66c9c3fb\n> --- /dev/null\n> +++ b/include/libcamera/internal/software_isp/meson.build\n> @@ -0,0 +1,5 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +libcamera_internal_headers += files([\n> +    'swisp_stats.h',\n> +])\n> diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h\n> new file mode 100644\n> index 00000000..afe42c9a\n> --- /dev/null\n> +++ b/include/libcamera/internal/software_isp/swisp_stats.h\n> @@ -0,0 +1,38 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2023, Linaro Ltd\n> + *\n> + * swisp_stats.h - Statistics data format used by the software ISP and software IPA\n> + */\n> +\n> +#pragma once\n> +\n> +namespace libcamera {\n> +\n> +/**\n> + * \\brief Struct that holds the statistics for the Software ISP.\n\nNo period at the end of the \\brief line. This apply through the whole\nseries.\n\n> + */\n\nWe store documentation in .cpp files.\n\n> +struct SwIspStats {\n> +\t/**\n> +\t * \\brief Holds the sum of all sampled red pixels.\n\nDoes it wrap around or saturate in case of overflow ?\n\n> +\t */\n> +\tunsigned long sumR_;\n\nAny reason for using a platform-dependent type instead of uint32_t or\nuint64_t ?\n\n> +\t/**\n> +\t * \\brief Holds the sum of all sampled green pixels.\n> +\t */\n> +\tunsigned long sumG_;\n> +\t/**\n> +\t * \\brief Holds the sum of all sampled blue pixels.\n> +\t */\n> +\tunsigned long sumB_;\n> +\t/**\n> +\t * \\brief Number of bins in the yHistogram.\n> +\t */\n> +\tstatic constexpr unsigned int kYHistogramSize = 16;\n> +\t/**\n> +\t * \\brief A histogram of luminance values.\n> +\t */\n> +\tstd::array<unsigned int, kYHistogramSize> yHistogram;\n\nSame comments here.\n\n> +};\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index ce31180b..a3b12bc1 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -70,6 +70,7 @@ subdir('ipa')\n>  subdir('pipeline')\n>  subdir('proxy')\n>  subdir('sensor')\n> +subdir('software_isp')\n>  \n>  null_dep = dependency('', required : false)\n>  \n> diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build\n> new file mode 100644\n> index 00000000..fcfff74a\n> --- /dev/null\n> +++ b/src/libcamera/software_isp/meson.build\n> @@ -0,0 +1,12 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +softisp_enabled = pipelines.contains('simple')\n> +summary({'SoftISP support' : softisp_enabled}, section : 'Configuration')\n> +\n> +if not (softisp_enabled)\n\nNo need for parentheses.\n\n> +    subdir_done()\n> +endif\n> +\n> +libcamera_sources += files([\n> +    'swstats_cpu.cpp',\n> +])\n> diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp\n> new file mode 100644\n> index 00000000..448d0e4c\n> --- /dev/null\n> +++ b/src/libcamera/software_isp/swstats_cpu.cpp\n> @@ -0,0 +1,208 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2023, Linaro Ltd\n> + * Copyright (C) 2023, Red Hat Inc.\n> + *\n> + * Authors:\n> + * Hans de Goede <hdegoede@redhat.com>\n> + *\n> + * swstats_cpu.cpp - CPU based software statistics implementation\n> + */\n> +\n> +#include \"swstats_cpu.h\"\n> +\n> +#include <libcamera/base/log.h>\n> +\n> +#include <libcamera/stream.h>\n> +\n> +#include \"libcamera/internal/bayer_format.h\"\n> +\n> +namespace libcamera {\n> +\n> +/**\n> + * \\class SwStatsCpu\n> + * \\brief Class for gathering statistics on the CPU\n> + *\n> + * CPU based software ISP statistics implementation.\n> + *\n> + * This class offers a configure function + functions to gather statistics\n> + * on a line by line basis. This allows CPU based software debayering to\n> + * interlace debayering and statistics gathering on a line by line basis\n\ns/interlace/interleave/\n\nand while at it you can reflow to 80 columns :-) Same below.\n\n> + * while the input data is still hot in the cache.\n> + *\n> + * It is also possible to specify a window over which to gather\n> + * statistics instead of processing the whole frame.\n> + */\n> +\n> +LOG_DEFINE_CATEGORY(SwStatsCpu)\n> +\n> +SwStatsCpu::SwStatsCpu()\n\n\t: sharedStats_(\"softIsp_stats\");\n\n> +{\n> +\tsharedStats_ = SharedMemObject<SwIspStats>(\"softIsp_stats\");\n\nAnd drop this line.\n\n> +\tif (!sharedStats_.fd().isValid())\n\nI think you can simply write\n\n\tif (!sharedStats_)\n\n> +\t\tLOG(SwStatsCpu, Error)\n> +\t\t\t<< \"Failed to create shared memory for statistics\";\n> +}\n> +\n> +static const unsigned int kRedYMul = 77; /* 0.299 * 256 */\n\nconstexpr\n\n> +static const unsigned int kGreenYMul = 150; /* 0.587 * 256 */\n> +static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */\n> +\n> +#define SWSTATS_START_LINE_STATS(pixel_t) \\\n> +\tpixel_t r, g, g2, b;              \\\n> +\tunsigned int yVal;                \\\n> +                                          \\\n> +\tunsigned int sumR = 0;            \\\n> +\tunsigned int sumG = 0;            \\\n> +\tunsigned int sumB = 0;\n> +\n> +#define SWSTATS_ACCUMULATE_LINE_STATS(div) \\\n> +\tsumR += r;                         \\\n> +\tsumG += g;                         \\\n> +\tsumB += b;                         \\\n> +                                           \\\n> +\tyVal = r * kRedYMul;               \\\n> +\tyVal += g * kGreenYMul;            \\\n> +\tyVal += b * kBlueYMul;             \\\n> +\tstats_.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++;\n> +\n> +#define SWSTATS_FINISH_LINE_STATS() \\\n> +\tstats_.sumR_ += sumR;       \\\n> +\tstats_.sumG_ += sumG;       \\\n> +\tstats_.sumB_ += sumB;\n> +\n> +void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])\n> +{\n> +\tconst uint8_t *src0 = src[1] + window_.x * 5 / 4;\n> +\tconst uint8_t *src1 = src[2] + window_.x * 5 / 4;\n> +\tconst int widthInBytes = window_.width * 5 / 4;\n> +\n> +\tif (swapLines_)\n> +\t\tstd::swap(src0, src1);\n> +\n> +\tSWSTATS_START_LINE_STATS(uint8_t)\n> +\n> +\t/* x += 5 sample every other 2x2 block */\n> +\tfor (int x = 0; x < widthInBytes; x += 5) {\n> +\t\t/* BGGR */\n> +\t\tb = src0[x];\n> +\t\tg = src0[x + 1];\n> +\t\tg2 = src1[x];\n> +\t\tr = src1[x + 1];\n> +\t\tg = (g + g2) / 2;\n> +\t\t/* Data is already 8 bits, divide by 1 */\n> +\t\tSWSTATS_ACCUMULATE_LINE_STATS(1)\n> +\t}\n> +\n> +\tSWSTATS_FINISH_LINE_STATS()\n> +}\n> +\n> +void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])\n> +{\n> +\tconst uint8_t *src0 = src[1] + window_.x * 5 / 4;\n> +\tconst uint8_t *src1 = src[2] + window_.x * 5 / 4;\n> +\tconst int widthInBytes = window_.width * 5 / 4;\n> +\n> +\tif (swapLines_)\n> +\t\tstd::swap(src0, src1);\n> +\n> +\tSWSTATS_START_LINE_STATS(uint8_t)\n> +\n> +\t/* x += 5 sample every other 2x2 block */\n> +\tfor (int x = 0; x < widthInBytes; x += 5) {\n> +\t\t/* GBRG */\n> +\t\tg = src0[x];\n> +\t\tb = src0[x + 1];\n> +\t\tr = src1[x];\n> +\t\tg2 = src1[x + 1];\n> +\t\tg = (g + g2) / 2;\n> +\t\t/* Data is already 8 bits, divide by 1 */\n> +\t\tSWSTATS_ACCUMULATE_LINE_STATS(1)\n> +\t}\n> +\n> +\tSWSTATS_FINISH_LINE_STATS()\n> +}\n> +\n> +/**\n> + * \\brief Reset state to start statistics gathering for a new frame.\n> + *\n> + * This may only be called after a successful setWindow() call.\n\nIt would be nice to design the API in a way that makes this kind of\nlogic error impossible, or at least more difficult.\n\n> + */\n> +void SwStatsCpu::startFrame(void)\n> +{\n> +\tstats_.sumR_ = 0;\n> +\tstats_.sumB_ = 0;\n> +\tstats_.sumG_ = 0;\n> +\tstats_.yHistogram.fill(0);\n> +}\n> +\n> +/**\n> + * \\brief Finish statistics calculation for the current frame.\n> + *\n> + * This may only be called after a successful setWindow() call.\n> + */\n> +void SwStatsCpu::finishFrame(void)\n> +{\n> +\t*sharedStats_ = stats_;\n\nIs it more efficient to copy the stats instead of operating directly on\nthe shared memory ?\n\n> +\tstatsReady.emit(0);\n> +}\n> +\n> +/**\n> + * \\brief Configure the statistics object for the passed in input format.\n> + * \\param[in] inputCfg The input format\n> + *\n> + * \\return 0 on success, a negative errno value on failure\n> + */\n> +int SwStatsCpu::configure(const StreamConfiguration &inputCfg)\n> +{\n> +\tBayerFormat bayerFormat =\n> +\t\tBayerFormat::fromPixelFormat(inputCfg.pixelFormat);\n> +\n> +\tif (bayerFormat.bitDepth == 10 &&\n> +\t    bayerFormat.packing == BayerFormat::Packing::CSI2) {\n> +\t\tpatternSize_.height = 2;\n> +\t\tpatternSize_.width = 4; /* 5 bytes per *4* pixels */\n> +\t\t/* Skip every 3th and 4th line, sample every other 2x2 block */\n> +\t\tySkipMask_ = 0x02;\n> +\t\txShift_ = 0;\n> +\n> +\t\tswitch (bayerFormat.order) {\n> +\t\tcase BayerFormat::BGGR:\n> +\t\tcase BayerFormat::GRBG:\n> +\t\t\tstats0_ = &SwStatsCpu::statsBGGR10PLine0;\n> +\t\t\tswapLines_ = bayerFormat.order == BayerFormat::GRBG;\n> +\t\t\treturn 0;\n> +\t\tcase BayerFormat::GBRG:\n> +\t\tcase BayerFormat::RGGB:\n> +\t\t\tstats0_ = &SwStatsCpu::statsGBRG10PLine0;\n> +\t\t\tswapLines_ = bayerFormat.order == BayerFormat::RGGB;\n> +\t\t\treturn 0;\n> +\t\tdefault:\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +\n> +\tLOG(SwStatsCpu, Info)\n> +\t\t<< \"Unsupported input format \" << inputCfg.pixelFormat.toString();\n> +\treturn -EINVAL;\n> +}\n> +\n> +/**\n> + * \\brief Specify window coordinates over which to gather statistics.\n> + * \\param[in] window The window object.\n> + */\n> +void SwStatsCpu::setWindow(Rectangle window)\n> +{\n> +\twindow_ = window;\n> +\n> +\twindow_.x &= ~(patternSize_.width - 1);\n> +\twindow_.x += xShift_;\n> +\twindow_.y &= ~(patternSize_.height - 1);\n> +\n> +\t/* width_ - xShift_ to make sure the window fits */\n> +\twindow_.width -= xShift_;\n> +\twindow_.width &= ~(patternSize_.width - 1);\n> +\twindow_.height &= ~(patternSize_.height - 1);\n> +}\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h\n> new file mode 100644\n> index 00000000..0ac9ae71\n> --- /dev/null\n> +++ b/src/libcamera/software_isp/swstats_cpu.h\n> @@ -0,0 +1,159 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2023, Linaro Ltd\n> + * Copyright (C) 2023, Red Hat Inc.\n> + *\n> + * Authors:\n> + * Hans de Goede <hdegoede@redhat.com>\n> + *\n> + * swstats_cpu.h - CPU based software statistics implementation\n> + */\n> +\n> +#pragma once\n> +\n> +#include <stdint.h>\n> +\n> +#include <libcamera/base/signal.h>\n> +\n> +#include <libcamera/geometry.h>\n> +\n> +#include \"libcamera/internal/shared_mem_object.h\"\n> +#include \"libcamera/internal/software_isp/swisp_stats.h\"\n> +\n> +namespace libcamera {\n> +\n> +class PixelFormat;\n> +struct StreamConfiguration;\n> +\n> +class SwStatsCpu\n> +{\n> +public:\n> +\tSwStatsCpu();\n> +\t~SwStatsCpu() = default;\n> +\n> +\t/**\n> +\t * \\brief Gets whether the statistics object is valid.\n> +\t *\n> +\t * \\return true if it's valid, false otherwise\n> +\t */\n> +\tbool isValid() const { return sharedStats_.fd().isValid(); }\n> +\n> +\t/**\n> +\t * \\brief Get the file descriptor for the statistics.\n> +\t *\n> +\t * \\return the file descriptor\n> +\t */\n> +\tconst SharedFD &getStatsFD() { return sharedStats_.fd(); }\n> +\n> +\t/**\n> +\t * \\brief Get the pattern size.\n> +\t *\n> +\t * For some input-formats, e.g. Bayer data, processing is done multiple lines\n> +\t * and/or columns at a time. Get width and height at which the (bayer) pattern\n> +\t * repeats. Window values are rounded down to a multiple of this and the height\n> +\t * also indicates if processLine2() should be called or not.\n> +\t * This may only be called after a successful configure() call.\n> +\t *\n> +\t * \\return the pattern size\n> +\t */\n> +\tconst Size &patternSize() { return patternSize_; }\n> +\n> +\tint configure(const StreamConfiguration &inputCfg);\n> +\tvoid setWindow(Rectangle window);\n\nconst Rectangle &window\n\nAny reason to separate the two functions ?\n\n> +\tvoid startFrame();\n> +\tvoid finishFrame();\n> +\n> +\t/**\n> +\t * \\brief Process line 0.\n> +\t * \\param[in] y The y coordinate.\n> +\t * \\param[in] src The input data.\n> +\t *\n> +\t * This function processes line 0 for input formats with patternSize height == 1.\n> +\t * It'll process line 0 and 1 for input formats with patternSize height >= 2.\n> +\t * This function may only be called after a successful setWindow() call.\n> +\t */\n> +\tvoid processLine0(unsigned int y, const uint8_t *src[])\n> +\t{\n> +\t\tif ((y & ySkipMask_) || y < (unsigned int)window_.y ||\n\nC++ casts please.\n\n> +\t\t    y >= (window_.y + window_.height))\n> +\t\t\treturn;\n> +\n> +\t\t(this->*stats0_)(src);\n> +\t}\n\nDoes making the function inline improve performances ?\n\n> +\n> +\t/**\n> +\t * \\brief Process line 2 and 3.\n> +\t * \\param[in] y The y coordinate.\n> +\t * \\param[in] src The input data.\n> +\t *\n> +\t * This function processes line 2 and 3 for input formats with patternSize height == 4.\n> +\t * This function may only be called after a successful setWindow() call.\n> +\t */\n> +\tvoid processLine2(unsigned int y, const uint8_t *src[])\n> +\t{\n> +\t\tif ((y & ySkipMask_) || y < (unsigned int)window_.y ||\n> +\t\t    y >= (window_.y + window_.height))\n> +\t\t\treturn;\n> +\n> +\t\t(this->*stats2_)(src);\n> +\t}\n> +\n> +\t/**\n> +\t * \\brief Signals that the statistics are ready.\n> +\t *\n> +\t * The int parameter isn't actually used.\n\nThen drop it :-)\n\n\tSignal<> statsReady;\n\nis perfectly valid.\n\nBut better, I wonder if the signal could be dropped completely. The\nSwStatsCpu class does not operate asynchronously. Shouldn't whoever\ncalls the finishFrame() function then handle emitting the signal ?\n\nNow, the trouble is that this would be the DebayerCpu class, whose name\ndoesn't indicate as a prime candidate to handle stats. However, it\nalready exposes a getStatsFD() function, so we're already calling for\ntrouble :-) Either that should be moved to somewhere else, or the class\nshould be renamed. Considering that the class applies colour gains in\naddition to performing the interpolation, it may be more of a naming\nissue.\n\nRemoving the signal and refactoring those classes doesn't have to be\naddressed now, I think it would be part of a larger refactoring\n(possibly also considering platforms that have no ISP but can produce\nstats in hardware, such as the i.MX7), but please keep it on your radar.\n\n> +\t */\n> +\tSignal<int> statsReady;\n> +\n> +private:\n> +\t/**\n> +\t * \\brief Called when there is data to get statistics from.\n> +\t * \\param[in] src The input data\n> +\t *\n> +\t * These functions take an array of (patternSize_.height + 1) src\n> +\t * pointers each pointing to a line in the source image. The middle\n> +\t * element of the array will point to the actual line being processed.\n> +\t * Earlier element(s) will point to the previous line(s) and later\n> +\t * element(s) to the next line(s).\n> +\t *\n> +\t * See the documentation of DebayerCpu::debayerFn for more details.\n> +\t */\n> +\tusing statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);\n> +\n> +\tvoid statsBGGR10PLine0(const uint8_t *src[]);\n> +\tvoid statsGBRG10PLine0(const uint8_t *src[]);\n> +\n> +\t/* Variables set by configure(), used every line */\n> +\tstatsProcessFn stats0_;\n> +\tstatsProcessFn stats2_;\n> +\tbool swapLines_;\n> +\n> +\t/**\n> +\t * \\brief Skip lines where this bitmask is set in y.\n> +\t */\n> +\tunsigned int ySkipMask_;\n> +\n> +\t/**\n> +\t * \\brief Statistics window, set by setWindow(), used ever line.\n\ns/ever/every/\n\n> +\t */\n> +\tRectangle window_;\n> +\n> +\t/**\n> +\t * \\brief The size of the bayer pattern.\n> +\t *\n> +\t * Valid sizes are: 2x2, 4x2 or 4x4.\n> +\t */\n> +\tSize patternSize_;\n> +\n> +\t/**\n> +\t * \\brief The offset of x, applied to window_.x for bayer variants.\n> +\t *\n> +\t * This can either be 0 or 1.\n> +\t */\n> +\tunsigned int xShift_;\n> +\n> +\tSharedMemObject<SwIspStats> sharedStats_;\n> +\tSwIspStats stats_;\n> +};\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 60043C0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 27 Mar 2024 13:51:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9BC996308D;\n\tWed, 27 Mar 2024 14:51:39 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EE32761C41\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 27 Mar 2024 14:51:37 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 15BEA675;\n\tWed, 27 Mar 2024 14:51:05 +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=\"rhPob1sS\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1711547465;\n\tbh=+UCkXma5N4BsUqfuDXziEcmYh9IX/TkH3j5lNm+B4Dc=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=rhPob1sSi1HnWEpEQ3uWofIMqsN1q3/i83VsJLneh2BfZnJmQTO7B6uxlUCRpuP8u\n\tVzDYj4BEPa7NCUEeHTiUgKHklM9FdlFpb/1LLRJjBt5jCyoV04AFxfWfug2J0wdXbr\n\t6LiZQQYy+oR1IaQMGAbD/Vko/LcsFCszI1a2zZsQ=","Date":"Wed, 27 Mar 2024 15:51:29 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Milan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org, Hans de Goede <hdegoede@redhat.com>,\n\tAndrey Konovalov <andrey.konovalov.ynk@gmail.com>,\n\tBryan O'Donoghue <bryan.odonoghue@linaro.org>,\n\tMaxime Ripard <mripard@redhat.com>, Pavel Machek <pavel@ucw.cz>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tAndrey Konovalov <andrey.konovalov@linaro.org>,\n\tDennis Bonke <admin@dennisbonke.com>, Marttico <g.martti@gmail.com>, \n\tToon Langendam <t.langendam@gmail.com>","Subject":"Re: [PATCH v6 06/18] libcamera: software_isp: Add SwStatsCpu class","Message-ID":"<20240327135129.GD4721@pendragon.ideasonboard.com>","References":"<20240319123622.675599-1-mzamazal@redhat.com>\n\t<20240319123622.675599-7-mzamazal@redhat.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20240319123622.675599-7-mzamazal@redhat.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":29090,"web_url":"https://patchwork.libcamera.org/comment/29090/","msgid":"<a7acf25f-5bcb-4c7f-87bc-05269e1b13ce@redhat.com>","date":"2024-03-27T15:18:45","subject":"Re: [PATCH v6 06/18] libcamera: software_isp: Add SwStatsCpu class","submitter":{"id":102,"url":"https://patchwork.libcamera.org/api/people/102/","name":"Hans de Goede","email":"hdegoede@redhat.com"},"content":"Hi Laurent,\n\nOn 3/27/24 2:51 PM, Laurent Pinchart wrote:\n> Hi Milan and Hans,\n> \n> Thank you for the patch.\n> \n> I understand all this is work in progress, so I won't comment on all the\n> improvements we could add in the future, but focus on merging this as\n> quickly as possible (aiming for one last new version for the series to\n> address simple issues only).\n\nThank you for the review. Milan is going to prepare a v7 of this\nseries, so I'm going to <snip> all the trivial remarks and only\ncomment on the remaining remarks.\n\n> \n> On Tue, Mar 19, 2024 at 01:35:53PM +0100, Milan Zamazal wrote:\n>> From: Hans de Goede <hdegoede@redhat.com>\n>>\n>> Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.\n>>\n>> This implementation offers a configure function + functions to gather\n>> statistics on a line by line basis. This allows CPU based software\n>> debayering to call into interlace debayering and statistics gathering\n> \n> I think you meant interleave, not interlace.\n\nAck.\n\n<snip>\n\n>> +struct SwIspStats {\n>> +\t/**\n>> +\t * \\brief Holds the sum of all sampled red pixels.\n> \n> Does it wrap around or saturate in case of overflow ?\n\nWrap, the idea being that it needs to be big enough to\nhandle max-pixel-value * number-of-samples pixels.\n\nSaturate would require an conditional for each samples\npixel and benchmarking has shown that adding any conditionals\nwhich run for each pixel greatly slow things down.\n\n> \n>> +\t */\n>> +\tunsigned long sumR_;\n> \n> Any reason for using a platform-dependent type instead of uint32_t or\n> uint64_t ?\n\nNot really, the sums really should be uint64_t-s to avoid\nthe mentioned wrapping. Milan can you change the type here\nto uint64_t please.\n\n\n> \n>> +\t/**\n>> +\t * \\brief Holds the sum of all sampled green pixels.\n>> +\t */\n>> +\tunsigned long sumG_;\n>> +\t/**\n>> +\t * \\brief Holds the sum of all sampled blue pixels.\n>> +\t */\n>> +\tunsigned long sumB_;\n>> +\t/**\n>> +\t * \\brief Number of bins in the yHistogram.\n>> +\t */\n>> +\tstatic constexpr unsigned int kYHistogramSize = 16;\n>> +\t/**\n>> +\t * \\brief A histogram of luminance values.\n>> +\t */\n>> +\tstd::array<unsigned int, kYHistogramSize> yHistogram;\n> \n> Same comments here.\n\nThis also wraps, but since a single bin is increased by 1\nfor a single pixel in the worst case scenario a single bin\ngets increased to the total amount of sampled pixels which\nwill easily fit in an uint32_t, and this is an array so\nsize matters (for cacheline usage) so lets make this\nan uint32_t array.\n\n>> +/**\n>> + * \\brief Reset state to start statistics gathering for a new frame.\n>> + *\n>> + * This may only be called after a successful setWindow() call.\n> \n> It would be nice to design the API in a way that makes this kind of\n> logic error impossible, or at least more difficult.\n\nAck, I guess we can check for an empty window and then log\nan error?\n\n> \n>> + */\n>> +void SwStatsCpu::startFrame(void)\n>> +{\n>> +\tstats_.sumR_ = 0;\n>> +\tstats_.sumB_ = 0;\n>> +\tstats_.sumG_ = 0;\n>> +\tstats_.yHistogram.fill(0);\n>> +}\n>> +\n>> +/**\n>> + * \\brief Finish statistics calculation for the current frame.\n>> + *\n>> + * This may only be called after a successful setWindow() call.\n>> + */\n>> +void SwStatsCpu::finishFrame(void)\n>> +{\n>> +\t*sharedStats_ = stats_;\n> \n> Is it more efficient to copy the stats instead of operating directly on\n> the shared memory ?\n\nI inherited doing things this way from Andrey. I kept this because\nwe don't really have any synchronization with the IPA reading this.\n\nSo the idea is to only touch this when the next set of statistics\nis ready since we don't know when the IPA is done with accessing\nthe previous set of statistics ...\n\nThis is both something which seems mostly a theoretic problem,\nyet also definitely something which I think we need to fix.\n\nMaybe use a ringbuffer of stats buffers and pass the index into\nthe ringbuffer to the emit signal ?\n\nMilan can you add a SoftwareISP-TODO document for the next\nversion and mention this as something which we need to look at\nin the future ?\n\n> \n>> +\tstatsReady.emit(0);\n\n<snip>\n\n\n>> +\tint configure(const StreamConfiguration &inputCfg);\n>> +\tvoid setWindow(Rectangle window);\n> \n> const Rectangle &window\n> \n> Any reason to separate the two functions ?\n\nThe inputCfg gets set once before starting streaming, the window\ncan be updated on the fly, to e.g. track a face (if we ever add\nface tracking).\n\n> \n>> +\tvoid startFrame();\n>> +\tvoid finishFrame();\n>> +\n>> +\t/**\n>> +\t * \\brief Process line 0.\n>> +\t * \\param[in] y The y coordinate.\n>> +\t * \\param[in] src The input data.\n>> +\t *\n>> +\t * This function processes line 0 for input formats with patternSize height == 1.\n>> +\t * It'll process line 0 and 1 for input formats with patternSize height >= 2.\n>> +\t * This function may only be called after a successful setWindow() call.\n>> +\t */\n>> +\tvoid processLine0(unsigned int y, const uint8_t *src[])\n>> +\t{\n>> +\t\tif ((y & ySkipMask_) || y < (unsigned int)window_.y ||\n> \n> C++ casts please.\n> \n>> +\t\t    y >= (window_.y + window_.height))\n>> +\t\t\treturn;\n>> +\n>> +\t\t(this->*stats0_)(src);\n>> +\t}\n> \n> Does making the function inline improve performances ?\n\nThat is the idea yes. I've not measured this separately but this\nwas part of a bigger rework for performance reasons and that\nentire rework did speed up things by a factor 2x - 3x .\n\n\n> \n>> +\n>> +\t/**\n>> +\t * \\brief Process line 2 and 3.\n>> +\t * \\param[in] y The y coordinate.\n>> +\t * \\param[in] src The input data.\n>> +\t *\n>> +\t * This function processes line 2 and 3 for input formats with patternSize height == 4.\n>> +\t * This function may only be called after a successful setWindow() call.\n>> +\t */\n>> +\tvoid processLine2(unsigned int y, const uint8_t *src[])\n>> +\t{\n>> +\t\tif ((y & ySkipMask_) || y < (unsigned int)window_.y ||\n>> +\t\t    y >= (window_.y + window_.height))\n>> +\t\t\treturn;\n>> +\n>> +\t\t(this->*stats2_)(src);\n>> +\t}\n>> +\n>> +\t/**\n>> +\t * \\brief Signals that the statistics are ready.\n>> +\t *\n>> +\t * The int parameter isn't actually used.\n> \n> Then drop it :-)\n> \n> \tSignal<> statsReady;\n> \n> is perfectly valid.\n\nAh so just empty? This was remarked on a previous version (possibly by you?)\nand I think I tried adding <void> as the more of a C-programmer that I am\nand that definitely did not work.\n\nMilan can you give this a try.\n\n> But better, I wonder if the signal could be dropped completely. The\n> SwStatsCpu class does not operate asynchronously. Shouldn't whoever\n> calls the finishFrame() function then handle emitting the signal ?\n> \n> Now, the trouble is that this would be the DebayerCpu class, whose name\n> doesn't indicate as a prime candidate to handle stats. However, it\n> already exposes a getStatsFD() function, so we're already calling for\n> trouble :-) Either that should be moved to somewhere else, or the class\n> should be renamed. Considering that the class applies colour gains in\n> addition to performing the interpolation, it may be more of a naming\n> issue.\n> \n> Removing the signal and refactoring those classes doesn't have to be\n> addressed now, I think it would be part of a larger refactoring\n> (possibly also considering platforms that have no ISP but can produce\n> stats in hardware, such as the i.MX7), but please keep it on your radar.\n\nRight, I think this is all stuff to add to the SoftwareISP-TODO doc\nwhich I suggested above.\n\nRegards,\n\nHans","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 6A576BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 27 Mar 2024 15:18:57 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4F55163331;\n\tWed, 27 Mar 2024 16:18:56 +0100 (CET)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C101C61C35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 27 Mar 2024 16:18:54 +0100 (CET)","from mail-ej1-f70.google.com (mail-ej1-f70.google.com\n\t[209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-383-nAYjk1_OM0OwCKURmcznqQ-1; Wed, 27 Mar 2024 11:18:51 -0400","by mail-ej1-f70.google.com with SMTP id\n\ta640c23a62f3a-a474c52817fso169228766b.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 27 Mar 2024 08:18:51 -0700 (PDT)","from ?IPV6:2001:1c00:c32:7800:5bfa:a036:83f0:f9ec?\n\t(2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl.\n\t[2001:1c00:c32:7800:5bfa:a036:83f0:f9ec])\n\tby smtp.gmail.com with ESMTPSA id\n\tjs23-20020a170906ca9700b00a4df3ea61a9sm2033360ejb.57.2024.03.27.08.18.46\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tWed, 27 Mar 2024 08:18:46 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"Y7nM85TD\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1711552733;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=PZiCw01VZc6FlUin9aGmavx8aR0Wy181k158bgdHSBg=;\n\tb=Y7nM85TDeGGHT/8MUeu2nh/p01uklbb/7ngvUJXfHTVhGZPyCeWmpIL8FxEp87RDsW3KAW\n\tWGV6Q+gjfr0WV0MkfYRLvTneMA5taao7IPJANSDE7KtnSOWeG4qFTNa/D0IijGwF4618vC\n\t+OBG45BB2BcAhZTGZp3Pf+JlScVKnrU=","X-MC-Unique":"nAYjk1_OM0OwCKURmcznqQ-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1711552730; x=1712157530;\n\th=content-transfer-encoding:in-reply-to:from:references:cc:to\n\t:content-language:subject:user-agent:mime-version:date:message-id\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=PZiCw01VZc6FlUin9aGmavx8aR0Wy181k158bgdHSBg=;\n\tb=bMbmw/j6Eu4MPhdDdc+KfMsxYjjkFfp6hCy7CK4z5tc+Hlg/UOFwDgsdmytzm7wavV\n\tWDRCJDPSPpz8VEybbmc8kJC2hdznAUxdhGcusm81LHhnqWNr51wOuWVQeGm0FE/3HmEn\n\ti4HAsJkZk56pgx1tdvtefXt99kJNW6R2zYUn56GqYhwBOFeH0DmZXR1Zho6q3a0mhRdE\n\t8OIkpI//ycJlQQ+66ng+BU0ifTbzESUvMkcDKHdwbv0USXfv3cPSLOqtn58Siyww3huF\n\tW/vrC3FrODB6k71fioE1Pm6fj2sP8xTSBu6yzjmuUO4GHi/vtsCuN/UbzRPSTUTqrecN\n\t5M/Q==","X-Gm-Message-State":"AOJu0YxVkwQtWGYPHI4JGy7mRlbx42piZ/w8Yz8HZiSEJgJzQFWihTdD\n\t4bDp0k+bAU3pQZuSHh3Td4NGYZ9+FJAC+sGCLeHpV43GtUhsYkIqmL3SGJBguXyYgNm2dfFvFCO\n\t1VvdwCpgQ/B5+0GJ8msthnFRn3wI1KzU6bim9VC7sPpKSVJmiE1E/0ReQ+WQ709LdS5E1zj0=","X-Received":["by 2002:a17:906:3747:b0:a45:ad29:725c with SMTP id\n\te7-20020a170906374700b00a45ad29725cmr1144268ejc.62.1711552730122; \n\tWed, 27 Mar 2024 08:18:50 -0700 (PDT)","by 2002:a17:906:3747:b0:a45:ad29:725c with SMTP id\n\te7-20020a170906374700b00a45ad29725cmr1144161ejc.62.1711552726999; \n\tWed, 27 Mar 2024 08:18:46 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IHF/FXqd3azYvTerAhvPd0wn7mCOLd3CMehkRdMjbpPIrJz9GregJ16KXXO07fV0SBsVSJPWA==","Message-ID":"<a7acf25f-5bcb-4c7f-87bc-05269e1b13ce@redhat.com>","Date":"Wed, 27 Mar 2024 16:18:45 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v6 06/18] libcamera: software_isp: Add SwStatsCpu class","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tMilan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tAndrey Konovalov <andrey.konovalov.ynk@gmail.com>,\n\tBryan O'Donoghue <bryan.odonoghue@linaro.org>,\n\tMaxime Ripard <mripard@redhat.com>, Pavel Machek <pavel@ucw.cz>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tAndrey Konovalov <andrey.konovalov@linaro.org>,\n\tDennis Bonke <admin@dennisbonke.com>, Marttico <g.martti@gmail.com>, \n\tToon Langendam <t.langendam@gmail.com>","References":"<20240319123622.675599-1-mzamazal@redhat.com>\n\t<20240319123622.675599-7-mzamazal@redhat.com>\n\t<20240327135129.GD4721@pendragon.ideasonboard.com>","From":"Hans de Goede <hdegoede@redhat.com>","In-Reply-To":"<20240327135129.GD4721@pendragon.ideasonboard.com>","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Content-Language":"en-US, nl","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"7bit","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":29111,"web_url":"https://patchwork.libcamera.org/comment/29111/","msgid":"<20240327192505.GQ4721@pendragon.ideasonboard.com>","date":"2024-03-27T19:25:05","subject":"Re: [PATCH v6 06/18] libcamera: software_isp: Add SwStatsCpu class","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Wed, Mar 27, 2024 at 04:18:45PM +0100, Hans de Goede wrote:\n> On 3/27/24 2:51 PM, Laurent Pinchart wrote:\n> > Hi Milan and Hans,\n> > \n> > Thank you for the patch.\n> > \n> > I understand all this is work in progress, so I won't comment on all the\n> > improvements we could add in the future, but focus on merging this as\n> > quickly as possible (aiming for one last new version for the series to\n> > address simple issues only).\n> \n> Thank you for the review. Milan is going to prepare a v7 of this\n> series, so I'm going to <snip> all the trivial remarks and only\n> comment on the remaining remarks.\n> \n> > On Tue, Mar 19, 2024 at 01:35:53PM +0100, Milan Zamazal wrote:\n> >> From: Hans de Goede <hdegoede@redhat.com>\n> >>\n> >> Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.\n> >>\n> >> This implementation offers a configure function + functions to gather\n> >> statistics on a line by line basis. This allows CPU based software\n> >> debayering to call into interlace debayering and statistics gathering\n> > \n> > I think you meant interleave, not interlace.\n> \n> Ack.\n> \n> <snip>\n> \n> >> +struct SwIspStats {\n> >> +\t/**\n> >> +\t * \\brief Holds the sum of all sampled red pixels.\n> > \n> > Does it wrap around or saturate in case of overflow ?\n> \n> Wrap, the idea being that it needs to be big enough to\n> handle max-pixel-value * number-of-samples pixels.\n> \n> Saturate would require an conditional for each samples\n> pixel and benchmarking has shown that adding any conditionals\n> which run for each pixel greatly slow things down.\n\nI'm fine with wrap-around, but let's document it.\n\n> > \n> >> +\t */\n> >> +\tunsigned long sumR_;\n> > \n> > Any reason for using a platform-dependent type instead of uint32_t or\n> > uint64_t ?\n> \n> Not really, the sums really should be uint64_t-s to avoid\n> the mentioned wrapping. Milan can you change the type here\n> to uint64_t please.\n> \n> >> +\t/**\n> >> +\t * \\brief Holds the sum of all sampled green pixels.\n> >> +\t */\n> >> +\tunsigned long sumG_;\n> >> +\t/**\n> >> +\t * \\brief Holds the sum of all sampled blue pixels.\n> >> +\t */\n> >> +\tunsigned long sumB_;\n> >> +\t/**\n> >> +\t * \\brief Number of bins in the yHistogram.\n> >> +\t */\n> >> +\tstatic constexpr unsigned int kYHistogramSize = 16;\n> >> +\t/**\n> >> +\t * \\brief A histogram of luminance values.\n> >> +\t */\n> >> +\tstd::array<unsigned int, kYHistogramSize> yHistogram;\n> > \n> > Same comments here.\n> \n> This also wraps, but since a single bin is increased by 1\n> for a single pixel in the worst case scenario a single bin\n> gets increased to the total amount of sampled pixels which\n> will easily fit in an uint32_t, and this is an array so\n> size matters (for cacheline usage) so lets make this\n> an uint32_t array.\n> \n> >> +/**\n> >> + * \\brief Reset state to start statistics gathering for a new frame.\n> >> + *\n> >> + * This may only be called after a successful setWindow() call.\n> > \n> > It would be nice to design the API in a way that makes this kind of\n> > logic error impossible, or at least more difficult.\n> \n> Ack, I guess we can check for an empty window and then log\n> an error?\n\nOr maybe set a default window in configure() ?\n\n> >> + */\n> >> +void SwStatsCpu::startFrame(void)\n> >> +{\n> >> +\tstats_.sumR_ = 0;\n> >> +\tstats_.sumB_ = 0;\n> >> +\tstats_.sumG_ = 0;\n> >> +\tstats_.yHistogram.fill(0);\n> >> +}\n> >> +\n> >> +/**\n> >> + * \\brief Finish statistics calculation for the current frame.\n> >> + *\n> >> + * This may only be called after a successful setWindow() call.\n> >> + */\n> >> +void SwStatsCpu::finishFrame(void)\n> >> +{\n> >> +\t*sharedStats_ = stats_;\n> > \n> > Is it more efficient to copy the stats instead of operating directly on\n> > the shared memory ?\n> \n> I inherited doing things this way from Andrey. I kept this because\n> we don't really have any synchronization with the IPA reading this.\n> \n> So the idea is to only touch this when the next set of statistics\n> is ready since we don't know when the IPA is done with accessing\n> the previous set of statistics ...\n> \n> This is both something which seems mostly a theoretic problem,\n> yet also definitely something which I think we need to fix.\n> \n> Maybe use a ringbuffer of stats buffers and pass the index into\n> the ringbuffer to the emit signal ?\n\nThat would match how we deal with hardware ISPs, and I think that's a\ngood idea. It will help decoupling the processing side from the IPA.\n\n> Milan can you add a SoftwareISP-TODO document for the next\n> version and mention this as something which we need to look at\n> in the future ?\n> \n> >> +\tstatsReady.emit(0);\n> \n> <snip>\n> \n> >> +\tint configure(const StreamConfiguration &inputCfg);\n> >> +\tvoid setWindow(Rectangle window);\n> > \n> > const Rectangle &window\n> > \n> > Any reason to separate the two functions ?\n> \n> The inputCfg gets set once before starting streaming, the window\n> can be updated on the fly, to e.g. track a face (if we ever add\n> face tracking).\n\nGood reason.\n\n> >> +\tvoid startFrame();\n> >> +\tvoid finishFrame();\n> >> +\n> >> +\t/**\n> >> +\t * \\brief Process line 0.\n> >> +\t * \\param[in] y The y coordinate.\n> >> +\t * \\param[in] src The input data.\n> >> +\t *\n> >> +\t * This function processes line 0 for input formats with patternSize height == 1.\n> >> +\t * It'll process line 0 and 1 for input formats with patternSize height >= 2.\n> >> +\t * This function may only be called after a successful setWindow() call.\n> >> +\t */\n> >> +\tvoid processLine0(unsigned int y, const uint8_t *src[])\n> >> +\t{\n> >> +\t\tif ((y & ySkipMask_) || y < (unsigned int)window_.y ||\n> > \n> > C++ casts please.\n> > \n> >> +\t\t    y >= (window_.y + window_.height))\n> >> +\t\t\treturn;\n> >> +\n> >> +\t\t(this->*stats0_)(src);\n> >> +\t}\n> > \n> > Does making the function inline improve performances ?\n> \n> That is the idea yes. I've not measured this separately but this\n> was part of a bigger rework for performance reasons and that\n> entire rework did speed up things by a factor 2x - 3x .\n> \n> >> +\n> >> +\t/**\n> >> +\t * \\brief Process line 2 and 3.\n> >> +\t * \\param[in] y The y coordinate.\n> >> +\t * \\param[in] src The input data.\n> >> +\t *\n> >> +\t * This function processes line 2 and 3 for input formats with patternSize height == 4.\n> >> +\t * This function may only be called after a successful setWindow() call.\n> >> +\t */\n> >> +\tvoid processLine2(unsigned int y, const uint8_t *src[])\n> >> +\t{\n> >> +\t\tif ((y & ySkipMask_) || y < (unsigned int)window_.y ||\n> >> +\t\t    y >= (window_.y + window_.height))\n> >> +\t\t\treturn;\n> >> +\n> >> +\t\t(this->*stats2_)(src);\n> >> +\t}\n> >> +\n> >> +\t/**\n> >> +\t * \\brief Signals that the statistics are ready.\n> >> +\t *\n> >> +\t * The int parameter isn't actually used.\n> > \n> > Then drop it :-)\n> > \n> > \tSignal<> statsReady;\n> > \n> > is perfectly valid.\n> \n> Ah so just empty? This was remarked on a previous version (possibly by you?)\n> and I think I tried adding <void> as the more of a C-programmer that I am\n> and that definitely did not work.\n\nYes, if you don't want any argument to the signal just leave it empty. I\ninitially tried to write Signal<void> when implementing the Signal\nclass, but realized that syntax wouldn't work.\n\n> Milan can you give this a try.\n> \n> > But better, I wonder if the signal could be dropped completely. The\n> > SwStatsCpu class does not operate asynchronously. Shouldn't whoever\n> > calls the finishFrame() function then handle emitting the signal ?\n> > \n> > Now, the trouble is that this would be the DebayerCpu class, whose name\n> > doesn't indicate as a prime candidate to handle stats. However, it\n> > already exposes a getStatsFD() function, so we're already calling for\n> > trouble :-) Either that should be moved to somewhere else, or the class\n> > should be renamed. Considering that the class applies colour gains in\n> > addition to performing the interpolation, it may be more of a naming\n> > issue.\n> > \n> > Removing the signal and refactoring those classes doesn't have to be\n> > addressed now, I think it would be part of a larger refactoring\n> > (possibly also considering platforms that have no ISP but can produce\n> > stats in hardware, such as the i.MX7), but please keep it on your radar.\n> \n> Right, I think this is all stuff to add to the SoftwareISP-TODO doc\n> which I suggested above.\n\nYes, I'm fine with that.","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 DA5F2C0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 27 Mar 2024 19:25:16 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 34E4463339;\n\tWed, 27 Mar 2024 20:25:16 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B651C61C41\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 27 Mar 2024 20:25:14 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id BCF69899;\n\tWed, 27 Mar 2024 20:24:41 +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=\"eFOdF/f4\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1711567481;\n\tbh=3aTYHPPelDRpu8HcvuSUuiHx6QoE0EX6+3NuEOr6pMw=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=eFOdF/f4Pv2MvtMKZ46VmCZ3NaWzwGy7mVysz6d2P2InyxcC9nkCS3HrGGp3TMMBe\n\ti8x6dnLEcVvZs3Ca/IAyafg9tlYwoHICiCTnzNtyTeqAZ5BN8ruWb4mhKQ3YZk41B4\n\tfZ13mKQyJnYAO8kkBoEEDRMsZVwNglt13kX0mEYs=","Date":"Wed, 27 Mar 2024 21:25:05 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Hans de Goede <hdegoede@redhat.com>","Cc":"Milan Zamazal <mzamazal@redhat.com>, libcamera-devel@lists.libcamera.org,\n\tAndrey Konovalov <andrey.konovalov.ynk@gmail.com>,\n\tBryan O'Donoghue <bryan.odonoghue@linaro.org>,\n\tMaxime Ripard <mripard@redhat.com>, Pavel Machek <pavel@ucw.cz>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tAndrey Konovalov <andrey.konovalov@linaro.org>,\n\tDennis Bonke <admin@dennisbonke.com>, Marttico <g.martti@gmail.com>, \n\tToon Langendam <t.langendam@gmail.com>","Subject":"Re: [PATCH v6 06/18] libcamera: software_isp: Add SwStatsCpu class","Message-ID":"<20240327192505.GQ4721@pendragon.ideasonboard.com>","References":"<20240319123622.675599-1-mzamazal@redhat.com>\n\t<20240319123622.675599-7-mzamazal@redhat.com>\n\t<20240327135129.GD4721@pendragon.ideasonboard.com>\n\t<a7acf25f-5bcb-4c7f-87bc-05269e1b13ce@redhat.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<a7acf25f-5bcb-4c7f-87bc-05269e1b13ce@redhat.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>"}}]