[{"id":38038,"web_url":"https://patchwork.libcamera.org/comment/38038/","msgid":"<85cy2rtbyy.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2026-01-30T14:24:37","subject":"Re: [PATCH] libcamera: layer_manager: Add support for global\n\tconfiguration","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Hi Paul,\n\nthe idea looks generally fine to me.  A few comments below, others may\nhave more comments.\n\nPaul Elder <paul.elder@ideasonboard.com> writes:\n\n> Enable configuring the layer system with the global configuration file,\n> while retaining the ability to overwrite the global configuration via\n> environment variables. This allows users to more easily retain layer\n> setups without having to always set environment variables.\n>\n> To achieve this, the CameraManager needs to manually construct the\n> LayerManager to feed it the global configuration.\n>\n> While at it, add documentation for the layer configuration options\n>\n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> ---\n> This patch is on top of v6 of \"Add Layers support\" [0]\n>\n> [0] https://patchwork.libcamera.org/project/libcamera/list/?series=5753\n>\n>  Documentation/runtime_configuration.rst     | 13 +++++\n>  include/libcamera/internal/camera_manager.h |  4 +-\n>  include/libcamera/internal/layer_manager.h  | 10 +++-\n>  src/libcamera/camera_manager.cpp            |  1 +\n>  src/libcamera/layer_manager.cpp             | 61 ++++++++++-----------\n>  5 files changed, 53 insertions(+), 36 deletions(-)\n>\n> diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst\n> index e99ef2fb9561..a74a738803d6 100644\n> --- a/Documentation/runtime_configuration.rst\n> +++ b/Documentation/runtime_configuration.rst\n> @@ -139,6 +139,19 @@ LIBCAMERA_<NAME>_TUNING_FILE\n>  \n>     Example value: ``/usr/local/share/libcamera/ipa/rpi/vc4/custom_sensor.json``\n>  \n> +LIBCAMERA_LAYER_PATH, layer.path\n> +   Define custom search locations for Layer implementations.\n> +\n> +   Example value: ``${HOME}/.libcamera/share/layer:/opt/libcamera/vendor/share/layer``\n> +\n> +LIBCAMERA_LAYERS_ENABLE, layer.layers\n> +  Define an ordered list of Layers to load. The layer names are declared in the\n> +  'name' field of their repsective LayerInfo structs. The layers declared first\n\ns/repsective/respective/\n\n> +  are closer to the application, and the layers declared later are closer to\n> +  libcamera.\n> +\n> +  Example value: ``inject_controls,sync``\n> +\n>  pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp\n>     Override whether software ISP is enabled for the given driver.\n>  \n> diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h\n> index cc3ede02507f..00afcd2177e7 100644\n> --- a/include/libcamera/internal/camera_manager.h\n> +++ b/include/libcamera/internal/camera_manager.h\n> @@ -46,7 +46,7 @@ public:\n>  \t}\n>  \n>  \tIPAManager *ipaManager() const { return ipaManager_.get(); }\n> -\tconst LayerManager *layerManager() const { return &layerManager_; }\n> +\tconst LayerManager *layerManager() const { return layerManager_.get(); }\n\nIt doesn't look like a real problem but still, it feels like an abuse of\nunique_ptr.  Should shared_ptr be used?\n\n>  protected:\n>  \tvoid run() override;\n> @@ -73,7 +73,7 @@ private:\n>  \tstd::unique_ptr<DeviceEnumerator> enumerator_;\n>  \n>  \tstd::unique_ptr<IPAManager> ipaManager_;\n> -\tLayerManager layerManager_;\n> +\tstd::unique_ptr<LayerManager> layerManager_;\n>  \n>  \tconst GlobalConfiguration configuration_;\n>  };\n> diff --git a/include/libcamera/internal/layer_manager.h b/include/libcamera/internal/layer_manager.h\n> index 8427ec3eb5d9..82a6a1d82461 100644\n> --- a/include/libcamera/internal/layer_manager.h\n> +++ b/include/libcamera/internal/layer_manager.h\n> @@ -26,6 +26,8 @@\n>  #include <libcamera/request.h>\n>  #include <libcamera/stream.h>\n>  \n> +#include \"libcamera/internal/global_configuration.h\"\n> +\n>  namespace libcamera {\n>  \n>  LOG_DECLARE_CATEGORY(LayerLoaded)\n> @@ -150,7 +152,9 @@ struct LayerInstance {\n>  class LayerController\n>  {\n>  public:\n> -\tLayerController(const Camera *camera, const ControlList &properties,\n> +\tLayerController(const Camera *camera,\n> +\t\t\tconst GlobalConfiguration &configuration,\n\nThere are plans to generally pass CameraManager instead.  This is a part\nof the huge Laurent's patch series, which may hang around for a while\nbefore it gets merged.  I'd suggest keeping eye on it and change the\nargument if/when needed.\n\n> +\t\t\tconst ControlList &properties,\n>  \t\t\tconst ControlInfoMap &controlInfoMap,\n>  \t\t\tconst std::map<std::string, std::shared_ptr<LayerLoaded>> &layers);\n>  \t~LayerController();\n> @@ -190,7 +194,7 @@ private:\n>  class LayerManager\n>  {\n>  public:\n> -\tLayerManager();\n> +\tLayerManager(const GlobalConfiguration &configuration);\n>  \t~LayerManager() = default;\n>  \n>  \tstd::unique_ptr<LayerController>\n> @@ -200,6 +204,8 @@ public:\n>  \n>  private:\n>  \tstd::map<std::string, std::shared_ptr<LayerLoaded>> layers_;\n> +\n> +\tconst GlobalConfiguration &configuration_;\n>  };\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp\n> index 554cd935339a..a8e7cfe9daff 100644\n> --- a/src/libcamera/camera_manager.cpp\n> +++ b/src/libcamera/camera_manager.cpp\n> @@ -43,6 +43,7 @@ CameraManager::Private::Private()\n>  \t: Thread(\"CameraManager\"), initialized_(false)\n>  {\n>  \tipaManager_ = std::make_unique<IPAManager>(this->configuration());\n> +\tlayerManager_ = std::make_unique<LayerManager>(this->configuration());\n>  }\n>  \n>  int CameraManager::Private::start()\n> diff --git a/src/libcamera/layer_manager.cpp b/src/libcamera/layer_manager.cpp\n> index bef69f6bd04b..445e26234b23 100644\n> --- a/src/libcamera/layer_manager.cpp\n> +++ b/src/libcamera/layer_manager.cpp\n> @@ -25,6 +25,7 @@\n>  #include <libcamera/control_ids.h>\n>  #include <libcamera/layer.h>\n>  \n> +#include \"libcamera/internal/global_configuration.h\"\n>  #include \"libcamera/internal/utils.h\"\n>  \n>  /**\n> @@ -276,6 +277,7 @@ LayerLoaded::LayerLoaded(const std::string &filename)\n>  /**\n>   * \\brief Initialize the Layers\n>   * \\param[in] camera The Camera for whom to initialize layers\n> + * \\param[in] configuration The global configuration\n>   * \\param[in] properties The Camera properties\n>   * \\param[in] controlInfoMap The Camera controls\n>   * \\param[in] layers Map of available layers\n> @@ -290,29 +292,26 @@ LayerLoaded::LayerLoaded(const std::string &filename)\n>   * efficiently returned at properties() and controls(), respectively.\n>   */\n>  LayerController::LayerController(const Camera *camera,\n> +\t\t\t\t const GlobalConfiguration &configuration,\n>  \t\t\t\t const ControlList &properties,\n>  \t\t\t\t const ControlInfoMap &controlInfoMap,\n>  \t\t\t\t const std::map<std::string, std::shared_ptr<LayerLoaded>> &layers)\n>  {\n>  \t/* Order the layers */\n> -\t/* \\todo Document this. First is closer to application, last is closer to libcamera */\n> -\t/* \\todo Get this from configuration file */\n> -\tconst char *layerList = utils::secure_getenv(\"LIBCAMERA_LAYERS_ENABLE\");\n> -\tif (layerList) {\n> -\t\tfor (const auto &layerName : utils::split(layerList, \",\")) {\n> -\t\t\tif (layerName.empty())\n> -\t\t\t\tcontinue;\n> -\n> -\t\t\tconst auto &it = layers.find(layerName);\n> -\t\t\tif (it == layers.end()) {\n> -\t\t\t\tLOG(LayerController, Warning)\n> -\t\t\t\t\t<< \"Requested layer '\" << layerName\n> -\t\t\t\t\t<< \"' not found\";\n> -\t\t\t\tcontinue;\n> -\t\t\t}\n> -\n> -\t\t\texecutionQueue_.emplace_back(std::make_unique<LayerInstance>(it->second));\n> +\tstd::vector<std::string> configLayers =\n> +\t\tconfiguration.envListOption(\"LIBCAMERA_LAYERS_ENABLE\", { \"layer\", \"layers\" }, \",\")\n> +\t\t\t     .value_or(std::vector<std::string>());\n> +\n> +\tfor (const std::string &layerName : configLayers) {\n> +\t\tconst auto &it = layers.find(layerName);\n> +\t\tif (it == layers.end()) {\n> +\t\t\tLOG(LayerController, Warning)\n> +\t\t\t\t<< \"Requested layer '\" << layerName\n> +\t\t\t\t<< \"' not found\";\n> +\t\t\tcontinue;\n>  \t\t}\n> +\n> +\t\texecutionQueue_.emplace_back(std::make_unique<LayerInstance>(it->second));\n>  \t}\n>  \n>  \tfor (std::unique_ptr<LayerInstance> &layer : executionQueue_)\n> @@ -532,7 +531,8 @@ void LayerController::stop()\n>   * LayerController is responsible for organizing them into queues to be\n>   * executed, and for managing closures for each Camera that they belong to.\n>   */\n> -LayerManager::LayerManager()\n> +LayerManager::LayerManager(const GlobalConfiguration &configuration)\n> +\t: configuration_(configuration)\n>  {\n>  \t/* This is so that we can capture it in the lambda below */\n>  \tstd::map<std::string, std::shared_ptr<LayerLoaded>> &layers = layers_;\n> @@ -560,19 +560,15 @@ LayerManager::LayerManager()\n>  \t};\n>  \n>  \t/* User-specified paths take precedence. */\n> -\t/* \\todo Document this */\n> -\tconst char *layerPaths = utils::secure_getenv(\"LIBCAMERA_LAYER_PATH\");\n> -\tif (layerPaths) {\n> -\t\tfor (const auto &dir : utils::split(layerPaths, \":\")) {\n> -\t\t\tif (dir.empty())\n> -\t\t\t\tcontinue;\n> -\n> -\t\t\t/*\n> -\t\t\t * \\todo Move the shared objects into one directory\n> -\t\t\t * instead of each in their own subdir\n> -\t\t\t */\n> -\t\t\tutils::findSharedObjects(dir.c_str(), 1, soHandler);\n> -\t\t}\n> +\tstd::vector<std::string> layerPaths =\n> +\t\tconfiguration.envListOption(\"LIBCAMERA_LAYER_PATH\", { \"layer\", \"path\" })\n> +\t\t\t     .value_or(std::vector<std::string>());\n> +\tfor (const auto &dir : layerPaths) {\n> +\t\t/*\n> +\t\t * \\todo Move the shared objects into one directory\n> +\t\t * instead of each in their own subdir\n> +\t\t */\n> +\t\tutils::findSharedObjects(dir.c_str(), 1, soHandler);\n>  \t}\n>  \n>  \t/*\n> @@ -606,7 +602,8 @@ LayerManager::createController(const Camera *camera,\n>  \t\t\t       const ControlList &properties,\n>  \t\t\t       const ControlInfoMap &controlInfoMap) const\n>  {\n> -\treturn std::make_unique<LayerController>(camera, properties, controlInfoMap, layers_);\n> +\treturn std::make_unique<LayerController>(camera, configuration_,\n> +\t\t\t\t\t\t properties, controlInfoMap, layers_);\n>  }\n>  \n>  } /* namespace libcamera */","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 14832BD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 30 Jan 2026 14:24:48 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4695E61FCC;\n\tFri, 30 Jan 2026 15:24:47 +0100 (CET)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A400361F84\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 30 Jan 2026 15:24:45 +0100 (CET)","from mail-lj1-f197.google.com (mail-lj1-f197.google.com\n\t[209.85.208.197]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-679-B0o5dBJ7PLOdU5SiXmxmlQ-1; Fri, 30 Jan 2026 09:24:41 -0500","by mail-lj1-f197.google.com with SMTP id\n\t38308e7fff4ca-385d4c511e4so12645161fa.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 30 Jan 2026 06:24:41 -0800 (PST)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\t38308e7fff4ca-38625f6c15esm14365931fa.27.2026.01.30.06.24.38\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 30 Jan 2026 06:24:38 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"II0uVcg5\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1769783084;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=autEJ7PSzM81H9QMPWGS2WvdrE9PL0skEYDwTH11iSo=;\n\tb=II0uVcg5Nq9Vs85WzQAZn2UvCPU+YruUNsZoz3e1v0mBy9pRSQ3/11yy3VTzE0dp5xNvKw\n\tWbEhFyOlfUNcOismF1s16b4SChWWVdpt6RdEV7ezeH3oWOtd38pdG2Lg3JCz7kcjxdKPx3\n\ta3t6ZPNiYeEehFkWOu+g2lDVT97HSQM=","X-MC-Unique":"B0o5dBJ7PLOdU5SiXmxmlQ-1","X-Mimecast-MFC-AGG-ID":"B0o5dBJ7PLOdU5SiXmxmlQ_1769783080","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1769783080; x=1770387880;\n\th=mime-version:user-agent:message-id:date:references:in-reply-to\n\t:subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject\n\t:date:message-id:reply-to;\n\tbh=autEJ7PSzM81H9QMPWGS2WvdrE9PL0skEYDwTH11iSo=;\n\tb=ORAh1aI4d7tXDEoKt+kb+uSiRNhrztjAjCTkKd7Z2UL2HieZnYGg0skMRAdpd/PN02\n\tr3MRVR2UblpyUKIu2/xanYp/sqSivRHX11PM6cAOafQmUTksi/E2QnsEdTNZfstfP/at\n\t8yTjH5jSwa8vF6IQ2FEHSAy/k7WrUlPbHTeJ9itQKfQouA5MGv+Bs04KXMqacO/u1SL9\n\tnHNFmGU6yr43EjjFMJrxeqo7P698R3bW4++2E+k/zv7chD4Vknu2YgPSvri/ItPS+I68\n\tV3Vqi/yR3g4rNgMOJtAMVZudxEXDhJUjtAKtTocin1XoMz5qH9XiwL0+cC86OdiCyZbU\n\tkrmA==","X-Gm-Message-State":"AOJu0YyrEeMcJ1D546kiglJTJxwuAFQE0vCSX7v++3UUlPNKDIx9GBFK\n\tHlA03KmdULGBIXbkfFzjVfD7Be+i2nqZ3exleoqomy9KEBIHFgsu1kz4yrnyJAov3daFYiOoibp\n\t+lrqaxpsoZtvOp9phPHOCKDnlomter2JsMAKOWTil4DcHOFPy76CJge9yzq8hVnThxUwD4Ts0Ik\n\tHS5BbYoJ5iKIIPInxNe8LgowjNc4ZLewLYLXfTureD7y+KPhiuyw84Y3NQqv8=","X-Gm-Gg":"AZuq6aLv5CWHTnUI9EaEeeQr84vr9vig0JImafZazMKY/5hdkqCmUb16pxATBpwAbxW\n\tJA5hw0QmKnxsmIZO7M3RI9Ydxw4+CNUufuO1cxoYtjalWBGnq8K0RUpgIYSPAsGspBQTkr/AY33\n\tZa74AdMDbYnicf0x/ePr7mitP5Owne14VKaUU3NwKP4JsxTToQ5Uhcd+9QcG35eB+rczvSNXei8\n\t1OkIZHgxu9nvkh6nnZpo1rjZgTYXnVXOcX+Hqj1aJxE8Pf1Wkc70CCT0y9vqMzzBmfBZz/Zkaha\n\ttn5eAgkyFaZuO7D3oc6K1MQsb8FLd4IAd3/D77y+aJ9zg/jGKbKyHOtI/9ijRyz8F9hCTIZJJqx\n\tT0dmokKnEJCuroWTLuZOVzFZJMi0hIntyOVTKOSHcI+655Nvdosj1sx5ocIb/94U=","X-Received":["by 2002:a2e:be25:0:b0:386:46ac:8357 with SMTP id\n\t38308e7fff4ca-38646ac83c5mr10544911fa.45.1769783079789; \n\tFri, 30 Jan 2026 06:24:39 -0800 (PST)","by 2002:a2e:be25:0:b0:386:46ac:8357 with SMTP id\n\t38308e7fff4ca-38646ac83c5mr10544731fa.45.1769783079132; \n\tFri, 30 Jan 2026 06:24:39 -0800 (PST)"],"From":"Milan Zamazal <mzamazal@redhat.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH] libcamera: layer_manager: Add support for global\n\tconfiguration","In-Reply-To":"<20260130054028.2043443-1-paul.elder@ideasonboard.com> (Paul\n\tElder's message of \"Fri, 30 Jan 2026 14:40:28 +0900\")","References":"<20260130054028.2043443-1-paul.elder@ideasonboard.com>","Date":"Fri, 30 Jan 2026 15:24:37 +0100","Message-ID":"<85cy2rtbyy.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"01-5khXuvcSosoGY3YZDMqMg5vffzQ0Dv2J94LXo_5A_1769783080","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain","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>"}}]