Patch Detail
Show a patch.
GET /api/1.1/patches/2422/?format=api
{ "id": 2422, "url": "https://patchwork.libcamera.org/api/1.1/patches/2422/?format=api", "web_url": "https://patchwork.libcamera.org/patch/2422/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/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": "<20191211145327.58633-1-jacopo@jmondi.org>", "date": "2019-12-11T14:53:26", "name": "[libcamera-devel,RFC,1/2] WIP libcamera: controls: Support compound controls", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "5c8503728e39e89777dc567a63a5aaae157cebbf", "submitter": { "id": 3, "url": "https://patchwork.libcamera.org/api/1.1/people/3/?format=api", "name": "Jacopo Mondi", "email": "jacopo@jmondi.org" }, "delegate": { "id": 15, "url": "https://patchwork.libcamera.org/api/1.1/users/15/?format=api", "username": "jmondi", "first_name": "Jacopo", "last_name": "Mondi", "email": "jacopo@jmondi.org" }, "mbox": "https://patchwork.libcamera.org/patch/2422/mbox/", "series": [ { "id": 587, "url": "https://patchwork.libcamera.org/api/1.1/series/587/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=587", "date": "2019-12-11T14:53:26", "name": "[libcamera-devel,RFC,1/2] WIP libcamera: controls: Support compound controls", "version": 1, "mbox": "https://patchwork.libcamera.org/series/587/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/2422/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/2422/checks/", "tags": {}, "headers": { "Return-Path": "<jacopo@jmondi.org>", "Received": [ "from relay9-d.mail.gandi.net (relay9-d.mail.gandi.net\n\t[217.70.183.199])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 591D0600F5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 11 Dec 2019 15:51:19 +0100 (CET)", "from uno.lan (93-34-114-233.ip49.fastwebnet.it [93.34.114.233])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay9-d.mail.gandi.net (Postfix) with ESMTPSA id C1727FF809;\n\tWed, 11 Dec 2019 14:51:18 +0000 (UTC)" ], "X-Originating-IP": "93.34.114.233", "From": "Jacopo Mondi <jacopo@jmondi.org>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Wed, 11 Dec 2019 15:53:26 +0100", "Message-Id": "<20191211145327.58633-1-jacopo@jmondi.org>", "X-Mailer": "git-send-email 2.24.0", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [RFC 1/2] WIP libcamera: controls: Support\n\tcompound controls", "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>", "X-List-Received-Date": "Wed, 11 Dec 2019 14:51:19 -0000" }, "content": "A compound control transports an array of data values. Add\nsupport to compound controls to the ControlValue class.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\n---\n include/libcamera/controls.h | 50 +++-\n include/libcamera/span.h | 111 +++++++++\n src/libcamera/control_ids.in | 0\n src/libcamera/control_ids.yaml | 4 +\n src/libcamera/control_serializer.cpp | 15 +-\n src/libcamera/controls.cpp | 344 +++++++++++++++++++++++++--\n src/libcamera/pipeline/vimc.cpp | 7 +\n test/controls/compound_controls.cpp | 94 ++++++++\n test/controls/meson.build | 1 +\n 9 files changed, 596 insertions(+), 30 deletions(-)\n create mode 100644 include/libcamera/span.h\n create mode 100644 src/libcamera/control_ids.in\n create mode 100644 test/controls/compound_controls.cpp", "diff": "diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h\nindex 458b84e8fa8c..b9f8d4db0bea 100644\n--- a/include/libcamera/controls.h\n+++ b/include/libcamera/controls.h\n@@ -11,6 +11,8 @@\n #include <string>\n #include <unordered_map>\n \n+#include <libcamera/span.h>\n+\n namespace libcamera {\n \n class ControlValidator;\n@@ -18,8 +20,15 @@ class ControlValidator;\n enum ControlType {\n \tControlTypeNone,\n \tControlTypeBool,\n+\tControlTypeInteger8,\n \tControlTypeInteger32,\n \tControlTypeInteger64,\n+\tControlTypeFloat,\n+\tControlTypeCompoundBool,\n+\tControlTypeCompoundInt8,\n+\tControlTypeCompoundInt32,\n+\tControlTypeCompoundInt64,\n+\tControlTypeCompoundFloat,\n };\n \n class ControlValue\n@@ -27,16 +36,28 @@ class ControlValue\n public:\n \tControlValue();\n \tControlValue(bool value);\n+\tControlValue(int8_t value);\n \tControlValue(int32_t value);\n \tControlValue(int64_t value);\n+\tControlValue(float value);\n+\tControlValue(Span<bool> &values);\n+\tControlValue(Span<int8_t> &values);\n+\tControlValue(Span<int32_t> &values);\n+\tControlValue(Span<int64_t> &values);\n+\tControlValue(Span<float> &values);\n+\t~ControlValue();\n \n \tControlType type() const { return type_; }\n \tbool isNone() const { return type_ == ControlTypeNone; }\n+\tstd::size_t numElements() const { return numElements_; }\n \n \ttemplate<typename T>\n-\tconst T &get() const;\n+\tconst Span<T> get() const;\n+\n \ttemplate<typename T>\n \tvoid set(const T &value);\n+\ttemplate<typename T>\n+\tvoid set(const Span<T> &values);\n \n \tstd::string toString() const;\n \n@@ -51,9 +72,23 @@ private:\n \n \tunion {\n \t\tbool bool_;\n+\t\tint8_t integer8_;\n \t\tint32_t integer32_;\n \t\tint64_t integer64_;\n+\t\tfloat float_;\n \t};\n+\n+\tbool *pbool_;\n+\tint8_t *p8_;\n+\tint32_t *p32_;\n+\tint64_t *p64_;\n+\tfloat *pfloat_;\n+\n+\tstd::size_t numElements_;\n+\n+\tvoid release();\n+\tbool compareElement(const ControlValue &other, unsigned int i) const;\n+\tstd::string elemToString(unsigned int i) const;\n };\n \n class ControlId\n@@ -182,6 +217,7 @@ public:\n \n private:\n \tvoid generateIdmap();\n+\tbool matchRangeType(enum ControlType type1, enum ControlType type2);\n \n \tControlIdMap idmap_;\n };\n@@ -212,7 +248,7 @@ public:\n \tbool contains(unsigned int id) const;\n \n \ttemplate<typename T>\n-\tconst T &get(const Control<T> &ctrl) const\n+\tconst Span<T> get(const Control<T> &ctrl) const\n \t{\n \t\tconst ControlValue *val = find(ctrl.id());\n \t\tif (!val) {\n@@ -233,6 +269,16 @@ public:\n \t\tval->set<T>(value);\n \t}\n \n+\ttemplate<typename T>\n+\tvoid set(const Control<T> &ctrl, const Span<T> &values)\n+\t{\n+\t\tControlValue *val = find(ctrl.id());\n+\t\tif (!val)\n+\t\t\treturn;\n+\n+\t\tval->set<T>(values);\n+\t}\n+\n \tconst ControlValue &get(unsigned int id) const;\n \tvoid set(unsigned int id, const ControlValue &value);\n \ndiff --git a/include/libcamera/span.h b/include/libcamera/span.h\nnew file mode 100644\nindex 000000000000..8ff0ebc7c6d2\n--- /dev/null\n+++ b/include/libcamera/span.h\n@@ -0,0 +1,111 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * span.h - C++20 std::span<> implementation for C++11\n+ */\n+\n+#ifndef __LIBCAMERA_SPAN_H__\n+#define __LIBCAMERA_SPAN_H__\n+\n+#include <iostream>\n+\n+#include <array>\n+#include <cstddef>\n+#include <cstdint>\n+\n+namespace libcamera {\n+\n+template <typename T>\n+class Span\n+{\n+private:\n+\tusing iterator = T *;\n+\tusing const_iterator = const T *;\n+\tusing reverse_iterator = std::reverse_iterator<iterator>;\n+\tusing const_reverse_iterator = std::reverse_iterator<const_iterator>;\n+\n+\tclass Storage\n+\t{\n+\tpublic:\n+\t\tStorage(T *ptr, std::size_t size)\n+\t\t\t: ptr_(ptr), size_(size)\n+\t\t{\n+\t\t}\n+\n+\t\tT *ptr() const { return ptr_; }\n+\t\tstd::size_t size() const { return size_; }\n+\n+\tprivate:\n+\t\tT *ptr_;\n+\t\tstd::size_t size_;\n+\t};\n+\n+public:\n+\tSpan(T &v)\n+\t\t: storage_(&v, 1)\n+\t{\n+\t}\n+\n+\tSpan(const T &v)\n+\t\t: storage_(const_cast<T *>(&v), 1)\n+\t{\n+\t}\n+\n+\tSpan(T *v, std::size_t s)\n+\t\t: storage_(v, s)\n+\t{\n+\t}\n+\n+\tSpan(const T *v, std::size_t s)\n+\t\t: storage_(const_cast<T *>(v), s)\n+\t{\n+\t}\n+\n+\tSpan(std::initializer_list<T> list)\n+\t\t: storage_(const_cast<T *>(list.begin()), list.size())\n+\t{\n+\t}\n+\n+\tSpan(const Span &other) = default;\n+\n+\tSpan &operator=(const Span &other) = default;\n+\toperator T() const { return *data(); }\n+\tT &operator[](unsigned int index) const\n+\t{\n+\t\tif (index >= size())\n+\t\t\treturn *(end() - 1);\n+\t\treturn *(data() + index);\n+\t}\n+\n+\tT *data() const { return storage_.ptr(); }\n+\tstd::size_t size() const { return storage_.size(); }\n+\n+\tconstexpr iterator begin() const { return data(); }\n+\tconstexpr iterator end() const { return data() + size(); }\n+\tconstexpr iterator cbegin() const { return begin(); }\n+\tconstexpr iterator cend() const { return end(); }\n+\tconstexpr reverse_iterator rbegin() const\n+\t{\n+\t\treturn reverse_iterator(end());\n+\t}\n+\tconstexpr reverse_iterator rend() const\n+\t{\n+\t\treturn reverse_iterator(begin());\n+\t}\n+\tconstexpr const_reverse_iterator crbegin() const\n+\t{\n+\t\treturn const_reverse_iterator(end());\n+\t}\n+\tconstexpr const_reverse_iterator crend() const\n+\t{\n+\t\treturn const_reverse_iterator(begin());\n+\t}\n+\n+private:\n+\tStorage storage_;\n+};\n+\n+}; /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_SPAN_H__ */\ndiff --git a/src/libcamera/control_ids.in b/src/libcamera/control_ids.in\nnew file mode 100644\nindex 000000000000..e69de29bb2d1\ndiff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml\nindex 4befec746a59..b72e81dae060 100644\n--- a/src/libcamera/control_ids.yaml\n+++ b/src/libcamera/control_ids.yaml\n@@ -50,4 +50,8 @@ controls:\n type: int32_t\n description: Specify a fixed gain parameter\n \n+ - CompoundControl:\n+ type: int8_t\n+ description: A fictional compound control\n+\n ...\ndiff --git a/src/libcamera/control_serializer.cpp b/src/libcamera/control_serializer.cpp\nindex b787655e6769..2140d3a5dd3f 100644\n--- a/src/libcamera/control_serializer.cpp\n+++ b/src/libcamera/control_serializer.cpp\n@@ -30,10 +30,17 @@ LOG_DEFINE_CATEGORY(Serializer)\n namespace {\n \n static constexpr size_t ControlValueSize[] = {\n-\t[ControlTypeNone]\t= 1,\n-\t[ControlTypeBool]\t= sizeof(bool),\n-\t[ControlTypeInteger32]\t= sizeof(int32_t),\n-\t[ControlTypeInteger64]\t= sizeof(int64_t),\n+\t[ControlTypeNone]\t\t= 1,\n+\t[ControlTypeBool]\t\t= sizeof(bool),\n+\t[ControlTypeInteger8]\t\t= sizeof(int8_t),\n+\t[ControlTypeInteger32]\t\t= sizeof(int32_t),\n+\t[ControlTypeInteger64]\t\t= sizeof(int64_t),\n+\t[ControlTypeFloat]\t\t= sizeof(float),\n+\t[ControlTypeCompoundBool]\t= sizeof(bool *),\n+\t[ControlTypeCompoundInt8]\t= sizeof(int8_t *),\n+\t[ControlTypeCompoundInt32]\t= sizeof(int32_t *),\n+\t[ControlTypeCompoundInt64]\t= sizeof(int64_t *),\n+\t[ControlTypeCompoundFloat]\t= sizeof(float *),\n };\n \n } /* namespace */\ndiff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp\nindex 1678d3cba4f3..64d99f7669b6 100644\n--- a/src/libcamera/controls.cpp\n+++ b/src/libcamera/controls.cpp\n@@ -9,7 +9,9 @@\n \n #include <iomanip>\n #include <sstream>\n-#include <string>\n+#include <string.h>\n+\n+#include <libcamera/span.h>\n \n #include \"control_validator.h\"\n #include \"log.h\"\n@@ -69,7 +71,8 @@ LOG_DEFINE_CATEGORY(Controls)\n * \\brief Construct an empty ControlValue.\n */\n ControlValue::ControlValue()\n-\t: type_(ControlTypeNone)\n+\t: type_(ControlTypeNone), pbool_(nullptr), p8_(nullptr), p32_(nullptr),\n+\t p64_(nullptr), pfloat_(nullptr), numElements_(0)\n {\n }\n \n@@ -78,7 +81,15 @@ ControlValue::ControlValue()\n * \\param[in] value Boolean value to store\n */\n ControlValue::ControlValue(bool value)\n-\t: type_(ControlTypeBool), bool_(value)\n+\t: type_(ControlTypeBool), bool_(value), pbool_(nullptr), p8_(nullptr),\n+\t p32_(nullptr), p64_(nullptr), pfloat_(nullptr), numElements_(1)\n+{\n+}\n+\n+ControlValue::ControlValue(int8_t value)\n+\t: type_(ControlTypeInteger8), integer8_(value), pbool_(nullptr),\n+\t p8_(nullptr), p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(1)\n {\n }\n \n@@ -87,7 +98,9 @@ ControlValue::ControlValue(bool value)\n * \\param[in] value Integer value to store\n */\n ControlValue::ControlValue(int32_t value)\n-\t: type_(ControlTypeInteger32), integer32_(value)\n+\t: type_(ControlTypeInteger32), integer32_(value), pbool_(nullptr),\n+\t p8_(nullptr), p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(1)\n {\n }\n \n@@ -96,8 +109,87 @@ ControlValue::ControlValue(int32_t value)\n * \\param[in] value Integer value to store\n */\n ControlValue::ControlValue(int64_t value)\n-\t: type_(ControlTypeInteger64), integer64_(value)\n+\t: type_(ControlTypeInteger64), integer64_(value), pbool_(nullptr),\n+\t p8_(nullptr), p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(1)\n+{\n+}\n+\n+ControlValue::ControlValue(float value)\n+\t: type_(ControlTypeInteger64), float_(value), pbool_(nullptr),\n+\t p8_(nullptr), p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(1)\n+{\n+}\n+\n+ControlValue::ControlValue(Span<bool> &values)\n+\t: type_(ControlTypeCompoundBool), pbool_(nullptr), p8_(nullptr),\n+\t p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(values.size())\n+{\n+\tpbool_ = new bool[numElements_];\n+\tmemcpy(pbool_, values.data(), sizeof(bool) * numElements_);\n+}\n+\n+ControlValue::ControlValue(Span<int8_t> &values)\n+\t: type_(ControlTypeCompoundInt8),pbool_(nullptr), p8_(nullptr),\n+\t p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(values.size())\n+{\n+\tp8_ = new int8_t[numElements_];\n+\tmemcpy(p8_, values.data(), sizeof(int8_t) * numElements_);\n+}\n+\n+ControlValue::ControlValue(Span<int32_t> &values)\n+\t: type_(ControlTypeCompoundInt32), pbool_(nullptr), p8_(nullptr),\n+\t p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(values.size())\n+{\n+\tp32_ = new int32_t[numElements_];\n+\tmemcpy(p32_, values.data(), sizeof(int32_t) * numElements_);\n+}\n+\n+ControlValue::ControlValue(Span<int64_t> &values)\n+\t: type_(ControlTypeCompoundInt64), pbool_(nullptr), p8_(nullptr),\n+\t p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(values.size())\n {\n+\tp64_ = new int64_t[numElements_];\n+\tmemcpy(p64_, values.data(), sizeof(int64_t) * numElements_);\n+}\n+\n+ControlValue::ControlValue(Span<float> &values)\n+\t: type_(ControlTypeCompoundFloat), pbool_(nullptr), p8_(nullptr),\n+\t p32_(nullptr), p64_(nullptr), pfloat_(nullptr),\n+\t numElements_(values.size())\n+{\n+\tpfloat_ = new float[numElements_];\n+\tmemcpy(pfloat_, values.data(), sizeof(float) * numElements_);\n+}\n+\n+void ControlValue::release()\n+{\n+\tif (pbool_)\n+\t\tdelete[] pbool_;\n+\tif (p8_)\n+\t\tdelete[] p8_;\n+\tif (p32_)\n+\t\tdelete[] p32_;\n+\tif (p64_)\n+\t\tdelete[] p64_;\n+\tif (pfloat_)\n+\t\tdelete[] pfloat_;\n+\n+\tpbool_ = nullptr;\n+\tp8_ = nullptr;\n+\tp32_ = nullptr;\n+\tp64_ = nullptr;\n+\tpfloat_ = nullptr;\n+}\n+\n+ControlValue::~ControlValue()\n+{\n+\trelease();\n }\n \n /**\n@@ -130,69 +222,225 @@ ControlValue::ControlValue(int64_t value)\n \n #ifndef __DOXYGEN__\n template<>\n-const bool &ControlValue::get<bool>() const\n+const Span<bool> ControlValue::get<bool>() const\n {\n-\tASSERT(type_ == ControlTypeBool);\n+\tASSERT(type_ == ControlTypeBool || type_ == ControlTypeCompoundBool);\n \n-\treturn bool_;\n+\tconst bool *p = pbool_ ? pbool_ : &bool_;\n+\t/*\n+\t * Explicitly create a Span<bool> instance, otherwise the compiler\n+\t * tries to match the \"{ p, numElements_}\" construct used in other get()\n+\t * operation specialization with the initializer_list constructor of\n+\t * class Span (clang++ 8.0.1)\n+\t */\n+\treturn Span<bool>(p, static_cast<std::size_t>(numElements_));\n }\n \n template<>\n-const int32_t &ControlValue::get<int32_t>() const\n+const Span<int8_t> ControlValue::get<int8_t>() const\n {\n-\tASSERT(type_ == ControlTypeInteger32 || type_ == ControlTypeInteger64);\n+\tASSERT(type_ == ControlTypeInteger8 ||\n+\t type_ == ControlTypeCompoundInt8);\n \n-\treturn integer32_;\n+\tconst int8_t *p = p8_ ? p8_ : &integer8_;\n+\treturn { p, static_cast<std::size_t>(numElements_) };\n }\n \n template<>\n-const int64_t &ControlValue::get<int64_t>() const\n+const Span<int32_t> ControlValue::get<int32_t>() const\n {\n-\tASSERT(type_ == ControlTypeInteger32 || type_ == ControlTypeInteger64);\n+\tASSERT(type_ == ControlTypeInteger32 ||\n+\t type_ == ControlTypeInteger64 ||\n+\t type_ == ControlTypeCompoundInt32 ||\n+\t type_ == ControlTypeCompoundInt64);\n \n-\treturn integer64_;\n+\tconst int32_t *p = p32_ ? p32_ : &integer32_;\n+\treturn { p, numElements_ };\n+}\n+\n+template<>\n+const Span<int64_t> ControlValue::get<int64_t>() const\n+{\n+\tASSERT(type_ == ControlTypeInteger32 ||\n+\t type_ == ControlTypeInteger64 ||\n+\t type_ == ControlTypeCompoundInt32 ||\n+\t type_ == ControlTypeCompoundInt64);\n+\n+\tconst int64_t *p = p64_ ? p64_ : &integer64_;\n+\treturn { p, numElements_ };\n+}\n+\n+template<>\n+const Span<float> ControlValue::get<float>() const\n+{\n+\tASSERT(type_ == ControlTypeFloat || type_ == ControlTypeCompoundFloat);\n+\n+\tconst float *p = pfloat_ ? pfloat_ : &float_;\n+\treturn { p, numElements_ };\n }\n \n template<>\n void ControlValue::set<bool>(const bool &value)\n {\n+\trelease();\n+\n \ttype_ = ControlTypeBool;\n \tbool_ = value;\n+\tnumElements_ = 1;\n }\n \n template<>\n void ControlValue::set<int32_t>(const int32_t &value)\n {\n+\trelease();\n+\n \ttype_ = ControlTypeInteger32;\n \tinteger32_ = value;\n+\tnumElements_ = 1;\n }\n \n template<>\n void ControlValue::set<int64_t>(const int64_t &value)\n {\n+\trelease();\n+\n \ttype_ = ControlTypeInteger64;\n \tinteger64_ = value;\n+\tnumElements_ = 1;\n }\n+\n+template<>\n+void ControlValue::set<bool>(const Span<bool> &values)\n+{\n+\trelease();\n+\n+\ttype_ = ControlTypeCompoundBool;\n+\tnumElements_ = values.size();\n+\n+\tpbool_ = new bool[numElements_];\n+\tmemcpy(pbool_, values.data(), sizeof(bool) * numElements_);\n+}\n+\n+template<>\n+void ControlValue::set<int8_t>(const Span<int8_t> &values)\n+{\n+\trelease();\n+\n+\ttype_ = ControlTypeCompoundInt8;\n+\tnumElements_ = values.size();\n+\n+\tp8_ = new int8_t[numElements_];\n+\tmemcpy(p8_, values.data(), sizeof(int8_t) * numElements_);\n+}\n+\n+template<>\n+void ControlValue::set<int32_t>(const Span<int32_t> &values)\n+{\n+\trelease();\n+\n+\ttype_ = ControlTypeCompoundInt32;\n+\tnumElements_ = values.size();\n+\n+\tp32_ = new int32_t[numElements_];\n+\tmemcpy(p32_, values.data(), sizeof(int32_t) * numElements_);\n+}\n+\n+template<>\n+void ControlValue::set<int64_t>(const Span<int64_t> &values)\n+{\n+\trelease();\n+\n+\ttype_ = ControlTypeCompoundInt64;\n+\tnumElements_ = values.size();\n+\n+\tp64_ = new int64_t[numElements_];\n+\tmemcpy(p64_, values.data(), sizeof(int64_t) * numElements_);\n+}\n+\n+template<>\n+void ControlValue::set<float>(const Span<float> &values)\n+{\n+\trelease();\n+\n+\ttype_ = ControlTypeCompoundFloat;\n+\tnumElements_ = values.size();\n+\n+\tpfloat_ = new float[numElements_];\n+\tmemcpy(pfloat_, values.data(), sizeof(float) * numElements_);\n+}\n+\n #endif /* __DOXYGEN__ */\n \n /**\n * \\brief Assemble and return a string describing the value\n * \\return A string describing the ControlValue\n */\n-std::string ControlValue::toString() const\n+std::string ControlValue::elemToString(unsigned int i) const\n {\n \tswitch (type_) {\n-\tcase ControlTypeNone:\n-\t\treturn \"<None>\";\n \tcase ControlTypeBool:\n-\t\treturn bool_ ? \"True\" : \"False\";\n+\t\treturn bool_ ? \"True \" : \"False \";\n+\tcase ControlTypeInteger8:\n+\t\treturn std::to_string(integer8_);\n \tcase ControlTypeInteger32:\n \t\treturn std::to_string(integer32_);\n \tcase ControlTypeInteger64:\n \t\treturn std::to_string(integer64_);\n+\tcase ControlTypeFloat:\n+\t\treturn std::to_string(float_);\n+\tcase ControlTypeCompoundBool:\n+\t\treturn pbool_[i] ? \"True \" : \"False \";\n+\tcase ControlTypeCompoundInt8:\n+\t\treturn std::to_string(p8_[i]) + \" \";\n+\tcase ControlTypeCompoundInt32:\n+\t\treturn std::to_string(p32_[i]) + \" \";\n+\tcase ControlTypeCompoundInt64:\n+\t\treturn std::to_string(p64_[i]) + \" \";\n+\tcase ControlTypeCompoundFloat:\n+\t\treturn std::to_string(pfloat_[i]) + \" \";\n+\tdefault:\n+\t\treturn \"<None>\";\n \t}\n+}\n+\n+std::string ControlValue::toString() const\n+{\n+\tif (ControlTypeNone)\n+\t\treturn \"<ValueType Error>\";\n+\n+\tstd::string str;\n+\tfor (unsigned int i = 0; i < numElements_; ++i)\n+\t\tstr += elemToString(i);\n+\n+\treturn str;\n+}\n \n-\treturn \"<ValueType Error>\";\n+bool ControlValue::compareElement(const ControlValue &other, unsigned int i) const\n+{\n+\tswitch (type_) {\n+\tcase ControlTypeBool:\n+\t\treturn bool_ == other.bool_;\n+\tcase ControlTypeInteger8:\n+\t\treturn integer8_ == other.integer8_;\n+\tcase ControlTypeInteger32:\n+\t\treturn integer32_ == other.integer32_;\n+\tcase ControlTypeInteger64:\n+\t\treturn integer64_ == other.integer64_;\n+\tcase ControlTypeFloat:\n+\t\treturn float_ == other.float_;\n+\tcase ControlTypeCompoundBool:\n+\t\treturn pbool_[i] == other.pbool_[i];\n+\tcase ControlTypeCompoundInt8:\n+\t\treturn p8_[i] == other.p8_[i];\n+\tcase ControlTypeCompoundInt32:\n+\t\treturn p32_[i] == other.p32_[i];\n+\tcase ControlTypeCompoundInt64:\n+\t\treturn p64_[i] == other.p64_[i];\n+\tcase ControlTypeCompoundFloat:\n+\t\treturn pfloat_[i] == other.pfloat_[i];\n+\tdefault:\n+\t\treturn false;\n+\t}\n }\n \n /**\n@@ -204,13 +452,27 @@ bool ControlValue::operator==(const ControlValue &other) const\n \tif (type_ != other.type_)\n \t\treturn false;\n \n+\tif (numElements_ != other.numElements())\n+\t\treturn false;\n+\n \tswitch (type_) {\n \tcase ControlTypeBool:\n-\t\treturn bool_ == other.bool_;\n+\tcase ControlTypeInteger8:\n \tcase ControlTypeInteger32:\n-\t\treturn integer32_ == other.integer32_;\n \tcase ControlTypeInteger64:\n-\t\treturn integer64_ == other.integer64_;\n+\tcase ControlTypeFloat:\n+\t\treturn compareElement(other, 0);\n+\tcase ControlTypeCompoundBool:\n+\tcase ControlTypeCompoundInt8:\n+\tcase ControlTypeCompoundInt32:\n+\tcase ControlTypeCompoundInt64:\n+\tcase ControlTypeCompoundFloat:\n+\t\tfor (unsigned int i = 0; i < numElements_; ++i) {\n+\t\t\tif (!compareElement(other, i))\n+\t\t\t\treturn false;\n+\t\t}\n+\n+\t\treturn true;\n \tdefault:\n \t\treturn false;\n \t}\n@@ -330,6 +592,12 @@ Control<bool>::Control(unsigned int id, const char *name)\n {\n }\n \n+template<>\n+Control<int8_t>::Control(unsigned int id, const char *name)\n+\t: ControlId(id, name, ControlTypeInteger8)\n+{\n+}\n+\n template<>\n Control<int32_t>::Control(unsigned int id, const char *name)\n \t: ControlId(id, name, ControlTypeInteger32)\n@@ -341,6 +609,12 @@ Control<int64_t>::Control(unsigned int id, const char *name)\n \t: ControlId(id, name, ControlTypeInteger64)\n {\n }\n+\n+template<>\n+Control<float>::Control(unsigned int id, const char *name)\n+\t: ControlId(id, name, ControlTypeFloat)\n+{\n+}\n #endif /* __DOXYGEN__ */\n \n /**\n@@ -583,15 +857,37 @@ ControlInfoMap::const_iterator ControlInfoMap::find(unsigned int id) const\n * \\return The ControlId map\n */\n \n+bool ControlInfoMap::matchRangeType(enum ControlType type1, enum ControlType type2)\n+{\n+\tif (type1 == type2)\n+\t\treturn true;\n+\n+\tswitch (type1) {\n+\tcase ControlTypeCompoundInt8:\n+\t\treturn type2 == ControlTypeInteger8;\n+\tcase ControlTypeCompoundInt32:\n+\t\treturn type2 == ControlTypeInteger32;\n+\tcase ControlTypeCompoundInt64:\n+\t\treturn type2 == ControlTypeInteger64;\n+\tcase ControlTypeCompoundFloat:\n+\t\treturn type2 == ControlTypeFloat;\n+\tdefault:\n+\t\treturn false;\n+\t}\n+}\n+\n void ControlInfoMap::generateIdmap()\n {\n \tidmap_.clear();\n \n \tfor (const auto &ctrl : *this) {\n-\t\tif (ctrl.first->type() != ctrl.second.min().type()) {\n+\t\tif (!matchRangeType(ctrl.first->type(),\n+\t\t\t\t ctrl.second.min().type())) {\n \t\t\tLOG(Controls, Error)\n \t\t\t\t<< \"Control \" << utils::hex(ctrl.first->id())\n-\t\t\t\t<< \" type and range type mismatch\";\n+\t\t\t\t<< \" type and range type mismatch: \"\n+\t\t\t\t<< ctrl.first->type() << \" - \"\n+\t\t\t\t<< ctrl.second.min().type();\n \t\t\tidmap_.clear();\n \t\t\tclear();\n \t\t\treturn;\ndiff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\nindex f043cf55889e..2bfab35314c4 100644\n--- a/src/libcamera/pipeline/vimc.cpp\n+++ b/src/libcamera/pipeline/vimc.cpp\n@@ -453,6 +453,13 @@ int VimcCameraData::init(MediaDevice *media)\n \t\t\t std::forward_as_tuple(range));\n \t}\n \n+\t/* Register a compound control. */\n+\tctrls.emplace(std::piecewise_construct,\n+\t\t std::forward_as_tuple(&controls::CompoundControl),\n+\t\t std::forward_as_tuple(0, 100));\n+\n+\n+\n \tcontrolInfo_ = std::move(ctrls);\n \n \t/* Initialize the camera properties. */\ndiff --git a/test/controls/compound_controls.cpp b/test/controls/compound_controls.cpp\nnew file mode 100644\nindex 000000000000..de00573df141\n--- /dev/null\n+++ b/test/controls/compound_controls.cpp\n@@ -0,0 +1,94 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * compound_controls.cpp - CompoundControls test\n+ */\n+\n+#include <iostream>\n+#include <vector>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/camera_manager.h>\n+#include <libcamera/control_ids.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/span.h>\n+\n+#include \"camera_controls.h\"\n+\n+#include \"camera_test.h\"\n+#include \"test.h\"\n+\n+using namespace std;\n+using namespace libcamera;\n+\n+class CompoundControlsTest : public CameraTest, public Test\n+{\n+public:\n+\tCompoundControlsTest()\n+\t\t: CameraTest(\"VIMC Sensor B\")\n+\t{\n+\t}\n+\n+protected:\n+\tint init() override\n+\t{\n+\t\treturn status_;\n+\t}\n+\n+\tint run() override\n+\t{\n+\t\tCameraControlValidator validator(camera_.get());\n+\t\tControlList list(controls::controls, &validator);\n+\n+\t\t/*\n+\t\t * Span\n+\t\t *\n+\t\t * A Span can be initialized with an initializer list\n+\t\t * and sequentially or random accessed\n+\t\t */\n+\t\tSpan<int32_t> span = { 1, 2, 3 };\n+\t\tfor (uint32_t i : span)\n+\t\t\tcout << i << endl;\n+\t\tcout << span[0] << endl;\n+\n+\t\t/*\n+\t\t * Compound Controls\n+\t\t *\n+\t\t * As of now, all Controls are now 'compounds' when set with a\n+\t\t * Span<> of values.\n+\t\t *\n+\t\t * We need to define how to establish that a Control is actually\n+\t\t * a compound or supports a single value.\n+\t\t */\n+\t\tlist.set(controls::Brightness, {0, 125, 255});\n+\t\tSpan<int32_t> iSpan = list.get(controls::Brightness);\n+\n+\t\tcout << iSpan.size() << endl;\n+\t\tfor (uint32_t i : iSpan)\n+\t\t\tcout << i << endl;\n+\n+\t\t/*\n+\t\t * But they can still be accessed and operated with a single\n+\t\t * value.\n+\t\t */\n+\t\tlist.set(controls::Brightness, 112);\n+\t\tcout << list.get(controls::Brightness) << endl;\n+\n+\t\t/*\n+\t\t * Or set with a Span and accessed by value. The first item\n+\t\t * is returned.\n+\t\t */\n+\t\tlist.set(controls::Brightness, {50, 125});\n+\t\tcout << list.get(controls::Brightness) << endl;\n+\n+\t\t/* The other way around works as well. */\n+\t\tlist.set(controls::Brightness, 133);\n+\t\tSpan<int32_t> s = list.get(controls::Brightness);\n+\t\tcout << s << endl;\n+\n+\t\treturn TestPass;\n+\t}\n+};\n+\n+TEST_REGISTER(CompoundControlsTest)\ndiff --git a/test/controls/meson.build b/test/controls/meson.build\nindex f0850df28c8a..f4752dcff33b 100644\n--- a/test/controls/meson.build\n+++ b/test/controls/meson.build\n@@ -3,6 +3,7 @@ control_tests = [\n [ 'control_list', 'control_list.cpp' ],\n [ 'control_range', 'control_range.cpp' ],\n [ 'control_value', 'control_value.cpp' ],\n+ [ 'compound_controls', 'compound_controls.cpp'],\n ]\n \n foreach t : control_tests\n", "prefixes": [ "libcamera-devel", "RFC", "1/2" ] }