Patch Detail
Show a patch.
GET /api/patches/12131/?format=api
{ "id": 12131, "url": "https://patchwork.libcamera.org/api/patches/12131/?format=api", "web_url": "https://patchwork.libcamera.org/patch/12131/", "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": "<20210428093331.478728-1-paul.elder@ideasonboard.com>", "date": "2021-04-28T09:33:31", "name": "[libcamera-devel,v11] Documentation: Add IPA writers guide", "commit_ref": "0906ddb2681a53aff9ecccb7e933e9f04974022f", "pull_url": null, "state": "accepted", "archived": false, "hash": "a3a80ca00e12c8f129d78dc6286024abcac6a7b0", "submitter": { "id": 17, "url": "https://patchwork.libcamera.org/api/people/17/?format=api", "name": "Paul Elder", "email": "paul.elder@ideasonboard.com" }, "delegate": { "id": 17, "url": "https://patchwork.libcamera.org/api/users/17/?format=api", "username": "epaul", "first_name": "Paul", "last_name": "Elder", "email": "paul.elder@ideasonboard.com" }, "mbox": "https://patchwork.libcamera.org/patch/12131/mbox/", "series": [ { "id": 1984, "url": "https://patchwork.libcamera.org/api/series/1984/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1984", "date": "2021-04-28T09:33:31", "name": "[libcamera-devel,v11] Documentation: Add IPA writers guide", "version": 11, "mbox": "https://patchwork.libcamera.org/series/1984/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/12131/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/12131/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 54FF8BDE41\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 28 Apr 2021 09:33:56 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9EAEE688C5;\n\tWed, 28 Apr 2021 11:33:55 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BC58B688AC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 28 Apr 2021 11:33:53 +0200 (CEST)", "from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 237C090C;\n\tWed, 28 Apr 2021 11:33:50 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"SC4bvVDb\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1619602433;\n\tbh=MJbZnSYNcqhOOw98csKF2RLptj/sqg/spsEYY5xVEpg=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=SC4bvVDb7pzIMmaGMX6oT1AsYBd0r4aNItQbu07aawlodx+4unvy+Yyln/s0mXmy7\n\tiXxbvySrd2SMUuN8ffBikjpl5/7dHN6M3uDFojpYb+qX4+fUiURc1I7Nml35gnbyt1\n\tb5AQCmlc0vmyNqaN5VO30qi1dIcein4fxQZUoRJM=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Wed, 28 Apr 2021 18:33:31 +0900", "Message-Id": "<20210428093331.478728-1-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.27.0", "MIME-Version": "1.0", "Subject": "[libcamera-devel] [PATCH v11] Documentation: Add IPA writers guide", "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>", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Add a guide about writing IPAs.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n---\nChanges in v11:\n- expand introduction and concepts that should have been pre-explained\n\nChanges in v10:\n- fix typos and indentation and remove todos\n\nChanges in v9:\n- update documentation to include customizable init() and direct return\n of int32\n\nNo change in v8\n\nChanges in v8:\n- fix bullet points\n- update wording about struct field names\n- fix typos\n\nChanges in v7:\n- fix TODO syntax\n- update the generated struct fiels\n - no more postfix underscore\n\nChanges in v6:\n- namespacing is now required\n- start() is now customizable\n- {pipeline_name} is no longer required\n- fix code block indentations\n\nChanges in v5:\n- fix doxygen compile errors\n- update example struct names from raspberry pi changes\n- add todo for restricting pre-start() to sync and post-start() to async\n\nChanges in v4.1:\n- Add section on namespacing, custom data structures, compiling, and\n usage of the data structures and interface\n- Add examples to the main ipa interface and event ipa interface\n sections\n\nNew in v4\n---\n Documentation/guides/ipa.rst | 511 +++++++++++++++++++++++++++++++++++\n Documentation/index.rst | 1 +\n Documentation/meson.build | 1 +\n 3 files changed, 513 insertions(+)\n create mode 100644 Documentation/guides/ipa.rst", "diff": "diff --git a/Documentation/guides/ipa.rst b/Documentation/guides/ipa.rst\nnew file mode 100644\nindex 00000000..e53486d1\n--- /dev/null\n+++ b/Documentation/guides/ipa.rst\n@@ -0,0 +1,511 @@\n+.. SPDX-License-Identifier: CC-BY-SA-4.0\n+\n+IPA Writer's Guide\n+==================\n+\n+IPA modules are Image Processing Algorithm modules. They provide functionality\n+that the pipeline handler can use for image processing.\n+\n+This guide covers the definition of the IPA interface, and how to plumb the\n+connection between the pipeline handler and the IPA.\n+\n+The IPA interface and protocol\n+------------------------------\n+\n+The IPA interface defines the interface between the pipeline handler and the\n+IPA. Specifically, it defines the functions that the IPA exposes that the\n+pipeline handler can call, and the signals that the pipeline handler can\n+connect to, in order to receive data from the IPA asynchronously. In addition,\n+it contains any custom data structures that the pipeline handler and IPA may\n+pass to each other.\n+\n+The IPA protocol refers to the agreement between the pipeline handler and the\n+IPA regarding the expected response(s) from the IPA for given calls to the IPA.\n+This protocol doesn't need to be declared anywhere in code, but it shall be\n+documented, as there may be multiple IPA implementations for one pipeline\n+handler.\n+\n+As part of the design of libcamera, IPAs may be isolated in a separate process,\n+or run in the same process but a different thread from libcamera. The pipeline\n+handler and IPA shall not have to change their operation based on whether the\n+IPA is isolated or not, but the possibility of isolation needs to be kept in\n+mind. Therefore all data that is passed between them must be serializable, so\n+they must be defined separately in the `mojo Interface Definition Language`_\n+(IDL), and a code generator will generate headers and serializers corresponding\n+to the definitions. Every interface is defined in a mojom file and includes:\n+\n+- the functions that the pipeline handler can call from the IPA\n+- signals in the pipeline handler that the IPA can emit\n+- any data structures that are to be passed between the pipeline handler and the IPA\n+\n+All IPA modules of a given pipeline handler use the same IPA interface. The IPA\n+interface definition is thus written by the pipeline handler author, based on\n+how they design the interactions between the pipeline handler and the IPA.\n+\n+The entire IPA interface, including the functions, signals, and any custom\n+structs shall be defined in a file named {pipeline_name}.mojom under\n+include/libcamera/ipa/.\n+\n+.. _mojo Interface Definition Language: https://chromium.googlesource.com/chromium/src.git/+/master/mojo/public/tools/bindings/README.md\n+\n+Namespacing\n+-----------\n+\n+To avoid name collisions between data types defined by different IPA interfaces\n+and data types defined by libcamera, each IPA interface must be defined in its\n+own namespace.\n+\n+The namespace is specific with mojo's module directive. It must be the first\n+non-comment line in the mojo data definition file. For example, the Raspberry\n+Pi IPA interface uses:\n+\n+.. code-block:: none\n+\n+ module ipa.rpi;\n+\n+This will become the ipa::rpi namespace in C++ code.\n+\n+Data containers\n+---------------\n+\n+Since the data passed between the pipeline handler and the IPA must support\n+serialization, any custom data containers must be defined with the mojo IDL.\n+\n+The following list of libcamera objects are supported in the interface\n+definition, and may be used as function parameter types or struct field types:\n+\n+- libcamera.CameraSensorInfo\n+- libcamera.ControlInfoMap\n+- libcamera.ControlList\n+- libcamera.FileDescriptor\n+- libcamera.IPABuffer\n+- libcamera.IPASettings\n+- libcamera.IPAStream\n+- libcamera.Point\n+- libcamera.Rectangle\n+- libcamera.Size\n+- libcamera.SizeRange\n+\n+To use them, core.mojom must be included in the mojo data definition file:\n+\n+.. code-block:: none\n+\n+ import \"include/libcamera/ipa/core.mojom\";\n+\n+Other custom structs may be defined and used as well. There is no requirement\n+that they must be defined before usage. enums and structs are supported.\n+\n+The following is an example of a definition of an enum, for the purpose of\n+being used as flags:\n+\n+.. code-block:: none\n+\n+ enum ConfigParameters {\n+ ConfigLsTable = 0x01,\n+ ConfigStaggeredWrite = 0x02,\n+ ConfigSensor = 0x04,\n+ ConfigDropFrames = 0x08,\n+ };\n+\n+The following is an example of a definition of a struct:\n+\n+.. code-block:: none\n+\n+ struct ConfigInput {\n+ uint32 op;\n+ uint32 transform;\n+ libcamera.FileDescriptor lsTableHandle;\n+ int32 lsTableHandleStatic = -1;\n+ map<uint32, libcamera.IPAStream> streamConfig;\n+ array<libcamera.IPABuffer> buffers;\n+ };\n+\n+This example has some special things about it. First of all, it uses the\n+FileDescriptor data type. This type must be used to ensure that the file\n+descriptor that it contains is translated properly across the IPC boundary\n+(when the IPA is in an isolated process).\n+\n+This does mean that if the file descriptor should be sent without being\n+translated (for example, for the IPA to tell the pipeline handler which\n+fd *that the pipeline handler holds* to act on), then it must be in a\n+regular int32 type.\n+\n+This example also illustrates that struct fields may have default values, as\n+is assigned to lsTableHandleStatic. This is the value that the field will\n+take when the struct is constructed with the default constructor.\n+\n+Arrays and maps are supported as well. They are translated to C++ vectors and\n+maps, respectively. The members of the arrays and maps are embedded, and cannot\n+be const.\n+\n+Note that nullable fields, static-length arrays, handles, and unions, which\n+are supported by mojo, are not supported by our code generator.\n+\n+The Main IPA interface\n+----------------------\n+\n+The IPA interface is split in two parts, the Main IPA interface, which\n+describes the functions that the pipeline handler can call from the IPA,\n+and the Event IPA interface, which describes the signals received by the\n+pipeline handler that the IPA can emit. Both must be defined. This section\n+focuses on the Main IPA interface.\n+\n+The main interface must be named as IPA{pipeline_name}Interface.\n+\n+The functions that the pipeline handler can call from the IPA may be\n+synchronous or asynchronous. Synchronous functions do not return until the IPA\n+returns from the function, while asynchronous functions return immediately\n+without waiting for the IPA to return.\n+\n+At a minimum, the following three functions must be present (and implemented):\n+\n+- init();\n+- start();\n+- stop();\n+\n+All three of these functions are synchronous. The parameters for start() and\n+init() may be customized.\n+\n+A configure() method is recommended. Any ControlInfoMap instances that will be\n+used by the IPA must be sent to the IPA from the pipeline handler, at configure\n+time, for example.\n+\n+All input parameters will become const references, except for arithmetic types,\n+which will be passed by value. Output parameters will become pointers, unless\n+the first output parameter is an int32, or there is only one primitive output\n+parameter, in which case it will become a regular return value.\n+\n+const is not allowed inside of arrays and maps. mojo arrays will become C++\n+std::vector<>.\n+\n+By default, all methods defined in the main interface are synchronous. This\n+means that in the case of IPC (i.e. isolated IPA), the function call will not\n+return until the return value or output parameters are ready. To specify an\n+asynchronous function, the [async] attribute can be used. Asynchronous\n+methods must not have any return value or output parameters, since in the\n+case of IPC the call needs to return immediately.\n+\n+It is also possible that the IPA will not be run in isolation. In this case,\n+the IPA thread will not exist until start() is called. This means that in the\n+case of no isolation, asynchronous calls cannot be made before start(). Since\n+the IPA interface must be the same regardless of isolation, the same\n+restriction applies to the case of isolation, and any function that will be\n+called before start() must be synchronous.\n+\n+In addition, any call made after start() and before stop() must be\n+asynchronous. The motivation for this is to avoid damaging real-time\n+performance of the pipeline handler. If the pipeline handler wants some data\n+from the IPA, the IPA should return the data asynchronously via an event\n+(see \"The Event IPA interface\").\n+\n+The following is an example of a main interface definition:\n+\n+.. code-block:: none\n+\n+ interface IPARPiInterface {\n+ init(libcamera.IPASettings settings, string sensorName)\n+ => (int32 ret, bool metadataSupport);\n+ start() => (int32 ret);\n+ stop();\n+\n+ configure(libcamera.CameraSensorInfo sensorInfo,\n+ map<uint32, libcamera.IPAStream> streamConfig,\n+ map<uint32, libcamera.ControlInfoMap> entityControls,\n+ ConfigInput ipaConfig)\n+ => (int32 ret, ConfigOutput results);\n+\n+ mapBuffers(array<IPABuffer> buffers);\n+ unmapBuffers(array<uint32> ids);\n+\n+ [async] signalStatReady(uint32 bufferId);\n+ [async] signalQueueRequest(libcamera.ControlList controls);\n+ [async] signalIspPrepare(ISPConfig data);\n+ };\n+\n+\n+The first three functions are the required functions. Functions do not need to\n+have return values, like stop(), mapBuffers(), and unmapBuffers(). In the case\n+of asynchronous functions, as explained before, they *must not* have return\n+values.\n+\n+The Event IPA interface\n+-----------------------\n+\n+The event IPA interface describes the signals received by the pipeline handler\n+that the IPA can emit. It must be defined. If there are no event functions,\n+then it may be empty. These emissions are meant to notify the pipeline handler\n+of some event, such as request data is ready, and *must not* be used to drive\n+the camera pipeline from the IPA.\n+\n+The event interface must be named as IPA{pipeline_name}EventInterface.\n+\n+Methods defined in the event interface are implicitly asynchronous.\n+Thus they cannot return any value. Specifying the [async] tag is not\n+necessary.\n+\n+Methods defined in the event interface will become signals in the IPA\n+interface. The IPA can emit signals, while the pipeline handler can connect\n+slots to them.\n+\n+The following is an example of an event interface definition:\n+\n+.. code-block:: none\n+\n+ interface IPARPiEventInterface {\n+ statsMetadataComplete(uint32 bufferId,\n+ libcamera.ControlList controls);\n+ runIsp(uint32 bufferId);\n+ embeddedComplete(uint32 bufferId);\n+ setIsp(libcamera.ControlList controls);\n+ setStaggered(libcamera.ControlList controls);\n+ };\n+\n+Compiling the IPA interface\n+---------------------------\n+\n+After the IPA interface is defined in include/libcamera/ipa/{pipeline_name}.mojom,\n+an entry for it must be added in meson so that it can be compiled. The filename\n+must be added to the ipa_mojom_files object in include/libcamera/ipa/meson.build.\n+\n+For example, adding the raspberrypi.mojom file to meson:\n+\n+.. code-block:: none\n+\n+ ipa_mojom_files = [\n+ 'raspberrypi.mojom',\n+ ]\n+\n+This will cause the mojo data definition file to be compiled. Specifically, it\n+generates five files:\n+\n+- a header describing the custom data structures, and the complete IPA\n+ interface (at {$build_dir}/include/libcamera/ipa/{pipeline}_ipa_interface.h)\n+\n+- a serializer implementing de/serialization for the custom data structures (at\n+ {$build_dir}/include/libcamera/ipa/{pipeline}_ipa_serializer.h)\n+\n+- a proxy header describing a specialized IPA proxy (at\n+ {$build_dir}/include/libcamera/ipa/{pipeline}_ipa_proxy.h)\n+\n+- a proxy source implementing the IPA proxy (at\n+ {$build_dir}/src/libcamera/proxy/{pipeline}_ipa_proxy.cpp)\n+\n+- a proxy worker source implementing the other end of the IPA proxy (at\n+ {$build_dir}/src/libcamera/proxy/worker/{pipeline}_ipa_proxy_worker.cpp)\n+\n+The IPA proxy serves as the layer between the pipeline handler and the IPA, and\n+handles threading vs isolation transparently. The pipeline handler and the IPA\n+only require the interface header and the proxy header. The serializer is only\n+used internally by the proxy.\n+\n+Using the custom data structures\n+--------------------------------\n+\n+To use the custom data structures that are defined in the mojo data definition\n+file, the following header must be included:\n+\n+.. code-block:: C++\n+\n+ #include <libcamera/ipa/{pipeline_name}_ipa_interface.h>\n+\n+The POD types of the structs simply become their C++ counterparts, eg. uint32\n+in mojo will become uint32_t in C++. mojo map becomes C++ std::map, and mojo\n+array becomes C++ std::vector. All members of maps and vectors are embedded,\n+and are not pointers. The members cannot be const.\n+\n+The names of all the fields of structs can be used in C++ in exactly the same\n+way as they are defined in the data definition file. For example, the following\n+struct as defined in the mojo file:\n+\n+.. code-block:: none\n+\n+ struct SensorConfig {\n+ uint32 gainDelay = 1;\n+ uint32 exposureDelay;\n+ uint32 sensorMetadata;\n+ };\n+\n+Will become this in C++:\n+\n+.. code-block:: C++\n+\n+ struct SensorConfig {\n+ uint32_t gainDelay;\n+ uint32_t exposureDelay;\n+ uint32_t sensorMetadata;\n+ };\n+\n+The generated structs will also have two constructors, a constructor that\n+fills all fields with the default values, and a second constructor that takes\n+a value for every field. The default value constructor will fill in the fields\n+with the specified default value if it exists. In the above example, `gainDelay_`\n+will be initialized to 1. If no default value is specified, then it will be\n+filled in as zero (or -1 for a FileDescriptor type).\n+\n+All fields and constructors/destructors in these generated structs are public.\n+\n+Using the IPA interface (pipeline handler)\n+------------------------------------------\n+\n+The following headers are necessary to use an IPA in the pipeline handler\n+(with raspberrypi as an example):\n+\n+.. code-block:: C++\n+\n+ #include <libcamera/ipa/raspberrypi_ipa_interface.h>\n+ #include <libcamera/ipa/raspberrypi_ipa_proxy.h>\n+\n+The first header includes definitions of the custom data structures, and\n+the definition of the complete IPA interface (including both the Main and\n+the Event IPA interfaces). The name of the header file comes from the name\n+of the mojom file, which in this case was raspberrypi.mojom.\n+\n+The second header includes the definition of the specialized IPA proxy. It\n+exposes the complete IPA interface. We will see how to use it in this section.\n+\n+In the pipeline handler, we first need to construct a specialized IPA proxy.\n+From the point of view of the pipeline hander, this is the object that is the\n+IPA.\n+\n+To do so, we invoke the IPAManager:\n+\n+.. code-block:: C++\n+\n+ std::unique_ptr<ipa::rpi::IPAProxyRPi> ipa_ =\n+ IPAManager::createIPA<ipa::rpi::IPAProxyRPi>(pipe_, 1, 1);\n+\n+The ipa::rpi namespace comes from the namespace that we defined in the mojo\n+data definition file, in the \"Namespacing\" section. The name of the proxy,\n+IPAProxyRPi, comes from the name given to the main IPA interface,\n+IPARPiInterface, in the \"The Main IPA interface\" section.\n+\n+The return value of IPAManager::createIPA shall be error-checked, to confirm\n+that the returned pointer is not a nullptr.\n+\n+After this, before initializing the IPA, slots should be connected to all of\n+the IPA's signals, as defined in the Event IPA interface:\n+\n+.. code-block:: C++\n+\n+\tipa_->statsMetadataComplete.connect(this, &RPiCameraData::statsMetadataComplete);\n+\tipa_->runIsp.connect(this, &RPiCameraData::runIsp);\n+\tipa_->embeddedComplete.connect(this, &RPiCameraData::embeddedComplete);\n+\tipa_->setIsp.connect(this, &RPiCameraData::setIsp);\n+\tipa_->setStaggered.connect(this, &RPiCameraData::setStaggered);\n+\n+The slot functions have a function signature based on the function definition\n+in the Event IPA interface. All plain old data (POD) types are as-is (with\n+their C++ versions, eg. uint32 -> uint32_t), and all structs are const references.\n+\n+For example, for the following entry in the Event IPA interface:\n+\n+.. code-block:: none\n+\n+ statsMetadataComplete(uint32 bufferId, ControlList controls);\n+\n+A function with the following function signature shall be connected to the\n+signal:\n+\n+.. code-block:: C++\n+\n+ void statsMetadataComplete(uint32_t bufferId, const ControlList &controls);\n+\n+After connecting the slots to the signals, the IPA should be initialized\n+(using the main interface definition example from earlier):\n+\n+.. code-block:: C++\n+\n+ IPASettings settings{};\n+ bool metadataSupport;\n+ int ret = ipa_->init(settings, \"sensor name\", &metadataSupport);\n+\n+At this point, any IPA functions that were defined in the Main IPA interface\n+can be called as if they were regular member functions, for example (based on\n+the main interface definition example from earlier):\n+\n+.. code-block:: C++\n+\n+ ipa_->start();\n+ int ret = ipa_->configure(sensorInfo_, streamConfig, entityControls, ipaConfig, &result);\n+ ipa_->signalStatReady(RPi::BufferMask::STATS | static_cast<unsigned int>(index));\n+\n+Remember that any functions designated as asynchronous *must not* be called\n+before start().\n+\n+Notice that for both init() and configure(), the first output parameter is a\n+direct return, since it is an int32, while the other output parameter is a\n+pointer-based output parameter.\n+\n+Using the IPA interface (IPA Module)\n+-----------------------------\n+\n+The following header is necessary to implement an IPA Module (with raspberrypi\n+as an example):\n+\n+.. code-block:: C++\n+\n+ #include <libcamera/ipa/raspberrypi_ipa_interface.h>\n+\n+This header includes definitions of the custom data structures, and\n+the definition of the complete IPA interface (including both the Main and\n+the Event IPA interfaces). The name of the header file comes from the name\n+of the mojom file, which in this case was raspberrypi.mojom.\n+\n+The IPA module must implement the IPA interface class that is defined in the\n+header. In the case of our example, that is ipa::rpi::IPARPiInterface. The\n+ipa::rpi namespace comes from the namespace that we defined in the mojo data\n+definition file, in the \"Namespacing\" section. The name of the interface is the\n+same as the name given to the Main IPA interface.\n+\n+The function signature rules are the same as for the slots in the pipeline\n+handler side; PODs are passed by value, and structs are passed by const\n+reference. For the Main IPA interface, output values are also allowed (only\n+for synchronous calls), so there may be output parameters as well. If the\n+first output parameter is a POD it will be returned by value, otherwise\n+it will be returned by an output parameter pointer. The second and any other\n+output parameters will also be returned by output parameter pointers.\n+\n+For example, for the following function specification in the Main IPA interface\n+definition:\n+\n+.. code-block:: none\n+\n+ configure(libcamera.CameraSensorInfo sensorInfo,\n+ uint32 exampleNumber,\n+ map<uint32, libcamera.IPAStream> streamConfig,\n+ map<uint32, libcamera.ControlInfoMap> entityControls,\n+ ConfigInput ipaConfig)\n+ => (int32 ret, ConfigOutput results);\n+\n+We will need to implement a function with the following function signature:\n+\n+.. code-block:: C++\n+\n+ int configure(const CameraSensorInfo &sensorInfo,\n+ uint32_t exampleNumber,\n+ const std::map<unsigned int, IPAStream> &streamConfig,\n+ const std::map<unsigned int, ControlInfoMap> &entityControls,\n+ const ipa::rpi::ConfigInput &data,\n+ ipa::rpi::ConfigOutput *response);\n+\n+The return value is int, because the first output parameter is int32. The rest\n+of the output parameters (in this case, only response) become output parameter\n+pointers. The non-POD input parameters become const references, and the POD\n+input parameter is passed by value.\n+\n+At any time after start() and before stop() (though usually only in response to\n+an IPA call), the IPA may send data to the pipeline handler by emitting\n+signals. These signals are defined in the C++ IPA interface class (which is in\n+the generated and included header).\n+\n+For example, for the following function defined in the Event IPA interface:\n+\n+.. code-block:: none\n+\n+ statsMetadataComplete(uint32 bufferId, libcamera.ControlList controls);\n+\n+We can emit a signal like so:\n+\n+.. code-block:: C++\n+\n+ statsMetadataComplete.emit(bufferId & RPi::BufferMask::ID, libcameraMetadata_);\ndiff --git a/Documentation/index.rst b/Documentation/index.rst\nindex 7e3fe2f6..1f4fc485 100644\n--- a/Documentation/index.rst\n+++ b/Documentation/index.rst\n@@ -17,6 +17,7 @@\n Developer Guide <guides/introduction>\n Application Writer's Guide <guides/application-developer>\n Pipeline Handler Writer's Guide <guides/pipeline-handler>\n+ IPA Writer's guide <guides/ipa>\n Tracing guide <guides/tracing>\n Environment variables <environment_variables>\n Sensor driver requirements <sensor_driver_requirements>\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 9950465d..8cf68a07 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -54,6 +54,7 @@ if sphinx.found()\n 'environment_variables.rst',\n 'guides/application-developer.rst',\n 'guides/introduction.rst',\n+ 'guides/ipa.rst',\n 'guides/pipeline-handler.rst',\n 'guides/tracing.rst',\n 'index.rst',\n", "prefixes": [ "libcamera-devel", "v11" ] }