[{"id":38210,"web_url":"https://patchwork.libcamera.org/comment/38210/","msgid":"<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>","date":"2026-02-17T09:07:26","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"Hi\n\n2026. 02. 16. 20:02 keltezéssel, Hans de Goede írta:\n> Add CPU soft ISP multi-threading support.\n> \n> Benchmark results for the Uno-Q with a weak CPU which is good for\n> performance testing, all numbers with an IMX219 running at\n> 3280x2464 -> 3272x2464:\n> \n> 1 thread : 147ms / frame, ~6.5 fps\n> 2 threads:  81ms / frame, ~12 fps\n> 3 threads:  66ms / frame, ~14.5 fps\n> \n> Adding a 4th thread does not improve performance.\n> \n> Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>\n> ---\n>   src/libcamera/software_isp/debayer_cpu.cpp | 49 +++++++++++++++++-----\n>   src/libcamera/software_isp/debayer_cpu.h   |  2 +-\n>   2 files changed, 40 insertions(+), 11 deletions(-)\n> \n> diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp\n> index 5e168554..c4b6c5b8 100644\n> --- a/src/libcamera/software_isp/debayer_cpu.cpp\n> +++ b/src/libcamera/software_isp/debayer_cpu.cpp\n> @@ -14,6 +14,7 @@\n>   #include <algorithm>\n>   #include <stdlib.h>\n>   #include <sys/ioctl.h>\n> +#include <thread>\n>   #include <time.h>\n>   #include <utility>\n>   \n> @@ -41,7 +42,7 @@ namespace libcamera {\n>    * \\param[in] configuration The global configuration\n>    */\n>   DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfiguration &configuration)\n> -\t: Debayer(configuration), stats_(std::move(stats)), threadCount_(1)\n> +\t: Debayer(configuration), stats_(std::move(stats))\n>   {\n>   \t/*\n>   \t * Reading from uncached buffers may be very slow.\n> @@ -56,6 +57,9 @@ DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfigurat\n>   \t */\n>   \tenableInputMemcpy_ =\n>   \t\tconfiguration.option<bool>({ \"software_isp\", \"copy_input_buffer\" }).value_or(true);\n> +\tthreadCount_ =\n> +\t\tconfiguration.option<unsigned int>({ \"software_isp\", \"threads\" }).value_or(3);\n> +\tthreadCount_ = std::clamp(threadCount_, 1u, kMaxThreads);\n>   }\n>   \n>   DebayerCpu::~DebayerCpu() = default;\n> @@ -692,7 +696,7 @@ void DebayerCpu::process2(uint32_t frame, const uint8_t *src, uint8_t *dst,\n>   \tfor (unsigned int y = threadData->yStart; y < threadData->yEnd; y += 2) {\n>   \t\tshiftLinePointers(linePointers, src);\n>   \t\tmemcpyNextLine(linePointers, threadData);\n> -\t\tstats_->processLine0(frame, y, linePointers, &statsBuffer_);\n> +\t\tstats_->processLine0(frame, y, linePointers, threadData->statsBuffer);\n>   \t\t(this->*debayer0_)(dst, linePointers);\n>   \t\tsrc += inputConfig_.stride;\n>   \t\tdst += outputConfig_.stride;\n> @@ -707,7 +711,8 @@ void DebayerCpu::process2(uint32_t frame, const uint8_t *src, uint8_t *dst,\n>   \tif (threadData->processLastLinesSeperately) {\n>   \t\tshiftLinePointers(linePointers, src);\n>   \t\tmemcpyNextLine(linePointers, threadData);\n> -\t\tstats_->processLine0(frame, threadData->yEnd, linePointers, &statsBuffer_);\n> +\t\tstats_->processLine0(frame, threadData->yEnd, linePointers,\n> +\t\t\t\t     threadData->statsBuffer);\n>   \t\t(this->*debayer0_)(dst, linePointers);\n>   \t\tsrc += inputConfig_.stride;\n>   \t\tdst += outputConfig_.stride;\n> @@ -749,7 +754,7 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst,\n>   \tfor (unsigned int y = threadData->yStart; y < threadData->yEnd; y += 4) {\n>   \t\tshiftLinePointers(linePointers, src);\n>   \t\tmemcpyNextLine(linePointers, threadData);\n> -\t\tstats_->processLine0(frame, y, linePointers, &statsBuffer_);\n> +\t\tstats_->processLine0(frame, y, linePointers, threadData->statsBuffer);\n>   \t\t(this->*debayer0_)(dst, linePointers);\n>   \t\tsrc += inputConfig_.stride;\n>   \t\tdst += outputConfig_.stride;\n> @@ -762,7 +767,7 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst,\n>   \n>   \t\tshiftLinePointers(linePointers, src);\n>   \t\tmemcpyNextLine(linePointers, threadData);\n> -\t\tstats_->processLine2(frame, y, linePointers, &statsBuffer_);\n> +\t\tstats_->processLine2(frame, y, linePointers, threadData->statsBuffer);\n>   \t\t(this->*debayer2_)(dst, linePointers);\n>   \t\tsrc += inputConfig_.stride;\n>   \t\tdst += outputConfig_.stride;\n> @@ -869,6 +874,10 @@ void DebayerCpu::updateLookupTables(const DebayerParams &params)\n>   \n>   void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params)\n>   {\n> +\tstd::unique_ptr<std::thread> threads[threadCount_ - 1];\n\nNo need for the unique_ptr here, just `std::array<std::thread, kMaxThread - 1>` should be sufficient.\n\n\n> +\tSwIspStats statsBuffer[threadCount_];\n> +\tunsigned int i;\n> +\n>   \tbench_.startFrame();\n>   \n>   \tstd::vector<DmaSyncer> dmaSyncers;\n> @@ -891,11 +900,31 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output\n>   \t\treturn;\n>   \t}\n>   \n> -\tstats_->startFrame(frame, &statsBuffer_, 1);\n> +\tstats_->startFrame(frame, statsBuffer, threadCount_);\n>   \n> -\tthreadData_[0].yStart = 0;\n> -\tthreadData_[0].yEnd = window_.height;\n> -\t(this->*processInner_)(frame, in.planes()[0].data(), out.planes()[0].data(), &threadData_[0]);\n> +\tunsigned int yStart = 0;\n> +\tunsigned int linesPerThread = (window_.height / threadCount_) &\n> +\t\t\t\t      ~(inputConfig_.patternSize.width - 1);\n> +\tfor (i = 0; i < (threadCount_ - 1); i++) {\n> +\t\tthreadData_[i].yStart = yStart;\n> +\t\tthreadData_[i].yEnd = yStart + linesPerThread;\n> +\t\tthreadData_[i].statsBuffer = &statsBuffer[i];\n> +\t\tthreads[i] = std::make_unique<std::thread>(\n> +\t\t\t\tprocessInner_, this, frame,\n> +\t\t\t\tin.planes()[0].data(),\n> +\t\t\t\tout.planes()[0].data() + yStart * outputConfig_.stride,\n> +\t\t\t\t&threadData_[i]);\n> +\t\tyStart += linesPerThread;\n> +\t}\n> +\tthreadData_[i].yStart = yStart;\n> +\tthreadData_[i].yEnd = window_.height;\n> +\tthreadData_[i].statsBuffer = &statsBuffer[i];\n> +\t(this->*processInner_)(frame, in.planes()[0].data(),\n> +\t\t\t       out.planes()[0].data() + yStart * outputConfig_.stride,\n> +\t\t\t       &threadData_[i]);\n> +\n> +\tfor (i = 0; i < (threadCount_ - 1); i++)\n> +\t\tthreads[i]->join();\n\nThis is creating and then joining x threads *per frame*, right? I feel like that\ncould be improved. Have you looked at having a set of long-running threads? (Even\nsomething very simple, e.g. just threads with a mutex and condvar, started in start()\nand stopped in stop()?)\n\n\n>   \n>   \tmetadata.planes()[0].bytesused = out.planes()[0].size();\n>   \n> @@ -909,7 +938,7 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output\n>   \t *\n>   \t * \\todo Pass real bufferId once stats buffer passing is changed.\n>   \t */\n> -\tstats_->finishFrame(frame, 0, &statsBuffer_, 1);\n> +\tstats_->finishFrame(frame, 0, statsBuffer, threadCount_);\n>   \toutputBufferReady.emit(output);\n>   \tinputBufferReady.emit(input);\n>   }\n> diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h\n> index b85dd11c..63fa7710 100644\n> --- a/src/libcamera/software_isp/debayer_cpu.h\n> +++ b/src/libcamera/software_isp/debayer_cpu.h\n> @@ -85,6 +85,7 @@ private:\n>   \t\tunsigned int lineBufferIndex;\n>   \t\t/* Stored here to avoid causing register pressure in inner loop */\n>   \t\tbool processLastLinesSeperately;\n> +\t\tSwIspStats *statsBuffer;\n>   \t};\n>   \n>   \tusing processFn = void (DebayerCpu::*)(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> @@ -150,7 +151,6 @@ private:\n>   \tRectangle window_;\n>   \n>   \t/* Variables used every line */\n> -\tSwIspStats statsBuffer_;\n>   \tdebayerFn debayer0_;\n>   \tdebayerFn debayer1_;\n>   \tdebayerFn debayer2_;","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 78AE7C0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 17 Feb 2026 09:07:32 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 61EFF62218;\n\tTue, 17 Feb 2026 10:07:31 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4FA91620C9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 17 Feb 2026 10:07:29 +0100 (CET)","from [192.168.33.77] (185.221.141.206.nat.pool.zt.hu\n\t[185.221.141.206])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B73F44D3;\n\tTue, 17 Feb 2026 10:06: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=\"pAY95vHy\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771319197;\n\tbh=j/FdLLwbqyMdiXOsjbdKLgsWXj0V0/uZ5fz9WMxwhxE=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=pAY95vHyPSULay4tnLJ7ePEqvkz5aKjLHgu2cIA/qqDsbPfi4Zr4G/jo4cwB5Mjl4\n\tTn5pCklSREBd1feqbDVzgBl70hb3mJnbViW0iG2bNk6/7gpF+ytY2Ae1GuhNtbWuk3\n\tfPkovuWtwc6BXkHl8KWrmssFoMvQe4EUk4GlUqCw=","Message-ID":"<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>","Date":"Tue, 17 Feb 2026 10:07:26 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","To":"Hans de Goede <johannes.goede@oss.qualcomm.com>,\n\tlibcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>","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>"}},{"id":38255,"web_url":"https://patchwork.libcamera.org/comment/38255/","msgid":"<20260219141500.GQ520738@killaraus.ideasonboard.com>","date":"2026-02-19T14:15:00","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Tue, Feb 17, 2026 at 10:07:26AM +0100, Barnabás Pőcze wrote:\n> Hi\n> \n> 2026. 02. 16. 20:02 keltezéssel, Hans de Goede írta:\n> > Add CPU soft ISP multi-threading support.\n> > \n> > Benchmark results for the Uno-Q with a weak CPU which is good for\n> > performance testing, all numbers with an IMX219 running at\n> > 3280x2464 -> 3272x2464:\n> > \n> > 1 thread : 147ms / frame, ~6.5 fps\n> > 2 threads:  81ms / frame, ~12 fps\n> > 3 threads:  66ms / frame, ~14.5 fps\n> > \n> > Adding a 4th thread does not improve performance.\n> > \n> > Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>\n> > ---\n> >   src/libcamera/software_isp/debayer_cpu.cpp | 49 +++++++++++++++++-----\n> >   src/libcamera/software_isp/debayer_cpu.h   |  2 +-\n> >   2 files changed, 40 insertions(+), 11 deletions(-)\n> > \n> > diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp\n> > index 5e168554..c4b6c5b8 100644\n> > --- a/src/libcamera/software_isp/debayer_cpu.cpp\n> > +++ b/src/libcamera/software_isp/debayer_cpu.cpp\n> > @@ -14,6 +14,7 @@\n> >   #include <algorithm>\n> >   #include <stdlib.h>\n> >   #include <sys/ioctl.h>\n> > +#include <thread>\n> >   #include <time.h>\n> >   #include <utility>\n> >   \n> > @@ -41,7 +42,7 @@ namespace libcamera {\n> >    * \\param[in] configuration The global configuration\n> >    */\n> >   DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfiguration &configuration)\n> > -\t: Debayer(configuration), stats_(std::move(stats)), threadCount_(1)\n> > +\t: Debayer(configuration), stats_(std::move(stats))\n> >   {\n> >   \t/*\n> >   \t * Reading from uncached buffers may be very slow.\n> > @@ -56,6 +57,9 @@ DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfigurat\n> >   \t */\n> >   \tenableInputMemcpy_ =\n> >   \t\tconfiguration.option<bool>({ \"software_isp\", \"copy_input_buffer\" }).value_or(true);\n> > +\tthreadCount_ =\n> > +\t\tconfiguration.option<unsigned int>({ \"software_isp\", \"threads\" }).value_or(3);\n> > +\tthreadCount_ = std::clamp(threadCount_, 1u, kMaxThreads);\n> >   }\n> >   \n> >   DebayerCpu::~DebayerCpu() = default;\n> > @@ -692,7 +696,7 @@ void DebayerCpu::process2(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> >   \tfor (unsigned int y = threadData->yStart; y < threadData->yEnd; y += 2) {\n> >   \t\tshiftLinePointers(linePointers, src);\n> >   \t\tmemcpyNextLine(linePointers, threadData);\n> > -\t\tstats_->processLine0(frame, y, linePointers, &statsBuffer_);\n> > +\t\tstats_->processLine0(frame, y, linePointers, threadData->statsBuffer);\n> >   \t\t(this->*debayer0_)(dst, linePointers);\n> >   \t\tsrc += inputConfig_.stride;\n> >   \t\tdst += outputConfig_.stride;\n> > @@ -707,7 +711,8 @@ void DebayerCpu::process2(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> >   \tif (threadData->processLastLinesSeperately) {\n> >   \t\tshiftLinePointers(linePointers, src);\n> >   \t\tmemcpyNextLine(linePointers, threadData);\n> > -\t\tstats_->processLine0(frame, threadData->yEnd, linePointers, &statsBuffer_);\n> > +\t\tstats_->processLine0(frame, threadData->yEnd, linePointers,\n> > +\t\t\t\t     threadData->statsBuffer);\n> >   \t\t(this->*debayer0_)(dst, linePointers);\n> >   \t\tsrc += inputConfig_.stride;\n> >   \t\tdst += outputConfig_.stride;\n> > @@ -749,7 +754,7 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> >   \tfor (unsigned int y = threadData->yStart; y < threadData->yEnd; y += 4) {\n> >   \t\tshiftLinePointers(linePointers, src);\n> >   \t\tmemcpyNextLine(linePointers, threadData);\n> > -\t\tstats_->processLine0(frame, y, linePointers, &statsBuffer_);\n> > +\t\tstats_->processLine0(frame, y, linePointers, threadData->statsBuffer);\n> >   \t\t(this->*debayer0_)(dst, linePointers);\n> >   \t\tsrc += inputConfig_.stride;\n> >   \t\tdst += outputConfig_.stride;\n> > @@ -762,7 +767,7 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> >   \n> >   \t\tshiftLinePointers(linePointers, src);\n> >   \t\tmemcpyNextLine(linePointers, threadData);\n> > -\t\tstats_->processLine2(frame, y, linePointers, &statsBuffer_);\n> > +\t\tstats_->processLine2(frame, y, linePointers, threadData->statsBuffer);\n> >   \t\t(this->*debayer2_)(dst, linePointers);\n> >   \t\tsrc += inputConfig_.stride;\n> >   \t\tdst += outputConfig_.stride;\n> > @@ -869,6 +874,10 @@ void DebayerCpu::updateLookupTables(const DebayerParams &params)\n> >   \n> >   void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params)\n> >   {\n> > +\tstd::unique_ptr<std::thread> threads[threadCount_ - 1];\n> \n> No need for the unique_ptr here, just `std::array<std::thread, kMaxThread - 1>` should be sufficient.\n> \n> \n> > +\tSwIspStats statsBuffer[threadCount_];\n> > +\tunsigned int i;\n> > +\n> >   \tbench_.startFrame();\n> >   \n> >   \tstd::vector<DmaSyncer> dmaSyncers;\n> > @@ -891,11 +900,31 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output\n> >   \t\treturn;\n> >   \t}\n> >   \n> > -\tstats_->startFrame(frame, &statsBuffer_, 1);\n> > +\tstats_->startFrame(frame, statsBuffer, threadCount_);\n> >   \n> > -\tthreadData_[0].yStart = 0;\n> > -\tthreadData_[0].yEnd = window_.height;\n> > -\t(this->*processInner_)(frame, in.planes()[0].data(), out.planes()[0].data(), &threadData_[0]);\n> > +\tunsigned int yStart = 0;\n> > +\tunsigned int linesPerThread = (window_.height / threadCount_) &\n> > +\t\t\t\t      ~(inputConfig_.patternSize.width - 1);\n> > +\tfor (i = 0; i < (threadCount_ - 1); i++) {\n> > +\t\tthreadData_[i].yStart = yStart;\n> > +\t\tthreadData_[i].yEnd = yStart + linesPerThread;\n> > +\t\tthreadData_[i].statsBuffer = &statsBuffer[i];\n> > +\t\tthreads[i] = std::make_unique<std::thread>(\n> > +\t\t\t\tprocessInner_, this, frame,\n> > +\t\t\t\tin.planes()[0].data(),\n> > +\t\t\t\tout.planes()[0].data() + yStart * outputConfig_.stride,\n> > +\t\t\t\t&threadData_[i]);\n> > +\t\tyStart += linesPerThread;\n> > +\t}\n> > +\tthreadData_[i].yStart = yStart;\n> > +\tthreadData_[i].yEnd = window_.height;\n> > +\tthreadData_[i].statsBuffer = &statsBuffer[i];\n> > +\t(this->*processInner_)(frame, in.planes()[0].data(),\n> > +\t\t\t       out.planes()[0].data() + yStart * outputConfig_.stride,\n> > +\t\t\t       &threadData_[i]);\n> > +\n> > +\tfor (i = 0; i < (threadCount_ - 1); i++)\n> > +\t\tthreads[i]->join();\n> \n> This is creating and then joining x threads *per frame*, right? I feel like that\n> could be improved. Have you looked at having a set of long-running threads? (Even\n> something very simple, e.g. just threads with a mutex and condvar, started in start()\n> and stopped in stop()?)\n\nFurthermore, this should use the Thread class instead of std::thread,\nand it should subclass Thread to stored the per-thread data.\n\n> >   \n> >   \tmetadata.planes()[0].bytesused = out.planes()[0].size();\n> >   \n> > @@ -909,7 +938,7 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output\n> >   \t *\n> >   \t * \\todo Pass real bufferId once stats buffer passing is changed.\n> >   \t */\n> > -\tstats_->finishFrame(frame, 0, &statsBuffer_, 1);\n> > +\tstats_->finishFrame(frame, 0, statsBuffer, threadCount_);\n> >   \toutputBufferReady.emit(output);\n> >   \tinputBufferReady.emit(input);\n> >   }\n> > diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h\n> > index b85dd11c..63fa7710 100644\n> > --- a/src/libcamera/software_isp/debayer_cpu.h\n> > +++ b/src/libcamera/software_isp/debayer_cpu.h\n> > @@ -85,6 +85,7 @@ private:\n> >   \t\tunsigned int lineBufferIndex;\n> >   \t\t/* Stored here to avoid causing register pressure in inner loop */\n> >   \t\tbool processLastLinesSeperately;\n> > +\t\tSwIspStats *statsBuffer;\n> >   \t};\n> >   \n> >   \tusing processFn = void (DebayerCpu::*)(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> > @@ -150,7 +151,6 @@ private:\n> >   \tRectangle window_;\n> >   \n> >   \t/* Variables used every line */\n> > -\tSwIspStats statsBuffer_;\n> >   \tdebayerFn debayer0_;\n> >   \tdebayerFn debayer1_;\n> >   \tdebayerFn debayer2_;","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 3B910C31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Feb 2026 14:15:07 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DD0B06224F;\n\tThu, 19 Feb 2026 15:15:06 +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 CFBF462249\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 15:15:04 +0100 (CET)","from killaraus.ideasonboard.com (unknown [83.245.237.175])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 9713055C;\n\tThu, 19 Feb 2026 15:14:11 +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=\"HDDpgqYo\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771510451;\n\tbh=O0j/XM8ArvQOgqALicU0XTOGVoBjtpKtcpDEKFGUtqk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=HDDpgqYodv51wbKPnDsPU9ZMaYnpVmW+t8l2StVP59/COsprF422kG9BE15WXhVxq\n\t7rrF9rNupnQERKkNwxTASCVzcOW+dCybc/RZH081mOL+FrhLtYTph3JnRMOD5db52F\n\txHgq1z+CcIABPM/n9B0JgeAbdjSeF62mEbGHZWLs=","Date":"Thu, 19 Feb 2026 15:15:00 +0100","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"Hans de Goede <johannes.goede@oss.qualcomm.com>,\n\tlibcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","Message-ID":"<20260219141500.GQ520738@killaraus.ideasonboard.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>\n\t<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":38259,"web_url":"https://patchwork.libcamera.org/comment/38259/","msgid":"<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.com>","date":"2026-02-19T17:07:28","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":242,"url":"https://patchwork.libcamera.org/api/people/242/","name":"Hans de Goede","email":"johannes.goede@oss.qualcomm.com"},"content":"Hi Laurent,\n\nOn 19-Feb-26 15:15, Laurent Pinchart wrote:\n> On Tue, Feb 17, 2026 at 10:07:26AM +0100, Barnabás Pőcze wrote:\n>> Hi\n>>\n>> 2026. 02. 16. 20:02 keltezéssel, Hans de Goede írta:\n>>> Add CPU soft ISP multi-threading support.\n>>>\n>>> Benchmark results for the Uno-Q with a weak CPU which is good for\n>>> performance testing, all numbers with an IMX219 running at\n>>> 3280x2464 -> 3272x2464:\n>>>\n>>> 1 thread : 147ms / frame, ~6.5 fps\n>>> 2 threads:  81ms / frame, ~12 fps\n>>> 3 threads:  66ms / frame, ~14.5 fps\n>>>\n>>> Adding a 4th thread does not improve performance.\n>>>\n>>> Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>\n>>> ---\n>>>   src/libcamera/software_isp/debayer_cpu.cpp | 49 +++++++++++++++++-----\n>>>   src/libcamera/software_isp/debayer_cpu.h   |  2 +-\n>>>   2 files changed, 40 insertions(+), 11 deletions(-)\n>>>\n>>> diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp\n>>> index 5e168554..c4b6c5b8 100644\n>>> --- a/src/libcamera/software_isp/debayer_cpu.cpp\n>>> +++ b/src/libcamera/software_isp/debayer_cpu.cpp\n>>> @@ -14,6 +14,7 @@\n>>>   #include <algorithm>\n>>>   #include <stdlib.h>\n>>>   #include <sys/ioctl.h>\n>>> +#include <thread>\n>>>   #include <time.h>\n>>>   #include <utility>\n>>>   \n>>> @@ -41,7 +42,7 @@ namespace libcamera {\n>>>    * \\param[in] configuration The global configuration\n>>>    */\n>>>   DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfiguration &configuration)\n>>> -\t: Debayer(configuration), stats_(std::move(stats)), threadCount_(1)\n>>> +\t: Debayer(configuration), stats_(std::move(stats))\n>>>   {\n>>>   \t/*\n>>>   \t * Reading from uncached buffers may be very slow.\n>>> @@ -56,6 +57,9 @@ DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfigurat\n>>>   \t */\n>>>   \tenableInputMemcpy_ =\n>>>   \t\tconfiguration.option<bool>({ \"software_isp\", \"copy_input_buffer\" }).value_or(true);\n>>> +\tthreadCount_ =\n>>> +\t\tconfiguration.option<unsigned int>({ \"software_isp\", \"threads\" }).value_or(3);\n>>> +\tthreadCount_ = std::clamp(threadCount_, 1u, kMaxThreads);\n>>>   }\n>>>   \n>>>   DebayerCpu::~DebayerCpu() = default;\n>>> @@ -692,7 +696,7 @@ void DebayerCpu::process2(uint32_t frame, const uint8_t *src, uint8_t *dst,\n>>>   \tfor (unsigned int y = threadData->yStart; y < threadData->yEnd; y += 2) {\n>>>   \t\tshiftLinePointers(linePointers, src);\n>>>   \t\tmemcpyNextLine(linePointers, threadData);\n>>> -\t\tstats_->processLine0(frame, y, linePointers, &statsBuffer_);\n>>> +\t\tstats_->processLine0(frame, y, linePointers, threadData->statsBuffer);\n>>>   \t\t(this->*debayer0_)(dst, linePointers);\n>>>   \t\tsrc += inputConfig_.stride;\n>>>   \t\tdst += outputConfig_.stride;\n>>> @@ -707,7 +711,8 @@ void DebayerCpu::process2(uint32_t frame, const uint8_t *src, uint8_t *dst,\n>>>   \tif (threadData->processLastLinesSeperately) {\n>>>   \t\tshiftLinePointers(linePointers, src);\n>>>   \t\tmemcpyNextLine(linePointers, threadData);\n>>> -\t\tstats_->processLine0(frame, threadData->yEnd, linePointers, &statsBuffer_);\n>>> +\t\tstats_->processLine0(frame, threadData->yEnd, linePointers,\n>>> +\t\t\t\t     threadData->statsBuffer);\n>>>   \t\t(this->*debayer0_)(dst, linePointers);\n>>>   \t\tsrc += inputConfig_.stride;\n>>>   \t\tdst += outputConfig_.stride;\n>>> @@ -749,7 +754,7 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst,\n>>>   \tfor (unsigned int y = threadData->yStart; y < threadData->yEnd; y += 4) {\n>>>   \t\tshiftLinePointers(linePointers, src);\n>>>   \t\tmemcpyNextLine(linePointers, threadData);\n>>> -\t\tstats_->processLine0(frame, y, linePointers, &statsBuffer_);\n>>> +\t\tstats_->processLine0(frame, y, linePointers, threadData->statsBuffer);\n>>>   \t\t(this->*debayer0_)(dst, linePointers);\n>>>   \t\tsrc += inputConfig_.stride;\n>>>   \t\tdst += outputConfig_.stride;\n>>> @@ -762,7 +767,7 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst,\n>>>   \n>>>   \t\tshiftLinePointers(linePointers, src);\n>>>   \t\tmemcpyNextLine(linePointers, threadData);\n>>> -\t\tstats_->processLine2(frame, y, linePointers, &statsBuffer_);\n>>> +\t\tstats_->processLine2(frame, y, linePointers, threadData->statsBuffer);\n>>>   \t\t(this->*debayer2_)(dst, linePointers);\n>>>   \t\tsrc += inputConfig_.stride;\n>>>   \t\tdst += outputConfig_.stride;\n>>> @@ -869,6 +874,10 @@ void DebayerCpu::updateLookupTables(const DebayerParams &params)\n>>>   \n>>>   void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params)\n>>>   {\n>>> +\tstd::unique_ptr<std::thread> threads[threadCount_ - 1];\n>>\n>> No need for the unique_ptr here, just `std::array<std::thread, kMaxThread - 1>` should be sufficient.\n>>\n>>\n>>> +\tSwIspStats statsBuffer[threadCount_];\n>>> +\tunsigned int i;\n>>> +\n>>>   \tbench_.startFrame();\n>>>   \n>>>   \tstd::vector<DmaSyncer> dmaSyncers;\n>>> @@ -891,11 +900,31 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output\n>>>   \t\treturn;\n>>>   \t}\n>>>   \n>>> -\tstats_->startFrame(frame, &statsBuffer_, 1);\n>>> +\tstats_->startFrame(frame, statsBuffer, threadCount_);\n>>>   \n>>> -\tthreadData_[0].yStart = 0;\n>>> -\tthreadData_[0].yEnd = window_.height;\n>>> -\t(this->*processInner_)(frame, in.planes()[0].data(), out.planes()[0].data(), &threadData_[0]);\n>>> +\tunsigned int yStart = 0;\n>>> +\tunsigned int linesPerThread = (window_.height / threadCount_) &\n>>> +\t\t\t\t      ~(inputConfig_.patternSize.width - 1);\n>>> +\tfor (i = 0; i < (threadCount_ - 1); i++) {\n>>> +\t\tthreadData_[i].yStart = yStart;\n>>> +\t\tthreadData_[i].yEnd = yStart + linesPerThread;\n>>> +\t\tthreadData_[i].statsBuffer = &statsBuffer[i];\n>>> +\t\tthreads[i] = std::make_unique<std::thread>(\n>>> +\t\t\t\tprocessInner_, this, frame,\n>>> +\t\t\t\tin.planes()[0].data(),\n>>> +\t\t\t\tout.planes()[0].data() + yStart * outputConfig_.stride,\n>>> +\t\t\t\t&threadData_[i]);\n>>> +\t\tyStart += linesPerThread;\n>>> +\t}\n>>> +\tthreadData_[i].yStart = yStart;\n>>> +\tthreadData_[i].yEnd = window_.height;\n>>> +\tthreadData_[i].statsBuffer = &statsBuffer[i];\n>>> +\t(this->*processInner_)(frame, in.planes()[0].data(),\n>>> +\t\t\t       out.planes()[0].data() + yStart * outputConfig_.stride,\n>>> +\t\t\t       &threadData_[i]);\n>>> +\n>>> +\tfor (i = 0; i < (threadCount_ - 1); i++)\n>>> +\t\tthreads[i]->join();\n>>\n>> This is creating and then joining x threads *per frame*, right? I feel like that\n>> could be improved. Have you looked at having a set of long-running threads? (Even\n>> something very simple, e.g. just threads with a mutex and condvar, started in start()\n>> and stopped in stop()?)\n\nAck, I agree that only creating N threads once and re-using them\nwould be better.\n\nI started with the KISS approach of just firing of a new thread\nevery frame for v1, because C++'s stdlib still lacks any kind\nof ThreadPool mechanism which is kinda sad in 2026.\n\n> Furthermore, this should use the Thread class instead of std::thread,\n> and it should subclass Thread to stored the per-thread data.\n\nI looked into using the libcamera Thread class for this,\nbut it seems unsuitable for this purpose. AFAICT is designed\nto have methods called on an object which was moved to thread,\nwhich is not what is happening here.\n\nI did consider introducing a new DebayerCpuThread object which\nwould then contain the per thread-data. This would require that\nclass to have a copy of the minimum set of other vars (of which\nthere are not much) needed for the actual inner-loop processing.\n\nThen I can move those objects to threads and use ConnectionTypeQueued\ncalls for the actual process() call.\n\nBut I do not see any way to wait for queued calls to finish\non an Object type ?\n\nThe whole Thread / Object / BoundMethod trio seems to be designed\naround message dispatching / async calling methods. But here\nI want a worker thread to repeatedly do the same thing.\n\nWhat I'm envisioning for this is using a std::thread with\na main func like this:\n\n\nthread_main(int threadIdx)\n{\n\twhile (1) {\n\t\tworkPendingCondvar_.wait(mutex_, workPendingOrStop(threadIdx));\n\n\t\tif (stop_)\n\t\t\tbreak;\n\n\t\tprocess();\n\t\t\n\t\tmutex_.lock()\n\t\tpendingWork &= ~(1 << threadIdx);\n\t\tmutex_.unlock()\n\n\t\tworkDoneCondvar_.notify_all();\n\t}\t\n}\n\nWhich is something which the Thread class does not let me\ndo, AFAICT the Thread class does not let me specify my\nown thread function, instead forcing use of the message\nmechanism.\n\nSo I think sticking with std::thread is better in this case.\n\nAgain please advise how you want to proceed. I was about to\nrework things to something like the above based on Barnabás'\ncomments. But I'll hold of on doing a v2 until we've some\nagreement on how to handle the threading otherwise I'll just\nbe wasting my time.\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 9D4EDC0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Feb 2026 17:07:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E4B1A62258;\n\tThu, 19 Feb 2026 18:07:34 +0100 (CET)","from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com\n\t[205.220.180.131])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1EA93620C9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 18:07:33 +0100 (CET)","from pps.filterd (m0279868.ppops.net [127.0.0.1])\n\tby mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id\n\t61JC7dAk2883966 for <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 17:07:31 GMT","from mail-qk1-f197.google.com (mail-qk1-f197.google.com\n\t[209.85.222.197])\n\tby mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4cdqfgadjt-1\n\t(version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT)\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 17:07:31 +0000 (GMT)","by mail-qk1-f197.google.com with SMTP id\n\taf79cd13be357-8c70cff1da5so714228985a.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 09:07:31 -0800 (PST)","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\tffacd0b85a97d-43796a5ac92sm50027947f8f.1.2026.02.19.09.07.28\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tThu, 19 Feb 2026 09:07:29 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=qualcomm.com header.i=@qualcomm.com\n\theader.b=\"gXwrlMxb\"; dkim=pass (2048-bit key;\n\tunprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com\n\theader.b=\"IT2pnTCY\"; dkim-atps=neutral","DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h=\n\tcc:content-transfer-encoding:content-type:date:from:in-reply-to\n\t:message-id:mime-version:references:subject:to; s=qcppdkim1; bh=\n\tcZDkVVxd4/TDH7k+e/l3NtCdUJhapHJbO/v7FOsT8zo=; b=gXwrlMxbCNRNlhTG\n\t8QS56u1qgpOyLcVJHuQw+xBt68AyL5E85nPe7Ta5aNjnQ8VVqgVHpVXHFwqrtuI7\n\tIebVQz+w4G/uLIkwB3BLooIL97GhO343kUY9ocpEBwiQuzmEu2DwvIgzBCS04lzV\n\tZNX6BG+Dayx336kmTUXCnZrRWRsQw1vhYmc4dKKm8ZEQlf6Xl+mZ1WYjSfpF7UBc\n\tesqc4G0iir+wVObpVubjrmhpBwMXoleUdIx6a2DWUNy+uwiE+LsBIMs2AfZVHxWF\n\thfxYj5pVciM7t1fB4/txowCryWoaErhyfBSmKDhAfjBRAfCq7JSlBd5Dh6FFN7CF\n\tTipE3w==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=oss.qualcomm.com; s=google; t=1771520851; x=1772125651;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:in-reply-to:content-language:references\n\t:cc:to:subject:from:user-agent:mime-version:date:message-id:from:to\n\t:cc:subject:date:message-id:reply-to;\n\tbh=cZDkVVxd4/TDH7k+e/l3NtCdUJhapHJbO/v7FOsT8zo=;\n\tb=IT2pnTCYVdARPZUrxsF3RyU5vM6yDnJdSUDqsny7AT6j2BrIK17UGevIIAH/Virj8u\n\tSvog3G4WQc6fIqwD2p+Bi639vRZi6lTpagR+7wesDwqJ3yeknHuAFOOBo1VzJI3Bjq9s\n\ts8akug9n6ziS5XbaWZWQZLqhZ9eV8e1ItA1ivEsr5c83ZOTYpP55/3+CK+TtCi9snEhR\n\tUnh1Yyraiykb2k1khFWaBT0uJxO/sU/qovsugiwyBZ5be1rnuF0KYThNDjONaT7MDv85\n\t7i7dkA8YSXcCiAru1yb4y3kdkumBT5ZvSDHEX24aK6xqcoPpW/wG2fZ9K1Lcsz7Rl+b5\n\tz4tA=="],"X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1771520851; x=1772125651;\n\th=content-transfer-encoding:in-reply-to:content-language:references\n\t:cc:to:subject:from:user-agent:mime-version:date:message-id:x-gm-gg\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=cZDkVVxd4/TDH7k+e/l3NtCdUJhapHJbO/v7FOsT8zo=;\n\tb=J/g75qhvNCLTOfpHK7JKxvLa+k2hTgbe/uov5GmGFaPYr8rMkcHZU1q9AmhFFH4ROw\n\tf3EKKY2eBT4TsrVNwJVODTceLCTsb/2ExTLFL3XRl5rnDzQF3jg0gNfm1aKt/T8bD9VP\n\tX9LTp8n9XAjNML+JE2AVCZUJbS78JfL+QZD+cBfaXpoVU3YdKGeOIup/q+YHalJLOez6\n\tWfEMRJ1HSXFGx6nmIHuY6tENNLaiCUQ0r+ToAraohbgUGS5QCnDS+fyjub3AZsTStjqv\n\t8PPIDXimMoidfETqVGC6nUias65S0iraqXDyqlgx1WEgm7H1owBGVHobyxp1Kwd4aOAg\n\t/U5Q==","X-Gm-Message-State":"AOJu0YxuBr0AhHWkB2q1h6xuMtEhpgDKQljzYzbt8kEGL25yzsI9yBoE\n\tNQKOPlDxb78FP3tKwvlUGySzzS8uvIUhx+Z0RJee+KNeOw1dVbxYQ1706jbyJUtQDYNoh3Afu2O\n\tP5rWKIpfnNx+mARF/LMbKzTj1OuaxLSSpL0bqXsjdenXyhrMD5zYubfKen7mJ6zgzgDuq+b6PiC\n\tVE","X-Gm-Gg":"AZuq6aI1AYj9a8wTGezxVBmHg7pk8DhPFBcmVT6BNMClImBwbEDqQfwAdG4gBgBeMHG\n\tD8OvBYcj4qK4ffqt2zUVsjqjr0zG25FdjztbFkqz0dv7vYyDLTFozfqfUXdu/c7PjjuxrH5BLrA\n\t3Fr4C9/MV1vAn4Mp7YCY1avhM8X2yfLxGzzql7f0ZrNCPoTI2mJ82yeiPrqRikZrE3pEOXtKbH7\n\tcOznhtxLV9rzwVkb0rfgffMl7OmXdRZEY90t0vvupUCTHxs7a+OsM7E2JfqfFQ+S+PHc3zML8La\n\tml9rqTXXRfLon0ih/hUKAimW+ZX8LxD4Osf0LHnkLOgG6T/Y9MxsoKJxowW88Evpc2qEVzaFcOb\n\tRJ7ZtVBxiYG31yPW0Y9R52dSWSoIU7BsuY6aay0XabSdLYxFO5K+TD6YCL7q9WL1rr9Qk4po3/6\n\tahvc7D0WAvXrcviuLbK/ikEpeHzmHHge8AnnqbTYwcVbtsI8MgYZJ1BGbq1jwnf0eablXfoWaDk\n\tcBX3Pdvkk0vLYsA","X-Received":["by 2002:a05:620a:1a9c:b0:8b2:dabe:de32 with SMTP id\n\taf79cd13be357-8cb408d6adbmr2569217985a.42.1771520850395; \n\tThu, 19 Feb 2026 09:07:30 -0800 (PST)","by 2002:a05:620a:1a9c:b0:8b2:dabe:de32 with SMTP id\n\taf79cd13be357-8cb408d6adbmr2569210685a.42.1771520849666; \n\tThu, 19 Feb 2026 09:07:29 -0800 (PST)"],"Message-ID":"<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.com>","Date":"Thu, 19 Feb 2026 18:07:28 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","From":"Hans de Goede <johannes.goede@oss.qualcomm.com>","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>, =?utf-8?q?Barnab?=\n\t=?utf-8?b?w6FzIFDFkWN6ZQ==?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>\n\t<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>\n\t<20260219141500.GQ520738@killaraus.ideasonboard.com>","Content-Language":"en-US, nl","In-Reply-To":"<20260219141500.GQ520738@killaraus.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","X-Proofpoint-GUID":"uK5Dmoc_1Ndt3yI8iqYve44tHJ5OoAAN","X-Proofpoint-ORIG-GUID":"uK5Dmoc_1Ndt3yI8iqYve44tHJ5OoAAN","X-Authority-Analysis":"v=2.4 cv=A6hh/qWG c=1 sm=1 tr=0 ts=69974353 cx=c_pps\n\ta=50t2pK5VMbmlHzFWWp8p/g==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10\n\ta=HzLeVaNsDn8A:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22\n\ta=Mpw57Om8IfrbqaoTuvik:22 a=GgsMoib0sEa3-_RKJdDe:22 a=EUspDBNiAAAA:8\n\ta=uHHDyZzW_UChd-KXiqoA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10\n\ta=IoWCM6iH3mJn3m4BftBB:22","X-Proofpoint-Spam-Details-Enc":"AW1haW4tMjYwMjE5MDE1NSBTYWx0ZWRfX80pMHkTBnnDP\n\tHePXoHju3R5ZiOx6BUWeb++PH1+QonhbK8E9Uc4KkOpbM6CHxaYpG/OzjQnXcVpZ72dJVKQscQ8\n\teO6ghz9uyoTR8UvviOaObEWi8AsjKbyzfHAjvZY+Bm8pNtlg1AImA9P6mui00t2m1yY1lpRH0lz\n\tly7rDW33g9hC9c70CaSmWMyH8yn5Y4SnWmYnX4XeIytew4pp7kDoKW0iZpkFeqerONN+rNQVmVY\n\tyi3A8zZXndWkz+AgP/cXCzi6WElPhmkMSx1huQnqpr8fhZ2p6Gko8qqdtbOglY1VE3iQ3CwjEIw\n\tKm1nS1Q3rhaA6VSeK6sYePfQWtIkfIzjRqMbXZk5cRwWxlO33Ap/NgC4o9XkhFEavtkuJbbFy+3\n\t3kgXMWUFwns6hO2WoWrA35n5mwLpmX1yiFRw4UCEQu+1l8yUuHF0PCv40rRv1UKCwCfAeDaW9e6\n\tSopplq5HVkbovAnPFNQ==","X-Proofpoint-Virus-Version":"vendor=baseguard\n\tengine=ICAP:2.0.293, Aquarius:18.0.1121, Hydra:6.1.51,\n\tFMLib:17.12.100.49\n\tdefinitions=2026-02-19_04,2026-02-19_02,2025-10-01_01","X-Proofpoint-Spam-Details":"rule=outbound_notspam policy=outbound score=0\n\tphishscore=0 priorityscore=1501 adultscore=0 impostorscore=0\n\tsuspectscore=0\n\tspamscore=0 malwarescore=0 clxscore=1015 bulkscore=0\n\tlowpriorityscore=0\n\tclassifier=typeunknown authscore=0 authtc= authcc= route=outbound\n\tadjust=0\n\treason=mlx scancount=1 engine=8.22.0-2601150000\n\tdefinitions=main-2602190155","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":38260,"web_url":"https://patchwork.libcamera.org/comment/38260/","msgid":"<20260219172256.GA1619026@killaraus.ideasonboard.com>","date":"2026-02-19T17:22:56","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Thu, Feb 19, 2026 at 06:07:28PM +0100, Hans de Goede wrote:\n> On 19-Feb-26 15:15, Laurent Pinchart wrote:\n> > On Tue, Feb 17, 2026 at 10:07:26AM +0100, Barnabás Pőcze wrote:\n> >> 2026. 02. 16. 20:02 keltezéssel, Hans de Goede írta:\n> >>> Add CPU soft ISP multi-threading support.\n> >>>\n> >>> Benchmark results for the Uno-Q with a weak CPU which is good for\n> >>> performance testing, all numbers with an IMX219 running at\n> >>> 3280x2464 -> 3272x2464:\n> >>>\n> >>> 1 thread : 147ms / frame, ~6.5 fps\n> >>> 2 threads:  81ms / frame, ~12 fps\n> >>> 3 threads:  66ms / frame, ~14.5 fps\n> >>>\n> >>> Adding a 4th thread does not improve performance.\n> >>>\n> >>> Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>\n> >>> ---\n> >>>   src/libcamera/software_isp/debayer_cpu.cpp | 49 +++++++++++++++++-----\n> >>>   src/libcamera/software_isp/debayer_cpu.h   |  2 +-\n> >>>   2 files changed, 40 insertions(+), 11 deletions(-)\n> >>>\n> >>> diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp\n> >>> index 5e168554..c4b6c5b8 100644\n> >>> --- a/src/libcamera/software_isp/debayer_cpu.cpp\n> >>> +++ b/src/libcamera/software_isp/debayer_cpu.cpp\n> >>> @@ -14,6 +14,7 @@\n> >>>   #include <algorithm>\n> >>>   #include <stdlib.h>\n> >>>   #include <sys/ioctl.h>\n> >>> +#include <thread>\n> >>>   #include <time.h>\n> >>>   #include <utility>\n> >>>   \n> >>> @@ -41,7 +42,7 @@ namespace libcamera {\n> >>>    * \\param[in] configuration The global configuration\n> >>>    */\n> >>>   DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfiguration &configuration)\n> >>> -\t: Debayer(configuration), stats_(std::move(stats)), threadCount_(1)\n> >>> +\t: Debayer(configuration), stats_(std::move(stats))\n> >>>   {\n> >>>   \t/*\n> >>>   \t * Reading from uncached buffers may be very slow.\n> >>> @@ -56,6 +57,9 @@ DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfigurat\n> >>>   \t */\n> >>>   \tenableInputMemcpy_ =\n> >>>   \t\tconfiguration.option<bool>({ \"software_isp\", \"copy_input_buffer\" }).value_or(true);\n> >>> +\tthreadCount_ =\n> >>> +\t\tconfiguration.option<unsigned int>({ \"software_isp\", \"threads\" }).value_or(3);\n> >>> +\tthreadCount_ = std::clamp(threadCount_, 1u, kMaxThreads);\n> >>>   }\n> >>>   \n> >>>   DebayerCpu::~DebayerCpu() = default;\n> >>> @@ -692,7 +696,7 @@ void DebayerCpu::process2(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> >>>   \tfor (unsigned int y = threadData->yStart; y < threadData->yEnd; y += 2) {\n> >>>   \t\tshiftLinePointers(linePointers, src);\n> >>>   \t\tmemcpyNextLine(linePointers, threadData);\n> >>> -\t\tstats_->processLine0(frame, y, linePointers, &statsBuffer_);\n> >>> +\t\tstats_->processLine0(frame, y, linePointers, threadData->statsBuffer);\n> >>>   \t\t(this->*debayer0_)(dst, linePointers);\n> >>>   \t\tsrc += inputConfig_.stride;\n> >>>   \t\tdst += outputConfig_.stride;\n> >>> @@ -707,7 +711,8 @@ void DebayerCpu::process2(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> >>>   \tif (threadData->processLastLinesSeperately) {\n> >>>   \t\tshiftLinePointers(linePointers, src);\n> >>>   \t\tmemcpyNextLine(linePointers, threadData);\n> >>> -\t\tstats_->processLine0(frame, threadData->yEnd, linePointers, &statsBuffer_);\n> >>> +\t\tstats_->processLine0(frame, threadData->yEnd, linePointers,\n> >>> +\t\t\t\t     threadData->statsBuffer);\n> >>>   \t\t(this->*debayer0_)(dst, linePointers);\n> >>>   \t\tsrc += inputConfig_.stride;\n> >>>   \t\tdst += outputConfig_.stride;\n> >>> @@ -749,7 +754,7 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> >>>   \tfor (unsigned int y = threadData->yStart; y < threadData->yEnd; y += 4) {\n> >>>   \t\tshiftLinePointers(linePointers, src);\n> >>>   \t\tmemcpyNextLine(linePointers, threadData);\n> >>> -\t\tstats_->processLine0(frame, y, linePointers, &statsBuffer_);\n> >>> +\t\tstats_->processLine0(frame, y, linePointers, threadData->statsBuffer);\n> >>>   \t\t(this->*debayer0_)(dst, linePointers);\n> >>>   \t\tsrc += inputConfig_.stride;\n> >>>   \t\tdst += outputConfig_.stride;\n> >>> @@ -762,7 +767,7 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst,\n> >>>   \n> >>>   \t\tshiftLinePointers(linePointers, src);\n> >>>   \t\tmemcpyNextLine(linePointers, threadData);\n> >>> -\t\tstats_->processLine2(frame, y, linePointers, &statsBuffer_);\n> >>> +\t\tstats_->processLine2(frame, y, linePointers, threadData->statsBuffer);\n> >>>   \t\t(this->*debayer2_)(dst, linePointers);\n> >>>   \t\tsrc += inputConfig_.stride;\n> >>>   \t\tdst += outputConfig_.stride;\n> >>> @@ -869,6 +874,10 @@ void DebayerCpu::updateLookupTables(const DebayerParams &params)\n> >>>   \n> >>>   void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params)\n> >>>   {\n> >>> +\tstd::unique_ptr<std::thread> threads[threadCount_ - 1];\n> >>\n> >> No need for the unique_ptr here, just `std::array<std::thread, kMaxThread - 1>` should be sufficient.\n> >>\n> >>\n> >>> +\tSwIspStats statsBuffer[threadCount_];\n> >>> +\tunsigned int i;\n> >>> +\n> >>>   \tbench_.startFrame();\n> >>>   \n> >>>   \tstd::vector<DmaSyncer> dmaSyncers;\n> >>> @@ -891,11 +900,31 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output\n> >>>   \t\treturn;\n> >>>   \t}\n> >>>   \n> >>> -\tstats_->startFrame(frame, &statsBuffer_, 1);\n> >>> +\tstats_->startFrame(frame, statsBuffer, threadCount_);\n> >>>   \n> >>> -\tthreadData_[0].yStart = 0;\n> >>> -\tthreadData_[0].yEnd = window_.height;\n> >>> -\t(this->*processInner_)(frame, in.planes()[0].data(), out.planes()[0].data(), &threadData_[0]);\n> >>> +\tunsigned int yStart = 0;\n> >>> +\tunsigned int linesPerThread = (window_.height / threadCount_) &\n> >>> +\t\t\t\t      ~(inputConfig_.patternSize.width - 1);\n> >>> +\tfor (i = 0; i < (threadCount_ - 1); i++) {\n> >>> +\t\tthreadData_[i].yStart = yStart;\n> >>> +\t\tthreadData_[i].yEnd = yStart + linesPerThread;\n> >>> +\t\tthreadData_[i].statsBuffer = &statsBuffer[i];\n> >>> +\t\tthreads[i] = std::make_unique<std::thread>(\n> >>> +\t\t\t\tprocessInner_, this, frame,\n> >>> +\t\t\t\tin.planes()[0].data(),\n> >>> +\t\t\t\tout.planes()[0].data() + yStart * outputConfig_.stride,\n> >>> +\t\t\t\t&threadData_[i]);\n> >>> +\t\tyStart += linesPerThread;\n> >>> +\t}\n> >>> +\tthreadData_[i].yStart = yStart;\n> >>> +\tthreadData_[i].yEnd = window_.height;\n> >>> +\tthreadData_[i].statsBuffer = &statsBuffer[i];\n> >>> +\t(this->*processInner_)(frame, in.planes()[0].data(),\n> >>> +\t\t\t       out.planes()[0].data() + yStart * outputConfig_.stride,\n> >>> +\t\t\t       &threadData_[i]);\n> >>> +\n> >>> +\tfor (i = 0; i < (threadCount_ - 1); i++)\n> >>> +\t\tthreads[i]->join();\n> >>\n> >> This is creating and then joining x threads *per frame*, right? I feel like that\n> >> could be improved. Have you looked at having a set of long-running threads? (Even\n> >> something very simple, e.g. just threads with a mutex and condvar, started in start()\n> >> and stopped in stop()?)\n> \n> Ack, I agree that only creating N threads once and re-using them\n> would be better.\n> \n> I started with the KISS approach of just firing of a new thread\n> every frame for v1, because C++'s stdlib still lacks any kind\n> of ThreadPool mechanism which is kinda sad in 2026.\n> \n> > Furthermore, this should use the Thread class instead of std::thread,\n> > and it should subclass Thread to stored the per-thread data.\n> \n> I looked into using the libcamera Thread class for this,\n> but it seems unsuitable for this purpose. AFAICT is designed\n> to have methods called on an object which was moved to thread,\n> which is not what is happening here.\n> \n> I did consider introducing a new DebayerCpuThread object which\n> would then contain the per thread-data. This would require that\n> class to have a copy of the minimum set of other vars (of which\n> there are not much) needed for the actual inner-loop processing.\n> \n> Then I can move those objects to threads and use ConnectionTypeQueued\n> calls for the actual process() call.\n> \n> But I do not see any way to wait for queued calls to finish\n> on an Object type ?\n> \n> The whole Thread / Object / BoundMethod trio seems to be designed\n> around message dispatching / async calling methods. But here\n> I want a worker thread to repeatedly do the same thing.\n> \n> What I'm envisioning for this is using a std::thread with\n> a main func like this:\n> \n> \n> thread_main(int threadIdx)\n> {\n> \twhile (1) {\n> \t\tworkPendingCondvar_.wait(mutex_, workPendingOrStop(threadIdx));\n> \n> \t\tif (stop_)\n> \t\t\tbreak;\n> \n> \t\tprocess();\n> \t\t\n> \t\tmutex_.lock()\n> \t\tpendingWork &= ~(1 << threadIdx);\n> \t\tmutex_.unlock()\n> \n> \t\tworkDoneCondvar_.notify_all();\n> \t}\t\n> }\n> \n> Which is something which the Thread class does not let me\n> do, AFAICT the Thread class does not let me specify my\n> own thread function, instead forcing use of the message\n> mechanism.\n\nYou don't have to use messages. By default the Thread class will\ninstantiate an event loop, but you can override the run() function to\nprovide your own thread loop. Have you considered that ?\n\nNote that if you have a fixed number of threads, each processing a\npre-defined part of the image, messages can still be useful as a\ncommunication primitive. You can use invokeMethod() with queued\ndelivery, and benefit from the message queue to deliver the work to the\nthread asynchronously. If you want to implement a thread pool where each\nthread would automatically pick the next work item when it goes back to\nits idle loop then you will need your own run() function.\n\n> So I think sticking with std::thread is better in this case.\n> \n> Again please advise how you want to proceed. I was about to\n> rework things to something like the above based on Barnabás'\n> comments. But I'll hold of on doing a v2 until we've some\n> agreement on how to handle the threading otherwise I'll just\n> be wasting my time.","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 8453BC31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Feb 2026 17:23:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8B37A6225A;\n\tThu, 19 Feb 2026 18:23:01 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 21322620C9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 18:23:00 +0100 (CET)","from killaraus.ideasonboard.com (unknown [83.245.237.175])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6F4883A4;\n\tThu, 19 Feb 2026 18:22:06 +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=\"BYQLACff\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771521726;\n\tbh=BPowqT4/yWw6dqJsnR57uDEHvghgR/diOpba0Sd/FVQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=BYQLACff7jEwhHXBxwikZzPpe+pSTuA4any/93oxSwFsfsvNmeToxo+6GuXQOnurw\n\trcJbJZBEh3a09c5euU6HUf3CoYdqrSvCb4AhV8lq2EoIMZEPf9r0A/vx7q3bZ33nXO\n\tEcao8OQ7WSyQBt8OLMViVY7K1S92LJv6KQplFx/A=","Date":"Thu, 19 Feb 2026 18:22:56 +0100","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Hans de Goede <johannes.goede@oss.qualcomm.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","Message-ID":"<20260219172256.GA1619026@killaraus.ideasonboard.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>\n\t<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>\n\t<20260219141500.GQ520738@killaraus.ideasonboard.com>\n\t<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.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":38262,"web_url":"https://patchwork.libcamera.org/comment/38262/","msgid":"<6d0a36ba-3356-47d5-a5cd-57e6ae13a50c@oss.qualcomm.com>","date":"2026-02-19T18:20:41","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":242,"url":"https://patchwork.libcamera.org/api/people/242/","name":"Hans de Goede","email":"johannes.goede@oss.qualcomm.com"},"content":"Hi Laurent,\n\nOn 19-Feb-26 18:22, Laurent Pinchart wrote:\n\n<snip>\n\n>>>> This is creating and then joining x threads *per frame*, right? I feel like that\n>>>> could be improved. Have you looked at having a set of long-running threads? (Even\n>>>> something very simple, e.g. just threads with a mutex and condvar, started in start()\n>>>> and stopped in stop()?)\n>>\n>> Ack, I agree that only creating N threads once and re-using them\n>> would be better.\n>>\n>> I started with the KISS approach of just firing of a new thread\n>> every frame for v1, because C++'s stdlib still lacks any kind\n>> of ThreadPool mechanism which is kinda sad in 2026.\n>>\n>>> Furthermore, this should use the Thread class instead of std::thread,\n>>> and it should subclass Thread to stored the per-thread data.\n>>\n>> I looked into using the libcamera Thread class for this,\n>> but it seems unsuitable for this purpose. AFAICT is designed\n>> to have methods called on an object which was moved to thread,\n>> which is not what is happening here.\n>>\n>> I did consider introducing a new DebayerCpuThread object which\n>> would then contain the per thread-data. This would require that\n>> class to have a copy of the minimum set of other vars (of which\n>> there are not much) needed for the actual inner-loop processing.\n>>\n>> Then I can move those objects to threads and use ConnectionTypeQueued\n>> calls for the actual process() call.\n>>\n>> But I do not see any way to wait for queued calls to finish\n>> on an Object type ?\n>>\n>> The whole Thread / Object / BoundMethod trio seems to be designed\n>> around message dispatching / async calling methods. But here\n>> I want a worker thread to repeatedly do the same thing.\n>>\n>> What I'm envisioning for this is using a std::thread with\n>> a main func like this:\n>>\n>>\n>> thread_main(int threadIdx)\n>> {\n>> \twhile (1) {\n>> \t\tworkPendingCondvar_.wait(mutex_, workPendingOrStop(threadIdx));\n>>\n>> \t\tif (stop_)\n>> \t\t\tbreak;\n>>\n>> \t\tprocess();\n>> \t\t\n>> \t\tmutex_.lock()\n>> \t\tpendingWork &= ~(1 << threadIdx);\n>> \t\tmutex_.unlock()\n>>\n>> \t\tworkDoneCondvar_.notify_all();\n>> \t}\t\n>> }\n>>\n>> Which is something which the Thread class does not let me\n>> do, AFAICT the Thread class does not let me specify my\n>> own thread function, instead forcing use of the message\n>> mechanism.\n> \n> You don't have to use messages. By default the Thread class will\n> instantiate an event loop, but you can override the run() function to\n> provide your own thread loop. Have you considered that ?\n\nI did not consider that before. I've just looked into this a bit,\nI think I can make this work.\n\n> Note that if you have a fixed number of threads, each processing a\n> pre-defined part of the image, messages can still be useful as a\n> communication primitive. You can use invokeMethod() with queued\n> delivery, and benefit from the message queue to deliver the work to the\n> thread asynchronously.\n\nI do plan to have a fixed (configurable in config file) number\nof threads, like in this v1 patch.\n\nand being able to use invokeMethod does sound useful, the question\nis how do I make the \"main\" process() method wait for all\n(1 per debayerCpuThread object) invokeMethod calls to complete ?\n\nShould I add a ConditionVariable for this and have the run()\noverride function notify this ConditionVariable, or is their\nalready some mechanism for this which I'm missing ?\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 45B7DC31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Feb 2026 18:20:50 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7884A6225C;\n\tThu, 19 Feb 2026 19:20:49 +0100 (CET)","from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com\n\t[205.220.180.131])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id CA4A6620C9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 19:20:47 +0100 (CET)","from pps.filterd (m0279873.ppops.net [127.0.0.1])\n\tby mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id\n\t61JBMIak3564475 for <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 18:20:46 GMT","from mail-qk1-f199.google.com (mail-qk1-f199.google.com\n\t[209.85.222.199])\n\tby mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4ce1k81917-1\n\t(version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT)\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 18:20:46 +0000 (GMT)","by mail-qk1-f199.google.com with SMTP id\n\taf79cd13be357-8c70e610242so938480285a.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Feb 2026 10:20:46 -0800 (PST)","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\t4fb4d7f45d1cf-65bad19bed1sm4138124a12.7.2026.02.19.10.20.42\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tThu, 19 Feb 2026 10:20:43 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=qualcomm.com header.i=@qualcomm.com\n\theader.b=\"XNC3jOah\"; dkim=pass (2048-bit key;\n\tunprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com\n\theader.b=\"K+kfc63V\"; dkim-atps=neutral","DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h=\n\tcc:content-transfer-encoding:content-type:date:from:in-reply-to\n\t:message-id:mime-version:references:subject:to; s=qcppdkim1; bh=\n\tShoTGAaM42Mi6cm8owJyqrpMdnZQqfOE1JAas+hHWFU=; b=XNC3jOahjoEUQxnZ\n\tbS5eZzRnbpKtQHXaLLgRg/3TuEabH1Eqhydf2rz1yxA1yOCZRa7VRaITf14mqZkX\n\tvqXvXl0CHzjGL2TBva1psr6iZoJmoN6M0sM5bj4WgdimJqJgvp4mhilkxcmJhXFl\n\tt3sWFKuwjQMDfZW8iaRfABWJeEVP2M902VsSD/12S4rhhs+I2tGMXuIrTWe2M8Zp\n\tvn5+PLXZgvsM6g5vsxDW3wSXgF2v1HoIUaXB7bnv0eO5/W+yFDHGTm4YXBURajo0\n\tNddJrXwHIIociEFnr2hfIwnjUqYxLLCaMUATg+sIqbnYi/zfg9mLkZpES5lfyd1y\n\t3jNebQ==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=oss.qualcomm.com; s=google; t=1771525246; x=1772130046;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:in-reply-to:content-language:references\n\t:cc:to:subject:from:user-agent:mime-version:date:message-id:from:to\n\t:cc:subject:date:message-id:reply-to;\n\tbh=ShoTGAaM42Mi6cm8owJyqrpMdnZQqfOE1JAas+hHWFU=;\n\tb=K+kfc63VScGMIY/OmIdH3jqiqYD71yhW4GAj5NpqUIiskGHS3cdcIbQqPUWgZqnWjI\n\tjJBmXA5qHgCxNv6bEXWOpFhuCml03xO27e5MkWPf/fyNPvYYaC8rXk6dEb7efoMFHXBf\n\todbjiPjeY0oQfgye4RcRREdUpjxgb7Tsh0o0S/O0A9VY/ztNfpPKoTAELMrUeba6HbY/\n\teC2o9kfp6j4rzFd/b3F4hwhOfi9jQQTcIltAtp9XRhfAI1ys4LjAgRw7j67Dl1lsn2DB\n\tA8a6FBkOtHEcZAjhBM5cVzkU8rWj2JEcm7NedZXTRcXhhLpCmUCQPlSOcw6t3mqGbHod\n\tmPDQ=="],"X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1771525246; x=1772130046;\n\th=content-transfer-encoding:in-reply-to:content-language:references\n\t:cc:to:subject:from:user-agent:mime-version:date:message-id:x-gm-gg\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=ShoTGAaM42Mi6cm8owJyqrpMdnZQqfOE1JAas+hHWFU=;\n\tb=C7hkNOT1ve39BwCLug9vtwZhT3xcu/rvEiJOL/3vJ/XvcXm+0naExKNYs5kM/Xmfuh\n\tO0mYDoLcS9g5NL7WgymR/oP2DUBikc3B5zHVrv0PXYUGun1gNSJIbuAAFsLmsWuYPguV\n\tl9mupee63G0iMy7ezjoibcor4/arLTCrJXfbaMG1OxzNkvuwzysYS/g0hmH7iZq+7dz+\n\ts8UQ1H3HqsEG2+BR+PbsVXkMR2/Rw4YDKLDZxPqLRB/a3GgIOsChCzn2wQiOTBtaElK9\n\thJqdwK/sF3CGFewoueZU7Y5EZstYz/cuTQ/EyhdbKn+tEhbHNLD1X5gwh7Pa+5Ii62pS\n\tC/KQ==","X-Forwarded-Encrypted":"i=1;\n\tAJvYcCXJckR5UQJvzJDuxb6GJkJx2ltfrz1ofonBse1caMRu/mjZYG0tj8FQrfUlWH0XKLXWaNWDKCBXkyPVOO/dUkI=@lists.libcamera.org","X-Gm-Message-State":"AOJu0YyDd3KTVAMirVINkt6OdqBMhxJlkiJylYgIpQoukYec4qSOAh3z\n\tBbkI/QupcFuEe9hU0eLerJwIpYumxxIdccuyRrvnaKMoSAi3Uzm9SONbcz+h1D3sYmoJbH+o0pU\n\tjXyX5BCw1ZJfWreE6TqvMFdIAyDizfE6mAt1ns9xWt2U/dpc97LeH3MreN7Rg6ZtVKB3SlAWTGO\n\tps","X-Gm-Gg":"AZuq6aKCZhULvxKS6ZkhYzE2dxCYIaSTO4zQJvmx2+rTe7V3Q8YGNxa3j0DOsCfs9W3\n\tmy9t3XSFg8VgcPTnUG7DkBte0hCFyer/TIc/IbGqq8W+DIV6fZHrlRsJKD9I4owdNIJHTeGqaH3\n\tlMerEPNzZE7PVSEogjKn+RZwsFAEAGFPau2juEY3ZUevuHJ2SJBZ/pGTDOQwb0TxYQBK5Iy0bZf\n\tLx37k0DXmbFagjWaXjfLbwC+m13ESsaHGJqpEADoIrwHLFnqs19clSJ6Zdoz1FT2eSr2GxNry51\n\tJPv6K1GeDBPPhBAa4C5uHCjZsv1pzMd1f7CPregV5n4mw4iicfaOFL6Sb005icVWHgbKA4sDAg4\n\tAgGiuT2eJsTMcGraBL2NzjXDY9CgyNvPx51TkAMuMsMwoAccCasdas+34BAdUUua0XAuKTrucz7\n\tpZwPSLdndP2MUoBWbYpNB9WOF0EpOP7g7bDYclcpX+D/ID1K60zImfkUscx5GWWpmjLaZAh1Wuy\n\t8bTMV/bQH9+z3+W","X-Received":["by 2002:a05:620a:4446:b0:8c6:f414:3ba8 with SMTP id\n\taf79cd13be357-8cb4242a205mr2687936985a.49.1771525245730; \n\tThu, 19 Feb 2026 10:20:45 -0800 (PST)","by 2002:a05:620a:4446:b0:8c6:f414:3ba8 with SMTP id\n\taf79cd13be357-8cb4242a205mr2687932885a.49.1771525245184; \n\tThu, 19 Feb 2026 10:20:45 -0800 (PST)"],"Message-ID":"<6d0a36ba-3356-47d5-a5cd-57e6ae13a50c@oss.qualcomm.com>","Date":"Thu, 19 Feb 2026 19:20:41 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","From":"Hans de Goede <johannes.goede@oss.qualcomm.com>","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>\n\t<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>\n\t<20260219141500.GQ520738@killaraus.ideasonboard.com>\n\t<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.com>\n\t<20260219172256.GA1619026@killaraus.ideasonboard.com>","Content-Language":"en-US, nl","In-Reply-To":"<20260219172256.GA1619026@killaraus.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"7bit","X-Proofpoint-GUID":"81TA02GWGUX3CQj_7L7ZDN7h6NN7VFPL","X-Proofpoint-Spam-Details-Enc":"AW1haW4tMjYwMjE5MDE2NiBTYWx0ZWRfX+JQTwGnLZiNE\n\tflplAIscNGUXdvmZMNKGUnS5NyhFXN9nUP2znOWEh3YWL2yKM7xiFfwGN+7GalcpS62KQC7vp4h\n\t4ysWgVqxrBSjIhYc7GLAakATubRsePN+frOUjJpTMvmGGdrqpRAiOKT3+HbldWT9e6V/c1VCQvz\n\t3ZNVmzWccyD5IKUtxrabIq23PyGFQhv/ZHO5N4OEFFAZg6/jFbALdTdTCvOgFsB80Xi+l4KMk8+\n\tEzASNOSyy7rwkr8of80AVYrnAGN9D8lxOS3nLiwRb2QYBaz83S8o3G3+W+7R5qGyRIZx81IYCdJ\n\t2nGxDM860COSfQcfAc5sjdHBYPQ06zuPkoHOAKL/RFW+2LyW/hvrk9DW4wjuntAzv6/YFY9F9n2\n\tXv2dIZfsMMjMrMKVz17+i9pFcxYAxkKTU7TuVMDd8k74hkQD2cqVwukHN6zPo7bq9b20edytzQl\n\tgA/a5ZhRl2bqaxaBovQ==","X-Authority-Analysis":"v=2.4 cv=cdrfb3DM c=1 sm=1 tr=0 ts=6997547e cx=c_pps\n\ta=HLyN3IcIa5EE8TELMZ618Q==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10\n\ta=HzLeVaNsDn8A:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22\n\ta=Mpw57Om8IfrbqaoTuvik:22 a=GgsMoib0sEa3-_RKJdDe:22\n\ta=SS0tRihRASQHWP8GgcUA:9\n\ta=QEXdDO2ut3YA:10 a=bTQJ7kPSJx9SKPbeHEYW:22","X-Proofpoint-ORIG-GUID":"81TA02GWGUX3CQj_7L7ZDN7h6NN7VFPL","X-Proofpoint-Virus-Version":"vendor=baseguard\n\tengine=ICAP:2.0.293, Aquarius:18.0.1121, Hydra:6.1.51,\n\tFMLib:17.12.100.49\n\tdefinitions=2026-02-19_04,2026-02-19_03,2025-10-01_01","X-Proofpoint-Spam-Details":"rule=outbound_notspam policy=outbound score=0\n\tspamscore=0 bulkscore=0 adultscore=0 priorityscore=1501 phishscore=0\n\timpostorscore=0 lowpriorityscore=0 suspectscore=0 malwarescore=0\n\tclxscore=1015 classifier=typeunknown authscore=0 authtc= authcc=\n\troute=outbound adjust=0 reason=mlx scancount=1\n\tengine=8.22.0-2601150000\n\tdefinitions=main-2602190166","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":38265,"web_url":"https://patchwork.libcamera.org/comment/38265/","msgid":"<20260220160923.GE1619026@killaraus.ideasonboard.com>","date":"2026-02-20T16:09:23","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Thu, Feb 19, 2026 at 07:20:41PM +0100, Hans de Goede wrote:\n> Hi Laurent,\n> \n> On 19-Feb-26 18:22, Laurent Pinchart wrote:\n> \n> <snip>\n> \n> >>>> This is creating and then joining x threads *per frame*, right? I feel like that\n> >>>> could be improved. Have you looked at having a set of long-running threads? (Even\n> >>>> something very simple, e.g. just threads with a mutex and condvar, started in start()\n> >>>> and stopped in stop()?)\n> >>\n> >> Ack, I agree that only creating N threads once and re-using them\n> >> would be better.\n> >>\n> >> I started with the KISS approach of just firing of a new thread\n> >> every frame for v1, because C++'s stdlib still lacks any kind\n> >> of ThreadPool mechanism which is kinda sad in 2026.\n> >>\n> >>> Furthermore, this should use the Thread class instead of std::thread,\n> >>> and it should subclass Thread to stored the per-thread data.\n> >>\n> >> I looked into using the libcamera Thread class for this,\n> >> but it seems unsuitable for this purpose. AFAICT is designed\n> >> to have methods called on an object which was moved to thread,\n> >> which is not what is happening here.\n> >>\n> >> I did consider introducing a new DebayerCpuThread object which\n> >> would then contain the per thread-data. This would require that\n> >> class to have a copy of the minimum set of other vars (of which\n> >> there are not much) needed for the actual inner-loop processing.\n> >>\n> >> Then I can move those objects to threads and use ConnectionTypeQueued\n> >> calls for the actual process() call.\n> >>\n> >> But I do not see any way to wait for queued calls to finish\n> >> on an Object type ?\n> >>\n> >> The whole Thread / Object / BoundMethod trio seems to be designed\n> >> around message dispatching / async calling methods. But here\n> >> I want a worker thread to repeatedly do the same thing.\n> >>\n> >> What I'm envisioning for this is using a std::thread with\n> >> a main func like this:\n> >>\n> >>\n> >> thread_main(int threadIdx)\n> >> {\n> >> \twhile (1) {\n> >> \t\tworkPendingCondvar_.wait(mutex_, workPendingOrStop(threadIdx));\n> >>\n> >> \t\tif (stop_)\n> >> \t\t\tbreak;\n> >>\n> >> \t\tprocess();\n> >> \t\t\n> >> \t\tmutex_.lock()\n> >> \t\tpendingWork &= ~(1 << threadIdx);\n> >> \t\tmutex_.unlock()\n> >>\n> >> \t\tworkDoneCondvar_.notify_all();\n> >> \t}\t\n> >> }\n> >>\n> >> Which is something which the Thread class does not let me\n> >> do, AFAICT the Thread class does not let me specify my\n> >> own thread function, instead forcing use of the message\n> >> mechanism.\n> > \n> > You don't have to use messages. By default the Thread class will\n> > instantiate an event loop, but you can override the run() function to\n> > provide your own thread loop. Have you considered that ?\n> \n> I did not consider that before. I've just looked into this a bit,\n> I think I can make this work.\n> \n> > Note that if you have a fixed number of threads, each processing a\n> > pre-defined part of the image, messages can still be useful as a\n> > communication primitive. You can use invokeMethod() with queued\n> > delivery, and benefit from the message queue to deliver the work to the\n> > thread asynchronously.\n> \n> I do plan to have a fixed (configurable in config file) number\n> of threads, like in this v1 patch.\n> \n> and being able to use invokeMethod does sound useful,\n\nNote that invokeMethod() requires the receiving object to be bound to a\nthread that runs an event loop, so you won't be able to override run()\nin that case (or, to be precise, you can override run() but you will\nhave to call exec() from your implementation, so you can't run your own\nthread loop).\n\n> the question\n> is how do I make the \"main\" process() method wait for all\n> (1 per debayerCpuThread object) invokeMethod calls to complete ?\n> \n> Should I add a ConditionVariable for this and have the run()\n> override function notify this ConditionVariable, or is their\n> already some mechanism for this which I'm missing ?\n\nThat would work, but I think the design could be improved.\n\nIn this series you introduce n-1 new threads, with the existing software\nISP thread acting as the n'th thread. I think it would be nicer to have\nn identical instances of a class to handle the threads, with\nSoftwareIsp::process() handling the dispatching (with invokeMethod()).\nCompletion would be signalled through signals, the same way it is done\nnow with inputBufferReady and outputBufferReady. The\nSoftwareIsp::inputReady() and SoftwareIsp::outputReady() functions would\nbe called once per thread, and would handle the required accounting to\nemit the SoftwareIsp completion signals only after all threads complete\ntheir jobs.\n\nI haven't investigated in details to see if there would be dragons\nlurking in dark corners, but my gut feeling is that a design where no\nthread is special would be cleaner.","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 A8F06C31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 20 Feb 2026 16:09:29 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DA00C62267;\n\tFri, 20 Feb 2026 17:09:28 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0207761FBF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 17:09:26 +0100 (CET)","from killaraus.ideasonboard.com (unknown [83.245.237.175])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id AFA7B502;\n\tFri, 20 Feb 2026 17:08:32 +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=\"B0O5hYDB\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771603713;\n\tbh=NDZyLGW1Y1JfVtrsM64dZBNHfMm4SB6/tkfp0oACrXM=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=B0O5hYDBSv/r3h3f9cw9i1arzzODMWBtAzd7ZCLiRMu+5MoWGjbZkq3wGp4mKvvmf\n\tQKfJ1FM5ad7YEppE/utDbEyGXtT1lXF60ARkB0U5kvks1PHI+M0Xtw5lxasPmT1KIj\n\tCYWARVST+fV57c1T/WW4YO/nHtHkX2I4DpmwegBg=","Date":"Fri, 20 Feb 2026 17:09:23 +0100","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Hans de Goede <johannes.goede@oss.qualcomm.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","Message-ID":"<20260220160923.GE1619026@killaraus.ideasonboard.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>\n\t<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>\n\t<20260219141500.GQ520738@killaraus.ideasonboard.com>\n\t<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.com>\n\t<20260219172256.GA1619026@killaraus.ideasonboard.com>\n\t<6d0a36ba-3356-47d5-a5cd-57e6ae13a50c@oss.qualcomm.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<6d0a36ba-3356-47d5-a5cd-57e6ae13a50c@oss.qualcomm.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":38267,"web_url":"https://patchwork.libcamera.org/comment/38267/","msgid":"<1a0a1266-5585-40f9-83fc-d836ba66b518@oss.qualcomm.com>","date":"2026-02-20T18:51:17","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":242,"url":"https://patchwork.libcamera.org/api/people/242/","name":"Hans de Goede","email":"johannes.goede@oss.qualcomm.com"},"content":"Hi,\n\nOn 20-Feb-26 5:09 PM, Laurent Pinchart wrote:\n> On Thu, Feb 19, 2026 at 07:20:41PM +0100, Hans de Goede wrote:\n>> Hi Laurent,\n>>\n>> On 19-Feb-26 18:22, Laurent Pinchart wrote:\n>>\n>> <snip>\n>>\n>>>>>> This is creating and then joining x threads *per frame*, right? I feel like that\n>>>>>> could be improved. Have you looked at having a set of long-running threads? (Even\n>>>>>> something very simple, e.g. just threads with a mutex and condvar, started in start()\n>>>>>> and stopped in stop()?)\n>>>>\n>>>> Ack, I agree that only creating N threads once and re-using them\n>>>> would be better.\n>>>>\n>>>> I started with the KISS approach of just firing of a new thread\n>>>> every frame for v1, because C++'s stdlib still lacks any kind\n>>>> of ThreadPool mechanism which is kinda sad in 2026.\n>>>>\n>>>>> Furthermore, this should use the Thread class instead of std::thread,\n>>>>> and it should subclass Thread to stored the per-thread data.\n>>>>\n>>>> I looked into using the libcamera Thread class for this,\n>>>> but it seems unsuitable for this purpose. AFAICT is designed\n>>>> to have methods called on an object which was moved to thread,\n>>>> which is not what is happening here.\n>>>>\n>>>> I did consider introducing a new DebayerCpuThread object which\n>>>> would then contain the per thread-data. This would require that\n>>>> class to have a copy of the minimum set of other vars (of which\n>>>> there are not much) needed for the actual inner-loop processing.\n>>>>\n>>>> Then I can move those objects to threads and use ConnectionTypeQueued\n>>>> calls for the actual process() call.\n>>>>\n>>>> But I do not see any way to wait for queued calls to finish\n>>>> on an Object type ?\n>>>>\n>>>> The whole Thread / Object / BoundMethod trio seems to be designed\n>>>> around message dispatching / async calling methods. But here\n>>>> I want a worker thread to repeatedly do the same thing.\n>>>>\n>>>> What I'm envisioning for this is using a std::thread with\n>>>> a main func like this:\n>>>>\n>>>>\n>>>> thread_main(int threadIdx)\n>>>> {\n>>>> \twhile (1) {\n>>>> \t\tworkPendingCondvar_.wait(mutex_, workPendingOrStop(threadIdx));\n>>>>\n>>>> \t\tif (stop_)\n>>>> \t\t\tbreak;\n>>>>\n>>>> \t\tprocess();\n>>>> \t\t\n>>>> \t\tmutex_.lock()\n>>>> \t\tpendingWork &= ~(1 << threadIdx);\n>>>> \t\tmutex_.unlock()\n>>>>\n>>>> \t\tworkDoneCondvar_.notify_all();\n>>>> \t}\t\n>>>> }\n>>>>\n>>>> Which is something which the Thread class does not let me\n>>>> do, AFAICT the Thread class does not let me specify my\n>>>> own thread function, instead forcing use of the message\n>>>> mechanism.\n>>>\n>>> You don't have to use messages. By default the Thread class will\n>>> instantiate an event loop, but you can override the run() function to\n>>> provide your own thread loop. Have you considered that ?\n>>\n>> I did not consider that before. I've just looked into this a bit,\n>> I think I can make this work.\n>>\n>>> Note that if you have a fixed number of threads, each processing a\n>>> pre-defined part of the image, messages can still be useful as a\n>>> communication primitive. You can use invokeMethod() with queued\n>>> delivery, and benefit from the message queue to deliver the work to the\n>>> thread asynchronously.\n>>\n>> I do plan to have a fixed (configurable in config file) number\n>> of threads, like in this v1 patch.\n>>\n>> and being able to use invokeMethod does sound useful,\n> \n> Note that invokeMethod() requires the receiving object to be bound to a\n> thread that runs an event loop, so you won't be able to override run()\n> in that case (or, to be precise, you can override run() but you will\n> have to call exec() from your implementation, so you can't run your own\n> thread loop).\n> \n>> the question\n>> is how do I make the \"main\" process() method wait for all\n>> (1 per debayerCpuThread object) invokeMethod calls to complete ?\n>>\n>> Should I add a ConditionVariable for this and have the run()\n>> override function notify this ConditionVariable, or is their\n>> already some mechanism for this which I'm missing ?\n> \n> That would work, but I think the design could be improved.\n> \n> In this series you introduce n-1 new threads, with the existing software\n> ISP thread acting as the n'th thread. I think it would be nicer to have\n> n identical instances of a class to handle the threads, with\n> SoftwareIsp::process() handling the dispatching (with invokeMethod()).\n> Completion would be signalled through signals, the same way it is done\n> now with inputBufferReady and outputBufferReady. The\n> SoftwareIsp::inputReady() and SoftwareIsp::outputReady() functions would\n> be called once per thread, and would handle the required accounting to\n> emit the SoftwareIsp completion signals only after all threads complete\n> their jobs.\n> \n> I haven't investigated in details to see if there would be dragons\n> lurking in dark corners, but my gut feeling is that a design where no\n> thread is special would be cleaner.\n\nThe signal approach is interesting, I'll look into that.\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 C517EC31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 20 Feb 2026 18:51:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D3BC662275;\n\tFri, 20 Feb 2026 19:51:24 +0100 (CET)","from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com\n\t[205.220.168.131])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B789761FBF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 19:51:22 +0100 (CET)","from pps.filterd (m0279864.ppops.net [127.0.0.1])\n\tby mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id\n\t61KEGsbg766579 for <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 18:51:21 GMT","from mail-qv1-f70.google.com (mail-qv1-f70.google.com\n\t[209.85.219.70])\n\tby mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4ces848t0b-1\n\t(version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT)\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 18:51:20 +0000 (GMT)","by mail-qv1-f70.google.com with SMTP id\n\t6a1803df08f44-896f4dcef67so276307136d6.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 10:51:20 -0800 (PST)","from ?IPV6:2001:1c00:2a07:3a01:8e96:3679:b9c:de47?\n\t(2001-1c00-2a07-3a01-8e96-3679-0b9c-de47.cable.dynamic.v6.ziggo.nl.\n\t[2001:1c00:2a07:3a01:8e96:3679:b9c:de47])\n\tby smtp.gmail.com with ESMTPSA id\n\ta640c23a62f3a-b8fc7627fa7sm717855666b.38.2026.02.20.10.51.18\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tFri, 20 Feb 2026 10:51:18 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=qualcomm.com header.i=@qualcomm.com\n\theader.b=\"iQYz9iDw\"; dkim=pass (2048-bit key;\n\tunprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com\n\theader.b=\"LpRyMlcL\"; dkim-atps=neutral","DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h=\n\tcc:content-transfer-encoding:content-type:date:from:in-reply-to\n\t:message-id:mime-version:references:subject:to; s=qcppdkim1; bh=\n\tBIZ0obooV7K0StOdzUSqWE8Rw5IHTz/zxBIjro8UMbE=; b=iQYz9iDwkDNg7MSV\n\tI4UC6NATFKN+e+By0vuozOlNCE8oMBuqBSx/4y+KbpZLiSEM8PCSHQVWbzdIb/BY\n\t7YbJqu4wmoykCuX4eIBUvQIBvtAJtmyW8BGcQIqEHGWLIvsR6/9KFsjBvEDRDnQI\n\tYa9D3+bGlOLmAN8urazW8S2zSG/iXS7QRAYWJkL7/RVpdCVqZMUr2AHdx6xNeRy3\n\t8yJplgSbbfMLlWtsdB6bRFIYpE7pz5qtEbKfN7L9F1dmV8dSNL47zF1xNrdsqdXG\n\tmQ34SyYXrTjIDad2EYJ8PCsgK5wJh6AljtASWIBeL2OL693xGvt7OVWl0iN66spE\n\thK8JLA==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=oss.qualcomm.com; s=google; t=1771613480; x=1772218280;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:in-reply-to:content-language:references\n\t:cc:to:subject:from:user-agent:mime-version:date:message-id:from:to\n\t:cc:subject:date:message-id:reply-to;\n\tbh=BIZ0obooV7K0StOdzUSqWE8Rw5IHTz/zxBIjro8UMbE=;\n\tb=LpRyMlcLQX0mPEciIutlELh8/TfcXrKyPvolS/6ZhoTuEJOoMBrxhKKQiKX21sHoLV\n\tusxd7NVTX2nJWwTwL87OLYTWCNhv5Tth7k3ZFA8CMTzqwj6ZXpy1R7ATN9FVeCsvkAm4\n\tKMxHIFsCnSwipSFTGzLBw5gHqAorejl7NZX417ocoB4/pKsI/fl6hfZjce62QDqNQIjc\n\t/VYlOF0kD321Mfr5m+eEgXX0Y5ZYOuaPSZnmWbZAv8TZXMYq/vOYEo2W3/6nV07b4TOk\n\tSvsKXmIxJVodKBoBcqpoGj7CN75Yzkg9KGyf5ya7cOZJ3xvYzruD6wMBPAnr6SU7MrM5\n\tS6PA=="],"X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1771613480; x=1772218280;\n\th=content-transfer-encoding:in-reply-to:content-language:references\n\t:cc:to:subject:from:user-agent:mime-version:date:message-id:x-gm-gg\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=BIZ0obooV7K0StOdzUSqWE8Rw5IHTz/zxBIjro8UMbE=;\n\tb=rxgSuoxu53iZya3Zsb1cS5PwuYPqE2IAlcd+mYSoblXl+2O9k3LfGG2gr9G3sCUL3l\n\ts+ZOQd0iLFUDncAu0g2SzG9dVyT5qCukVBxjvs57VYJF4yM9E3xMeUa3tOOUjMLt9OCT\n\tH2Zxg5K04Oxx6IsKHcwb+R4AGVvm81V/hv0MfQpZzETElDWizz+KRvNtYASgUm3LjxnO\n\t+xD7fPUC2wzyegc7u4c8XKTonQITW77H1BoUxtM/hp8U8UrxrnoUr+U9nteToGiVIr6q\n\tXSg1uHJwTHAxykqqouaV4TMX+PH5N4tYbXwDERg5Cz9ZBwUyJqoEDxNkFw95hW1RKGvj\n\tiKkA==","X-Forwarded-Encrypted":"i=1;\n\tAJvYcCWGgpbINOpp2P4j+BTbNFiEdgHvPC3aY08ArDZWBx9YuNft5hLcWim4Enh2zuao5JJoGgGJzM0VAv8Un1WUD8A=@lists.libcamera.org","X-Gm-Message-State":"AOJu0YwamrLMlSI7bRGoLyYqgcn/1/1CMAYKgc1q9cq62LObLf2f6ltB\n\twXIZLisyKKUg2ZXRG8XKos30ugv270E0PFdygjQ0F8iLYaBj8/Ld3v/29ebIVv4fiBtk6vM47ZI\n\t/3gHYjNU9B2vdQkz7FWU+/n1+zjU0Ig42LECCBWc2Ujm4EqH2LenmNa5GAF1ZP3YjGd9jn7VNMa\n\ttd","X-Gm-Gg":"AZuq6aL68YZQrsAcYw6QDQl3JsZ5pJgHr+/s4X5AWZpD3dS5zIQdSlrHDfRZyI+G72A\n\tm3k14NtXkmmax294BYlf02YR0vTvALgpDGKQkI0oAV3xQUZCVb96JkJzmexw9vLF4ONjECxVWeL\n\tz0ZVRhCMlyaU8uJoZdt8dUdWG+xpRtblgPm0fBwj40A3fLNifBoSuaTivHCyruGy8rgu/jor13T\n\tmsKG2g8f1wSRdK3MuMWLVzBkWFE5jpFBlBLdYoNtOLdht9CYGmTKzmBGfrkdYjzaJ5SS7ASSo7h\n\tHq5sDYSEIJDhd02qlVhtzqgFm/4c3cOPx1HuOvEkcbmxjxCNVZtDLidsYfnzlxiCEsRhY/J2tjp\n\tLwG2wRlpAjbN+zZDLtkZOckiP4BpUn/b+IQywHDDhNwUZJBrLK1zoL8ysr3Fv7VkKYkuj7oK9Ib\n\tKFs37PSXD34t8SKw6QE7cZT7HJ07bidhjKrRGmkqV0So1JTR4F36Lgw671VOtlAmwHotPyx+4fy\n\tKZTi8x44IDYXICp","X-Received":["by 2002:a05:620a:199a:b0:8c0:cec4:b6fa with SMTP id\n\taf79cd13be357-8cb8ca8c47dmr67300585a.65.1771613479622; \n\tFri, 20 Feb 2026 10:51:19 -0800 (PST)","by 2002:a05:620a:199a:b0:8c0:cec4:b6fa with SMTP id\n\taf79cd13be357-8cb8ca8c47dmr67296385a.65.1771613479040; \n\tFri, 20 Feb 2026 10:51:19 -0800 (PST)"],"Message-ID":"<1a0a1266-5585-40f9-83fc-d836ba66b518@oss.qualcomm.com>","Date":"Fri, 20 Feb 2026 19:51:17 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","From":"Hans de Goede <johannes.goede@oss.qualcomm.com>","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>\n\t<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>\n\t<20260219141500.GQ520738@killaraus.ideasonboard.com>\n\t<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.com>\n\t<20260219172256.GA1619026@killaraus.ideasonboard.com>\n\t<6d0a36ba-3356-47d5-a5cd-57e6ae13a50c@oss.qualcomm.com>\n\t<20260220160923.GE1619026@killaraus.ideasonboard.com>","Content-Language":"en-US, nl","In-Reply-To":"<20260220160923.GE1619026@killaraus.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"7bit","X-Proofpoint-GUID":"AfNaZCcNuCcgJE1yb7p0BWZoTWMIp4wN","X-Authority-Analysis":"v=2.4 cv=PoyergM3 c=1 sm=1 tr=0 ts=6998ad28 cx=c_pps\n\ta=oc9J++0uMp73DTRD5QyR2A==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10\n\ta=HzLeVaNsDn8A:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22\n\ta=Mpw57Om8IfrbqaoTuvik:22 a=GgsMoib0sEa3-_RKJdDe:22\n\ta=CPejxIJ0Whzu2s-k7SoA:9\n\ta=QEXdDO2ut3YA:10 a=iYH6xdkBrDN1Jqds4HTS:22","X-Proofpoint-ORIG-GUID":"AfNaZCcNuCcgJE1yb7p0BWZoTWMIp4wN","X-Proofpoint-Spam-Details-Enc":"AW1haW4tMjYwMjIwMDE1OSBTYWx0ZWRfX3iHHYtexB87s\n\tymj/odkZraKPMj6kZYdSPeWm2drZOxBCWEOZ1JYFhBHSd76yzyPocifjk+roAzZOMkxMaqAC6is\n\tKikdlBsCe7UI998cZj0gL4AbjcNuDCaJf4y+EJueQZI4lQVSOMM5zL9J+LLKfKcmSGtKpS9kG68\n\t6leyZ2qvZhzy+tV647+PDRkwy2CPtuj6plOWFmH/KLbznqGJQmaaGCtrYAKQ2H5gAwhU4grdaHb\n\tvRW+T0EHMXO5cb8zeHMdojDhBdEIKtCim/7A9t960gRRb1MSpv4R74p+poFQu4quLMs8QuxOh5g\n\tOVxct09yfus0LXDKzZniCR6mkagNKuO5mV451RlOGxEG3fsEI/Iw1IdW1k4ywn2Blqi0GrZEyF5\n\tXqBm4CMch2+jMZxvCPzQKlYp/Mm2ZPK+KufDYk36B6YyT5xtH9uK5JI9GFXkLoNrZFUfSP56ojC\n\t/59YCierddCIHA1wkrw==","X-Proofpoint-Virus-Version":"vendor=baseguard\n\tengine=ICAP:2.0.293, Aquarius:18.0.1121, Hydra:6.1.51,\n\tFMLib:17.12.100.49\n\tdefinitions=2026-02-20_02,2026-02-20_03,2025-10-01_01","X-Proofpoint-Spam-Details":"rule=outbound_notspam policy=outbound score=0\n\tclxscore=1015 suspectscore=0 impostorscore=0 priorityscore=1501\n\tadultscore=0\n\tmalwarescore=0 bulkscore=0 phishscore=0 lowpriorityscore=0\n\tspamscore=0\n\tclassifier=typeunknown authscore=0 authtc= authcc= route=outbound\n\tadjust=0\n\treason=mlx scancount=1 engine=8.22.0-2601150000\n\tdefinitions=main-2602200159","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":38268,"web_url":"https://patchwork.libcamera.org/comment/38268/","msgid":"<e674e680-f424-44c4-baaf-a72ae7b36b9f@oss.qualcomm.com>","date":"2026-02-20T20:41:01","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":242,"url":"https://patchwork.libcamera.org/api/people/242/","name":"Hans de Goede","email":"johannes.goede@oss.qualcomm.com"},"content":"Hi,\n\nOn 20-Feb-26 7:51 PM, Hans de Goede wrote:\n> On 20-Feb-26 5:09 PM, Laurent Pinchart wrote:\n\n<snip>\n\n>> In this series you introduce n-1 new threads, with the existing software\n>> ISP thread acting as the n'th thread. I think it would be nicer to have\n>> n identical instances of a class to handle the threads, with\n>> SoftwareIsp::process() handling the dispatching (with invokeMethod()).\n>> Completion would be signalled through signals, the same way it is done\n>> now with inputBufferReady and outputBufferReady. The\n>> SoftwareIsp::inputReady() and SoftwareIsp::outputReady() functions would\n>> be called once per thread, and would handle the required accounting to\n>> emit the SoftwareIsp completion signals only after all threads complete\n>> their jobs.\n>>\n>> I haven't investigated in details to see if there would be dragons\n>> lurking in dark corners, but my gut feeling is that a design where no\n>> thread is special would be cleaner.\n> \n> The signal approach is interesting, I'll look into that.\n\nOk, so this does not work that well, for the CPU ISP we need to\ncreate mappings for the in and out buffer atm we use local\nMappedFrameBuffer objects relying on the destructor unmapping\nthe buffers when the desctructors run when the objects run out\nof context.\n\n\nAn other problem is that the emitted signals when processing\nis done take the input and output Framebuffers passed to\nthe process() function. So if we move the completion\nto a signal handler then we need to temporarily store those\nFramebuffer pointers as class variables.\n\nSo all in all just waiting in the process() function for all threads\nto complete with the threads signalling a conditional variable\nworks better from design pov. So i'm going to go with that for v2.\n\nRegards,\n\nHans\n\n\n\n\n\n\n\n> \n> Regards,\n> \n> Hans\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 52151C0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 20 Feb 2026 20:41:08 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8EE416226A;\n\tFri, 20 Feb 2026 21:41:07 +0100 (CET)","from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com\n\t[205.220.180.131])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 637A861FBF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 21:41:06 +0100 (CET)","from pps.filterd (m0279868.ppops.net [127.0.0.1])\n\tby mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id\n\t61KKcaxX512862 for <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 20:41:05 GMT","from mail-qk1-f197.google.com (mail-qk1-f197.google.com\n\t[209.85.222.197])\n\tby mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4cechhb035-1\n\t(version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT)\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 20:41:04 +0000 (GMT)","by mail-qk1-f197.google.com with SMTP id\n\taf79cd13be357-8ca3ef536ddso2472929985a.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Feb 2026 12:41:04 -0800 (PST)","from ?IPV6:2001:1c00:2a07:3a01:8e96:3679:b9c:de47?\n\t(2001-1c00-2a07-3a01-8e96-3679-0b9c-de47.cable.dynamic.v6.ziggo.nl.\n\t[2001:1c00:2a07:3a01:8e96:3679:b9c:de47])\n\tby smtp.gmail.com with ESMTPSA id\n\ta640c23a62f3a-b9084c5cb60sm7100366b.2.2026.02.20.12.41.02\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tFri, 20 Feb 2026 12:41:03 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=qualcomm.com header.i=@qualcomm.com\n\theader.b=\"oOi6o38A\"; dkim=pass (2048-bit key;\n\tunprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com\n\theader.b=\"Lm/Emmtw\"; dkim-atps=neutral","DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h=\n\tcc:content-transfer-encoding:content-type:date:from:in-reply-to\n\t:message-id:mime-version:references:subject:to; s=qcppdkim1; bh=\n\tUTGdAou91I3rdYFSz5qmCuiqMcgwiCK/+bS5nOocGos=; b=oOi6o38AJySdc9+X\n\tWXjxFVPyy07SPV8SlthfArgYY9LcVFGOYfMB62nIOmVwbBCa+c3A0yv5PEnZr6Nl\n\tFj7bGdtNFt5YHUqL79qjsR0yrOw8V1Sp5QA66GT8QbDGRUIL9p6ngyOa8ByNBilC\n\tau5S2Hhxc3XQ80OwnLDFvHtBAnSmPMBgCmML4XvbaYLxUDcMtHQEBModGk0xvwYt\n\t0UhomdIvl1N2xdp89LfHcQiFVIpB8ob9UaOeCITVQJpw+Zo6FvcxWtBsKIBEDYDg\n\tawzWP9BPFfRJGjXbv30KqKC82FYYPLp0Uk/bdrkquLle6aO1o3ss3Jqb4DgZ0VYX\n\tbnE1gQ==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=oss.qualcomm.com; s=google; t=1771620064; x=1772224864;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:in-reply-to:content-language:references\n\t:cc:to:from:subject:user-agent:mime-version:date:message-id:from:to\n\t:cc:subject:date:message-id:reply-to;\n\tbh=UTGdAou91I3rdYFSz5qmCuiqMcgwiCK/+bS5nOocGos=;\n\tb=Lm/EmmtwGAwArOGX5n31tuesaP42uVxvLDALzEyT4BKjIa7ocGoj70+SLWor9YzaR/\n\tmdmogpfKtmohevWm+XWfKOTWaFkcDtoZP5Yz7Ip4x3Y73AKmzhl3ZPbydNQeXdIvRY6S\n\tnabsdTmZ8QZE3M5JoSF8K/GxRVfZwZpNZRQr2gmF8w2dEBO9ONnWEqBgbbyfipKlzTMP\n\t+6+16r474uPayQzDWgMXrvNWnRTeB/KLfqpQ+pSX7t++o0V/8CtOYgRpMa6v9RYwomeU\n\tetZ/AbzxGrfCzpTdG8NYm1e9Ul7AsDBaTB6vk1SWkzdRNQq4v8v7dHT/GhgmrhTVaYnN\n\tba2Q=="],"X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1771620064; x=1772224864;\n\th=content-transfer-encoding:in-reply-to:content-language:references\n\t:cc:to:from:subject:user-agent:mime-version:date:message-id:x-gm-gg\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=UTGdAou91I3rdYFSz5qmCuiqMcgwiCK/+bS5nOocGos=;\n\tb=IADuTmkXpbfX0T3XyimkEEKU9oJz+FPFQVR8H5/E/5TYjZfyhdDcMBISSyXZLohMzT\n\t+85jtGD+xqaAuw36uM/IK/624686YcTgVm1XpFcTwPgI5XZpi/4+9FschwcOuPxR4aWF\n\t7l6yd+m8tXHWY5wuJpvkajpNajnuWGfghqnNWT6S7YWb+BjjynUsszcx6PdQa4Vdh4yB\n\t8CoQc/gXYgXXVIU4OfptJnvNRL2qOSbv2pG6dLBZZUg9vKjjwCIcaOCLNOG9UObKLI9w\n\trHOafSMiPeUxv6ERGBlAKzyEcrDrsZ3ZcymHyFJEqNFNlnh+CwEipMwq76NQwnmT6kTH\n\tDq+g==","X-Forwarded-Encrypted":"i=1;\n\tAJvYcCWkqvzS/UTH+CuuqQ+TalSrLnbIpJ8a+o3BJE5586o8/icKpPzo9kL7oQIV32f809T2xtcz7+Vuxzf7OwV0Rx0=@lists.libcamera.org","X-Gm-Message-State":"AOJu0Yxnn+yrohQnOMkTtaHNF0YRjjG5dg/0iFpwA+fObyYeqRTNXHD/\n\tQjyBJrrqaoPXYazm6w0KWqv0ED2CI9xZxD6jHW9z3WaksnjIbL49OmfxS3zZJGFO7EUy3Dm3h0D\n\tMlvDtaFebk/2OwduDFUXtngpnNsG/KDe7e7Mv+2KmLkG7I2PD142TjBCyVgcib5fbsYJSMqhv+D\n\t0B","X-Gm-Gg":"AZuq6aJICkJdNJI2dY3qp0EKaN/LKXSYoUktTBW9F/pP+8Rs0lvxynwN0VflvCQn66G\n\tvV96FW46LzUXtE+vl7QLcaW9XjHMojXiAvVw4cFH2zXqgJw27UhOSXKvUbIaQApZyxfmx2Q1zKx\n\tGNVNJ+MmM56ADQPEstnjWzzN8zggbctx7VrcXzpjxDYlV5iFxdtGAJXvZnWzOyidItaYXSI5m5S\n\tClfdMZDLlc49x+UCPJa29rYIBFSCwc/9nWUVmknlKuuo7OZUzzejDJ2nZ/YOO30OoudPtl0XyNn\n\tzJhKiij9BF5uJ1e9dpshlQQYuJZkzFBWsACqhZwWJH0bAkkEXtrPm+xV7WESDjv5R4UZ+JOdMLe\n\tOWnEjy5gMy9k4HT+UH6psiyPbVYZeGxy69kTbOZO1SOdJ7xj3UESFkTwJ9Q/OAf8n60HWXvAn6m\n\tanFi8MkwQI5797LVhYvkCz0Xd5MtKxhIomv8Ls38UrzFQJBQUzITsGbTzAkGSTJSwn0zZN/yv/E\n\timYjwxbHxga15oo","X-Received":["by 2002:a05:620a:28c3:b0:8b2:f2c5:e7f6 with SMTP id\n\taf79cd13be357-8cb8ca65f56mr111501485a.37.1771620064281; \n\tFri, 20 Feb 2026 12:41:04 -0800 (PST)","by 2002:a05:620a:28c3:b0:8b2:f2c5:e7f6 with SMTP id\n\taf79cd13be357-8cb8ca65f56mr111498985a.37.1771620063859; \n\tFri, 20 Feb 2026 12:41:03 -0800 (PST)"],"Message-ID":"<e674e680-f424-44c4-baaf-a72ae7b36b9f@oss.qualcomm.com>","Date":"Fri, 20 Feb 2026 21:41:01 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","From":"Hans de Goede <johannes.goede@oss.qualcomm.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>\n\t<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>\n\t<20260219141500.GQ520738@killaraus.ideasonboard.com>\n\t<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.com>\n\t<20260219172256.GA1619026@killaraus.ideasonboard.com>\n\t<6d0a36ba-3356-47d5-a5cd-57e6ae13a50c@oss.qualcomm.com>\n\t<20260220160923.GE1619026@killaraus.ideasonboard.com>\n\t<1a0a1266-5585-40f9-83fc-d836ba66b518@oss.qualcomm.com>","Content-Language":"en-US, nl","In-Reply-To":"<1a0a1266-5585-40f9-83fc-d836ba66b518@oss.qualcomm.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"7bit","X-Proofpoint-GUID":"e04OOmaQcqn8slMuz9raX_EiRlSn2M3v","X-Proofpoint-Spam-Details-Enc":"AW1haW4tMjYwMjIwMDE3MSBTYWx0ZWRfXzt7eJmDAF9lU\n\tIaNkSn/T4fXeg1f5si5Uh0l99bsUZJqtxItj/UOsdwncV3ajjl2tJpZVBuGu3ns9gNeHcPIN1Xo\n\tl3JxGxjtsUTx+0jMJrixCbTjNNlxVEJD4Sn4yDk/Vnrqh8Xl3iz3BcdTw/rGIC6uUJlctyprnny\n\tK80CB/US9zwAPkVdnfsfM9olrdKzzPnYV0K9G9bk9bAiSA9kz6dZk3d9saKr4+4iSwXfrhH4BLj\n\tdgVYLsFRObntho0OPC6ePJw4tjwSu5kBD1igkwbeqzIabT12ox+yy1d927Ztc35M6Dh0W3PQ2DP\n\tk3W+DM1an1d2/fhuLxVg+eN+Fnfqz/m4F/iGAEGpFaRqjZxDzCufyeJdj1vdNngQgKm23dngZUO\n\tmsHfG46J2bMqECgtnx9aAoqes0RQPKQ3ZUdQhYgeeuE0mwu5i6/6kIGvCKE7Zf/8YePNJPDw6Ix\n\tq1JG6TxS/N6k+bsyCtQ==","X-Proofpoint-ORIG-GUID":"e04OOmaQcqn8slMuz9raX_EiRlSn2M3v","X-Authority-Analysis":"v=2.4 cv=KYzfcAYD c=1 sm=1 tr=0 ts=6998c6e0 cx=c_pps\n\ta=50t2pK5VMbmlHzFWWp8p/g==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10\n\ta=HzLeVaNsDn8A:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22\n\ta=Mpw57Om8IfrbqaoTuvik:22 a=GgsMoib0sEa3-_RKJdDe:22\n\ta=nT6wVomHNW7IXlpjn0EA:9\n\ta=QEXdDO2ut3YA:10 a=IoWCM6iH3mJn3m4BftBB:22","X-Proofpoint-Virus-Version":"vendor=baseguard\n\tengine=ICAP:2.0.293, Aquarius:18.0.1121, Hydra:6.1.51,\n\tFMLib:17.12.100.49\n\tdefinitions=2026-02-20_03,2026-02-20_04,2025-10-01_01","X-Proofpoint-Spam-Details":"rule=outbound_notspam policy=outbound score=0\n\tspamscore=0 phishscore=0 impostorscore=0 bulkscore=0 clxscore=1015\n\tlowpriorityscore=0 suspectscore=0 malwarescore=0 priorityscore=1501\n\tadultscore=0 classifier=typeunknown authscore=0 authtc= authcc=\n\troute=outbound adjust=0 reason=mlx scancount=1\n\tengine=8.22.0-2601150000\n\tdefinitions=main-2602200171","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":38303,"web_url":"https://patchwork.libcamera.org/comment/38303/","msgid":"<20260225220059.GA2729361@killaraus.ideasonboard.com>","date":"2026-02-25T22:00:59","subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Fri, Feb 20, 2026 at 09:41:01PM +0100, Hans de Goede wrote:\n> Hi,\n> \n> On 20-Feb-26 7:51 PM, Hans de Goede wrote:\n> > On 20-Feb-26 5:09 PM, Laurent Pinchart wrote:\n> \n> <snip>\n> \n> >> In this series you introduce n-1 new threads, with the existing software\n> >> ISP thread acting as the n'th thread. I think it would be nicer to have\n> >> n identical instances of a class to handle the threads, with\n> >> SoftwareIsp::process() handling the dispatching (with invokeMethod()).\n> >> Completion would be signalled through signals, the same way it is done\n> >> now with inputBufferReady and outputBufferReady. The\n> >> SoftwareIsp::inputReady() and SoftwareIsp::outputReady() functions would\n> >> be called once per thread, and would handle the required accounting to\n> >> emit the SoftwareIsp completion signals only after all threads complete\n> >> their jobs.\n> >>\n> >> I haven't investigated in details to see if there would be dragons\n> >> lurking in dark corners, but my gut feeling is that a design where no\n> >> thread is special would be cleaner.\n> > \n> > The signal approach is interesting, I'll look into that.\n> \n> Ok, so this does not work that well, for the CPU ISP we need to\n> create mappings for the in and out buffer atm we use local\n> MappedFrameBuffer objects relying on the destructor unmapping\n> the buffers when the desctructors run when the objects run out\n> of context.\n> \n> \n> An other problem is that the emitted signals when processing\n> is done take the input and output Framebuffers passed to\n> the process() function. So if we move the completion\n> to a signal handler then we need to temporarily store those\n> Framebuffer pointers as class variables.\n> \n> So all in all just waiting in the process() function for all threads\n> to complete with the threads signalling a conditional variable\n> works better from design pov. So i'm going to go with that for v2.\n\nI still feel a symmetrical design would be nicer, but we can get there\nincrementally. Let's focus on progressing the patch series.","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 F2786C3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 25 Feb 2026 22:01:03 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C1AD9622E5;\n\tWed, 25 Feb 2026 23:01:02 +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 2EDD761FA0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 25 Feb 2026 23:01:01 +0100 (CET)","from killaraus.ideasonboard.com (unknown [194.75.195.10])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id F0FD112D6; \n\tWed, 25 Feb 2026 23:00:02 +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=\"f6kYyN+i\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1772056803;\n\tbh=HsCbJHXf0p5yGgVhrir4HX+6aFlrAPp42qxRgufCFNU=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=f6kYyN+iEEolsFnQkCrlLGq6Rd4iGz9my1yspNiJranmLkQFN2NTV20cBuTiec1Ke\n\tIYgnlw8mV5SOHVVKByHblGz3S5lKCUOxwH1bjcN1JNRuCOrKha9M5asfzr6tKIhxO+\n\tK0Zs9F5sOxFaAozUpgm/dxvM9HCIit8esFAbxZck=","Date":"Wed, 25 Feb 2026 22:00:59 +0000","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Hans de Goede <johannes.goede@oss.qualcomm.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org, Milan Zamazal <mzamazal@redhat.com>","Subject":"Re: [PATCH 5/5] software_isp: debayer_cpu: Add multi-threading\n\tsupport","Message-ID":"<20260225220059.GA2729361@killaraus.ideasonboard.com>","References":"<20260216190204.106922-1-johannes.goede@oss.qualcomm.com>\n\t<20260216190204.106922-6-johannes.goede@oss.qualcomm.com>\n\t<6c37eed5-3e03-49ac-bfea-be58d9b98d38@ideasonboard.com>\n\t<20260219141500.GQ520738@killaraus.ideasonboard.com>\n\t<4d6a4801-b270-4f99-a1b8-8cbb83bae664@oss.qualcomm.com>\n\t<20260219172256.GA1619026@killaraus.ideasonboard.com>\n\t<6d0a36ba-3356-47d5-a5cd-57e6ae13a50c@oss.qualcomm.com>\n\t<20260220160923.GE1619026@killaraus.ideasonboard.com>\n\t<1a0a1266-5585-40f9-83fc-d836ba66b518@oss.qualcomm.com>\n\t<e674e680-f424-44c4-baaf-a72ae7b36b9f@oss.qualcomm.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<e674e680-f424-44c4-baaf-a72ae7b36b9f@oss.qualcomm.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>"}}]