Patch Detail
Show a patch.
GET /api/patches/26364/?format=api
{ "id": 26364, "url": "https://patchwork.libcamera.org/api/patches/26364/?format=api", "web_url": "https://patchwork.libcamera.org/patch/26364/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260325151416.2114564-23-stefan.klug@ideasonboard.com>", "date": "2026-03-25T15:13:54", "name": "[v2,22/32] ipa: rkisp1: Lazy initialise frame context", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "4449052a74460fb2ec91a0449bbbd43dbdcdeb5a", "submitter": { "id": 184, "url": "https://patchwork.libcamera.org/api/people/184/?format=api", "name": "Stefan Klug", "email": "stefan.klug@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/26364/mbox/", "series": [ { "id": 5849, "url": "https://patchwork.libcamera.org/api/series/5849/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5849", "date": "2026-03-25T15:13:32", "name": "rkisp1: pipeline rework for PFC", "version": 2, "mbox": "https://patchwork.libcamera.org/series/5849/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/26364/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/26364/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 768C1C3305\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 25 Mar 2026 15:15:45 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E932A62CB1;\n\tWed, 25 Mar 2026 16:15:44 +0100 (CET)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4699162C44\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 25 Mar 2026 16:15:43 +0100 (CET)", "from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:b16a:5ed9:4ada:a95a])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 3005B1AFB; \n\tWed, 25 Mar 2026 16:14:25 +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=\"Z3dVqU0S\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1774451665;\n\tbh=ZXy9ncr79oa95/Dp9fjeSQ8CeGOzW7j447IMXY2MLuw=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=Z3dVqU0SMyeZ8oqvTxTbIO3sZIo3olKW+vCVjBfk4p0EYjsvqjz4xS+AqL3TlW9by\n\t2fLQKCZ8Aq7dy6Q6UNk2UkYxs0q2x0655WfVZBB6JzebFxf2P5NmE95w1AOtWgiqaA\n\t0qPe4iSsBBUwEAp5WWsD5ygeQI/LMQW/qq/4g2mo=", "From": "Stefan Klug <stefan.klug@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Stefan Klug <stefan.klug@ideasonboard.com>", "Subject": "[PATCH v2 22/32] ipa: rkisp1: Lazy initialise frame context", "Date": "Wed, 25 Mar 2026 16:13:54 +0100", "Message-ID": "<20260325151416.2114564-23-stefan.klug@ideasonboard.com>", "X-Mailer": "git-send-email 2.51.0", "In-Reply-To": "<20260325151416.2114564-1-stefan.klug@ideasonboard.com>", "References": "<20260325151416.2114564-1-stefan.klug@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "For per frame control we want to tick the IPA by the sensor frame\nsequence instead of the request sequence. This has the side effect\nthat the IPA must be able to cope with situations where a frame context\nis required for a frame that was not queued before (computeParams is\ncalled without a corresponding request) or processStats is called for an\nunexpected sequence number (because a scratch buffer was used on kernel\nside).\n\nWith the current FCQueue implementation this is not easy to model, as it\nhas distinct calls for alloc() and get(). Simplify that by passing the\nFCQueue a callback that it can call to initialize a frame context when\nneeded.\n\nThis has the added benefit that the FCQueue can collate controls for\nrequests that were queued in too late. This simplifies the logic on the\nIPA side.\n\nAs fetching an uninitialized frame context is no error anymore, demote the\ncorresponding warnings to debug messages.\n\nSigned-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n\n---\n\nChanges in v2:\n- Rewrote the patch to use a callback based mechanism.\n---\n src/ipa/ipu3/ipu3.cpp | 18 ++++--\n src/ipa/libipa/fc_queue.h | 97 +++++++++++--------------------\n src/ipa/mali-c55/mali-c55.cpp | 20 +++++--\n src/ipa/rkisp1/algorithms/awb.cpp | 2 +\n src/ipa/rkisp1/rkisp1.cpp | 25 ++++----\n src/ipa/simple/soft_simple.cpp | 18 ++++--\n 6 files changed, 92 insertions(+), 88 deletions(-)", "diff": "diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp\nindex 92f5bd072134..ed4ea8d31866 100644\n--- a/src/ipa/ipu3/ipu3.cpp\n+++ b/src/ipa/ipu3/ipu3.cpp\n@@ -172,6 +172,8 @@ private:\n \n \tvoid setControls(unsigned int frame);\n \tvoid calculateBdsGrid(const Size &bdsOutputSize);\n+\tvoid initializeFrameContext(IPAFrameContext &frameContext,\n+\t\t\t\t const ControlList &controls);\n \n \tstd::map<unsigned int, MappedFrameBuffer> buffers_;\n \n@@ -190,6 +192,10 @@ private:\n IPAIPU3::IPAIPU3()\n \t: context_(kMaxFrameContexts)\n {\n+\tcontext_.frameContexts.setInitCallback(\n+\t\t[this](IPAFrameContext &fc, const ControlList &c) {\n+\t\t\tthis->initializeFrameContext(fc, c);\n+\t\t});\n }\n \n std::string IPAIPU3::logPrefix() const\n@@ -561,7 +567,7 @@ void IPAIPU3::computeParams(const uint32_t frame, const uint32_t bufferId)\n \t */\n \tparams->use = {};\n \n-\tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);\n \n \tfor (const auto &algo : algorithms())\n \t\talgo->prepare(context_, frame, frameContext, params);\n@@ -594,7 +600,7 @@ void IPAIPU3::processStats(const uint32_t frame,\n \tconst ipu3_uapi_stats_3a *stats =\n \t\treinterpret_cast<ipu3_uapi_stats_3a *>(mem.data());\n \n-\tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);\n \n \tframeContext.sensor.exposure = sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n \tframeContext.sensor.gain = camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());\n@@ -627,10 +633,14 @@ void IPAIPU3::processStats(const uint32_t frame,\n */\n void IPAIPU3::queueRequest(const uint32_t frame, const ControlList &controls)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.alloc(frame);\n+\tcontext_.frameContexts.getOrInitContext(frame, controls);\n+}\n \n+void IPAIPU3::initializeFrameContext(IPAFrameContext &frameContext,\n+\t\t\t\t const ControlList &controls)\n+{\n \tfor (const auto &algo : algorithms())\n-\t\talgo->queueRequest(context_, frame, frameContext, controls);\n+\t\talgo->queueRequest(context_, frameContext.frame(), frameContext, controls);\n }\n \n /**\ndiff --git a/src/ipa/libipa/fc_queue.h b/src/ipa/libipa/fc_queue.h\nindex 812022c496ed..633bf646d88d 100644\n--- a/src/ipa/libipa/fc_queue.h\n+++ b/src/ipa/libipa/fc_queue.h\n@@ -11,6 +11,7 @@\n #include <vector>\n \n #include <libcamera/base/log.h>\n+#include <libcamera/controls.h>\n \n namespace libcamera {\n \n@@ -27,52 +28,33 @@ struct FrameContext {\n private:\n \ttemplate<typename T> friend class FCQueue;\n \tuint32_t frame_;\n-\tbool initialised_ = false;\n };\n \n template<typename FC>\n class FCQueue\n {\n public:\n+\tusing InitCallback = std::function<void(FC &, const ControlList &)>;\n+\n \tFCQueue(unsigned int size)\n \t\t: contexts_(size)\n \t{\n \t}\n \n+\tvoid setInitCallback(const InitCallback &cb)\n+\t{\n+\t\tinitCallback_ = cb;\n+\t}\n+\n \tvoid clear()\n \t{\n \t\tfor (FC &ctx : contexts_) {\n-\t\t\tctx.initialised_ = false;\n \t\t\tctx.frame_ = 0;\n \t\t}\n+\t\tinitialized_ = false;\n \t}\n \n-\tFC &alloc(const uint32_t frame)\n-\t{\n-\t\tFC &fc = contexts_[frame % contexts_.size()];\n-\t\tFrameContext &frameContext = fc;\n-\n-\t\t/*\n-\t\t * Do not re-initialise if a get() call has already fetched this\n-\t\t * frame context to preseve the context.\n-\t\t *\n-\t\t * \\todo If the the sequence number of the context to initialise\n-\t\t * is smaller than the sequence number of the queue slot to use,\n-\t\t * it means that we had a serious request underrun and more\n-\t\t * frames than the queue size has been produced since the last\n-\t\t * time the application has queued a request. Does this deserve\n-\t\t * an error condition ?\n-\t\t */\n-\t\tif (frame != 0 && frame <= frameContext.frame_)\n-\t\t\tLOG(FCQueue, Warning)\n-\t\t\t\t<< \"Frame \" << frame << \" already initialised\";\n-\t\telse\n-\t\t\tinit(fc, frame);\n-\n-\t\treturn fc;\n-\t}\n-\n-\tFC &get(uint32_t frame)\n+\tFC &getOrInitContext(unsigned int frame, const ControlList &controls = {})\n \t{\n \t\tFC &fc = contexts_[frame % contexts_.size()];\n \t\tFrameContext &frameContext = fc;\n@@ -90,51 +72,42 @@ public:\n \t\t\t\t\t << \" has been overwritten by \"\n \t\t\t\t\t << frameContext.frame_;\n \n-\t\tif (frame == 0 && !frameContext.initialised_) {\n-\t\t\t/*\n-\t\t\t * If the IPA calls get() at start() time it will get an\n-\t\t\t * un-intialized FrameContext as the below \"frame ==\n-\t\t\t * frameContext.frame_\" check will return success\n-\t\t\t * because FrameContexts are zeroed at creation time.\n-\t\t\t *\n-\t\t\t * Make sure the FrameContext gets initialised if get()\n-\t\t\t * is called before alloc() by the IPA for frame#0.\n-\t\t\t */\n-\t\t\tinit(fc, frame);\n-\n+\t\tif (initialized_ && frame == frameContext.frame_) {\n+\t\t\tif (!controls.empty()) {\n+\t\t\t\t/* Too late to apply the controls. Store them for later. */\n+\t\t\t\tLOG(FCQueue, Warning)\n+\t\t\t\t\t<< \"Request underrun. Controls for frame \"\n+\t\t\t\t\t<< frame << \" are delayed \";\n+\t\t\t\tcontrolsToApply_.merge(controls,\n+\t\t\t\t\t\t ControlList::MergePolicy::OverwriteExisting);\n+\t\t\t}\n+\t\t\tLOG(FCQueue, Debug) << \"Got \" << frame;\n \t\t\treturn fc;\n \t\t}\n \n-\t\tif (frame == frameContext.frame_)\n-\t\t\treturn fc;\n+\t\tconst ControlList *controls2 = &controls;\n+\t\tif (!controlsToApply_.empty()) {\n+\t\t\tLOG(FCQueue, Debug) << \"Applied late controls on frame\" << frame;\n+\t\t\tcontrolsToApply_.merge(controls, ControlList::MergePolicy::OverwriteExisting);\n+\t\t\tcontrols2 = &controlsToApply_;\n+\t\t}\n \n-\t\t/*\n-\t\t * The frame context has been retrieved before it was\n-\t\t * initialised through the initialise() call. This indicates an\n-\t\t * algorithm attempted to access a Frame context before it was\n-\t\t * queued to the IPA. Controls applied for this request may be\n-\t\t * left unhandled.\n-\t\t *\n-\t\t * \\todo Set an error flag for per-frame control errors.\n-\t\t */\n-\t\tLOG(FCQueue, Warning)\n-\t\t\t<< \"Obtained an uninitialised FrameContext for \" << frame;\n+\t\tLOG(FCQueue, Debug) << \"Init \" << frame;\n \n-\t\tinit(fc, frame);\n+\t\tfc = {};\n+\t\tframeContext.frame_ = frame;\n+\t\tinitCallback_(fc, *controls2);\n+\t\tinitialized_ = true;\n+\t\tcontrolsToApply_.clear();\n \n \t\treturn fc;\n \t}\n \n private:\n-\tvoid init(FC &fc, const uint32_t frame)\n-\t{\n-\t\tfc = {};\n-\t\tFrameContext &frameContext = fc;\n-\t\tframeContext.frame_ = frame;\n-\t\tframeContext.initialised_ = true;\n-\t}\n-\n \tstd::vector<FC> contexts_;\n+\tInitCallback initCallback_;\n+\tControlList controlsToApply_;\n+\tbool initialized_;\n };\n \n } /* namespace ipa */\ndiff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp\nindex fd5c9563d6c3..5bd112b5f30a 100644\n--- a/src/ipa/mali-c55/mali-c55.cpp\n+++ b/src/ipa/mali-c55/mali-c55.cpp\n@@ -71,6 +71,8 @@ private:\n \tvoid updateControls(const IPACameraSensorInfo &sensorInfo,\n \t\t\t const ControlInfoMap &sensorControls,\n \t\t\t ControlInfoMap *ipaControls);\n+\tvoid initializeFrameContext(IPAFrameContext &frameContext,\n+\t\t\t\t const ControlList &controls);\n \tvoid setControls();\n \n \tstd::map<unsigned int, MappedFrameBuffer> buffers_;\n@@ -91,6 +93,10 @@ namespace {\n IPAMaliC55::IPAMaliC55()\n \t: context_(kMaxFrameContexts)\n {\n+\tcontext_.frameContexts.setInitCallback(\n+\t\t[this](IPAFrameContext &fc, const ControlList &c) {\n+\t\t\tthis->initializeFrameContext(fc, c);\n+\t\t});\n }\n \n std::string IPAMaliC55::logPrefix() const\n@@ -320,21 +326,25 @@ void IPAMaliC55::unmapBuffers(const std::vector<IPABuffer> &buffers)\n \t}\n }\n \n-void IPAMaliC55::queueRequest(const uint32_t request, const ControlList &controls)\n+void IPAMaliC55::queueRequest(const uint32_t frame, const ControlList &controls)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.alloc(request);\n+\tcontext_.frameContexts.getOrInitContext(frame, controls);\n+}\n \n+void IPAMaliC55::initializeFrameContext(IPAFrameContext &frameContext,\n+\t\t\t\t\tconst ControlList &controls)\n+{\n \tfor (const auto &a : algorithms()) {\n \t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n \n-\t\talgo->queueRequest(context_, request, frameContext, controls);\n+\t\talgo->queueRequest(context_, frameContext.frame(), frameContext, controls);\n \t}\n }\n \n void IPAMaliC55::fillParams(unsigned int request,\n \t\t\t [[maybe_unused]] uint32_t bufferId)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.get(request);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(request);\n \tMaliC55Params params(buffers_.at(bufferId).planes()[0]);\n \n \tfor (const auto &algo : algorithms())\n@@ -346,7 +356,7 @@ void IPAMaliC55::fillParams(unsigned int request,\n void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,\n \t\t\t const ControlList &sensorControls)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.get(request);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(request);\n \tconst mali_c55_stats_buffer *stats = nullptr;\n \n \tstats = reinterpret_cast<mali_c55_stats_buffer *>(\ndiff --git a/src/ipa/rkisp1/algorithms/awb.cpp b/src/ipa/rkisp1/algorithms/awb.cpp\nindex f83da545be85..f2b387e09ac7 100644\n--- a/src/ipa/rkisp1/algorithms/awb.cpp\n+++ b/src/ipa/rkisp1/algorithms/awb.cpp\n@@ -174,6 +174,8 @@ void Awb::queueRequest(IPAContext &context,\n \tawbAlgo_->handleControls(controls);\n \n \tframeContext.awb.autoEnabled = awb.autoEnabled;\n+\tframeContext.awb.gains = awb.automatic.gains;\n+\tframeContext.awb.temperatureK = awb.automatic.temperatureK;\n \n \tif (awb.autoEnabled)\n \t\treturn;\ndiff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp\nindex 88cf6afc219d..d0c753976dd4 100644\n--- a/src/ipa/rkisp1/rkisp1.cpp\n+++ b/src/ipa/rkisp1/rkisp1.cpp\n@@ -8,6 +8,7 @@\n #include <algorithm>\n #include <array>\n #include <chrono>\n+#include <functional>\n #include <stdint.h>\n #include <string.h>\n \n@@ -67,8 +68,7 @@ public:\n \n \tvoid queueRequest(const uint32_t frame, const ControlList &controls) override;\n \tvoid computeParams(const uint32_t frame, const uint32_t bufferId) override;\n-\tvoid initializeFrameContext(const uint32_t frame,\n-\t\t\t\t IPAFrameContext &frameContext,\n+\tvoid initializeFrameContext(IPAFrameContext &frameContext,\n \t\t\t\t const ControlList &controls);\n \tvoid processStats(const uint32_t frame, const uint32_t bufferId,\n \t\t\t const ControlList &sensorControls) override;\n@@ -131,6 +131,10 @@ const ControlInfoMap::Map rkisp1Controls{\n IPARkISP1::IPARkISP1()\n \t: context_(kMaxFrameContexts)\n {\n+\tcontext_.frameContexts.setInitCallback(\n+\t\t[this](IPAFrameContext &fc, const ControlList &c) {\n+\t\t\tthis->initializeFrameContext(fc, c);\n+\t\t});\n }\n \n std::string IPARkISP1::logPrefix() const\n@@ -217,8 +221,7 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,\n \n void IPARkISP1::start(const ControlList &controls, StartResult *result)\n {\n-\tIPAFrameContext frameContext = {};\n-\tinitializeFrameContext(0, frameContext, controls);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(0, controls);\n \tresult->controls = getSensorControls(frameContext);\n \tresult->code = 0;\n }\n@@ -336,27 +339,23 @@ void IPARkISP1::unmapBuffers(const std::vector<unsigned int> &ids)\n \n void IPARkISP1::queueRequest(const uint32_t frame, const ControlList &controls)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.alloc(frame);\n \tcontext_.debugMetadata.enableByControl(controls);\n-\n-\tinitializeFrameContext(frame, frameContext, controls);\n+\tcontext_.frameContexts.getOrInitContext(frame, controls);\n }\n \n-void IPARkISP1::initializeFrameContext(const uint32_t frame,\n-\t\t\t\t IPAFrameContext &frameContext,\n-\t\t\t\t const ControlList &controls)\n+void IPARkISP1::initializeFrameContext(IPAFrameContext &fc, const ControlList &controls)\n {\n \tfor (const auto &a : algorithms()) {\n \t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n \t\tif (algo->disabled_)\n \t\t\tcontinue;\n-\t\talgo->queueRequest(context_, frame, frameContext, controls);\n+\t\talgo->queueRequest(context_, fc.frame(), fc, controls);\n \t}\n }\n \n void IPARkISP1::computeParams(const uint32_t frame, const uint32_t bufferId)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);\n \n \t/*\n \t * \\todo: This needs discussion. In raw mode, computeParams is\n@@ -385,7 +384,7 @@ void IPARkISP1::computeParams(const uint32_t frame, const uint32_t bufferId)\n void IPARkISP1::processStats(const uint32_t frame, const uint32_t bufferId,\n \t\t\t const ControlList &sensorControls)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);\n \n \t/*\n \t * In raw capture mode, the ISP is bypassed and no statistics buffer is\ndiff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp\nindex 7d25bdd26017..394107606713 100644\n--- a/src/ipa/simple/soft_simple.cpp\n+++ b/src/ipa/simple/soft_simple.cpp\n@@ -47,6 +47,10 @@ public:\n \tIPASoftSimple()\n \t\t: context_(kMaxFrameContexts)\n \t{\n+\t\tcontext_.frameContexts.setInitCallback(\n+\t\t\t[this](IPAFrameContext &fc, const ControlList &c) {\n+\t\t\t\tthis->initializeFrameContext(fc, c);\n+\t\t\t});\n \t}\n \n \t~IPASoftSimple();\n@@ -73,6 +77,8 @@ protected:\n \n private:\n \tvoid updateExposure(double exposureMSV);\n+\tvoid initializeFrameContext(IPAFrameContext &frameContext,\n+\t\t\t\t const ControlList &controls);\n \n \tDebayerParams *params_;\n \tSwIspStats *stats_;\n@@ -277,17 +283,21 @@ void IPASoftSimple::stop()\n \n void IPASoftSimple::queueRequest(const uint32_t frame, const ControlList &controls)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.alloc(frame);\n+\tcontext_.frameContexts.getOrInitContext(frame, controls);\n+}\n \n+void IPASoftSimple::initializeFrameContext(IPAFrameContext &frameContext,\n+\t\t\t\t\t const ControlList &controls)\n+{\n \tfor (const auto &algo : algorithms())\n-\t\talgo->queueRequest(context_, frame, frameContext, controls);\n+\t\talgo->queueRequest(context_, frameContext.frame(), frameContext, controls);\n }\n \n void IPASoftSimple::computeParams(const uint32_t frame)\n {\n \tcontext_.activeState.combinedMatrix = Matrix<float, 3, 3>::identity();\n \n-\tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);\n \tfor (const auto &algo : algorithms())\n \t\talgo->prepare(context_, frame, frameContext, params_);\n \tparams_->combinedMatrix = context_.activeState.combinedMatrix;\n@@ -299,7 +309,7 @@ void IPASoftSimple::processStats(const uint32_t frame,\n \t\t\t\t [[maybe_unused]] const uint32_t bufferId,\n \t\t\t\t const ControlList &sensorControls)\n {\n-\tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);\n \n \tframeContext.sensor.exposure =\n \t\tsensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n", "prefixes": [ "v2", "22/32" ] }