{"id":2599,"url":"https://patchwork.libcamera.org/api/1.1/patches/2599/?format=json","web_url":"https://patchwork.libcamera.org/patch/2599/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200112010212.2609025-18-niklas.soderlund@ragnatech.se>","date":"2020-01-12T01:01:57","name":"[libcamera-devel,v4,17/32] libcamera: v4l2_videodevice: Add V4L2BufferCache to deal with index mapping","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"775b9fea4ddcd199ba75cac9b31a72ca9002bf8b","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/1.1/people/5/?format=json","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/2599/mbox/","series":[{"id":617,"url":"https://patchwork.libcamera.org/api/1.1/series/617/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=617","date":"2020-01-12T01:01:40","name":"libcamera: Rework buffer API","version":4,"mbox":"https://patchwork.libcamera.org/series/617/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/2599/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/2599/checks/","tags":{},"headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net\n\t[195.74.38.228])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0FAF8606DF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 12 Jan 2020 02:03:13 +0100 (CET)","from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de\n\t[84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA\n\tid 4c2f8717-34d7-11ea-b6d8-005056917f90;\n\tSun, 12 Jan 2020 02:03:09 +0100 (CET)"],"X-Halon-ID":"4c2f8717-34d7-11ea-b6d8-005056917f90","Authorized-sender":"niklas@soderlund.pp.se","From":"=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","To":"libcamera-devel@lists.libcamera.org","Date":"Sun, 12 Jan 2020 02:01:57 +0100","Message-Id":"<20200112010212.2609025-18-niklas.soderlund@ragnatech.se>","X-Mailer":"git-send-email 2.24.1","In-Reply-To":"<20200112010212.2609025-1-niklas.soderlund@ragnatech.se>","References":"<20200112010212.2609025-1-niklas.soderlund@ragnatech.se>","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH v4 17/32] libcamera: v4l2_videodevice: Add\n\tV4L2BufferCache to deal with index mapping","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":"Sun, 12 Jan 2020 01:03:13 -0000"},"content":"In preparation for the FrameBuffer interface add a class that will deal\nwith keeping the cache between dmabuf file descriptors and V4L2 video\ndevice buffer indexes.\n\nThis initial implementation ensures that no hot association is lost\nwhile its eviction strategy could be improved in the future.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n* Changes since v3\n- Only print cach misses if it's more then cache_.size()\n- Invert hit/miss logic in V4L2BufferCache::get()\n\n* Changes since v2\n- Entry constructor and operator== takes FrameBuffer instead of\n  std::vector<FrameBuffer::Plane>.\n- Move Entry inline functions to from .h to .cpp file\n- Make Entry::Plane a struct\n- Add a miss counter\n\n* Changes since v1\n- fetch() take a const reference instead of a const pointer\n- Rename argument for V4L2BufferCache()\n- Rename V4L2BufferCache::CacheInfo to V4L2BufferCache::Entry.\n- Turn V4L2BufferCache::Entry into a class with constructors for\n  foo.emplace_back().\n- Rename V4L2BufferCache::fetch() to V4L2BufferCache::get()\n- Make use of operator==\n- Large updates of documentation.\n---\n src/libcamera/include/v4l2_videodevice.h |  45 +++++++-\n src/libcamera/v4l2_videodevice.cpp       | 136 ++++++++++++++++++++++-\n 2 files changed, 177 insertions(+), 4 deletions(-)","diff":"diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\nindex 27ec77cdcc3c07ff..9d22754a39a75621 100644\n--- a/src/libcamera/include/v4l2_videodevice.h\n+++ b/src/libcamera/include/v4l2_videodevice.h\n@@ -11,7 +11,9 @@\n #include <vector>\n \n #include <linux/videodev2.h>\n+#include <memory>\n \n+#include <libcamera/buffer.h>\n #include <libcamera/geometry.h>\n #include <libcamera/pixelformats.h>\n #include <libcamera/signal.h>\n@@ -22,9 +24,6 @@\n \n namespace libcamera {\n \n-class Buffer;\n-class BufferMemory;\n-class BufferPool;\n class EventNotifier;\n class FileDescriptor;\n class MediaDevice;\n@@ -106,6 +105,46 @@ struct V4L2Capability final : v4l2_capability {\n \t}\n };\n \n+class V4L2BufferCache\n+{\n+public:\n+\tV4L2BufferCache(unsigned int numEntries);\n+\tV4L2BufferCache(const std::vector<std::unique_ptr<FrameBuffer>> &buffers);\n+\t~V4L2BufferCache();\n+\n+\tint get(const FrameBuffer &buffer);\n+\tvoid put(unsigned int index);\n+\n+private:\n+\tclass Entry\n+\t{\n+\tpublic:\n+\t\tEntry();\n+\t\tEntry(bool free, const FrameBuffer &buffer);\n+\n+\t\tbool operator==(const FrameBuffer &buffer);\n+\n+\t\tbool free;\n+\n+\tprivate:\n+\t\tstruct Plane {\n+\t\t\tPlane(const FrameBuffer::Plane &plane)\n+\t\t\t\t: fd(plane.fd.fd()), length(plane.length)\n+\t\t\t{\n+\t\t\t}\n+\n+\t\t\tint fd;\n+\t\t\tunsigned int length;\n+\t\t};\n+\n+\t\tstd::vector<Plane> planes_;\n+\t};\n+\n+\tstd::vector<Entry> cache_;\n+\t/* \\todo Expose the miss counter through an instrumentation API. */\n+\tunsigned int missCounter_;\n+};\n+\n class V4L2DeviceFormat\n {\n public:\ndiff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\nindex d22655c676bef1ae..84c45dbcb85c8638 100644\n--- a/src/libcamera/v4l2_videodevice.cpp\n+++ b/src/libcamera/v4l2_videodevice.cpp\n@@ -20,7 +20,6 @@\n \n #include <linux/drm_fourcc.h>\n \n-#include <libcamera/buffer.h>\n #include <libcamera/event_notifier.h>\n #include <libcamera/file_descriptor.h>\n \n@@ -136,6 +135,141 @@ LOG_DECLARE_CATEGORY(V4L2)\n  * \\return True if the video device provides Streaming I/O IOCTLs\n  */\n \n+/**\n+ * \\class V4L2BufferCache\n+ * \\brief Hot cache of associations between V4L2 buffer indexes and FrameBuffer\n+ *\n+ * When importing buffers, V4L2 performs lazy mapping of dmabuf instances at\n+ * VIDIOC_QBUF (or VIDIOC_PREPARE_BUF) time and keeps the mapping associated\n+ * with the V4L2 buffer, as identified by its index. If the same V4L2 buffer is\n+ * then reused and queued with different dmabufs, the old dmabufs will be\n+ * unmapped and the new ones mapped. To keep this process efficient, it is\n+ * crucial to consistently use the same V4L2 buffer for given dmabufs through\n+ * the whole duration of a capture cycle.\n+ *\n+ * The V4L2BufferCache class keeps a map of previous dmabufs to V4L2 buffer\n+ * index associations to help selecting V4L2 buffers. It tracks, for every\n+ * entry, if the V4L2 buffer is in use, and offers lookup of the best free V4L2\n+ * buffer for a set of dmabufs.\n+ */\n+\n+/**\n+ * \\brief Create an empty cache with \\a numEntries entries\n+ * \\param[in] numEntries Number of entries to reserve in the cache\n+ *\n+ * Create a cache with \\a numEntries entries all marked as unused. The entries\n+ * will be populated as the cache is used. This is typically used to implement\n+ * buffer import, with buffers added to the cache as they are queued.\n+ */\n+V4L2BufferCache::V4L2BufferCache(unsigned int numEntries)\n+\t: missCounter_(0)\n+{\n+\tcache_.resize(numEntries);\n+}\n+\n+/**\n+ * \\brief Create a pre-populated cache\n+ * \\param[in] buffers Array of buffers to pre-populated with\n+ *\n+ * Create a cache pre-populated with \\a buffers. This is typically used to\n+ * implement buffer export, with all buffers added to the cache when they are\n+ * allocated.\n+ */\n+V4L2BufferCache::V4L2BufferCache(const std::vector<std::unique_ptr<FrameBuffer>> &buffers)\n+\t: missCounter_(0)\n+{\n+\tfor (const std::unique_ptr<FrameBuffer> &buffer : buffers)\n+\t\tcache_.emplace_back(true, buffer->planes());\n+}\n+\n+V4L2BufferCache::~V4L2BufferCache()\n+{\n+\tif (missCounter_ > cache_.size())\n+\t\tLOG(V4L2, Debug) << \"Cache misses: \" << missCounter_;\n+}\n+\n+/**\n+ * \\brief Find the best V4L2 buffer for a FrameBuffer\n+ * \\param[in] buffer The FrameBuffer\n+ *\n+ * Find the best V4L2 buffer index to be used for the FrameBuffer \\a buffer\n+ * based on previous mappings of frame buffers to V4L2 buffers. If a free V4L2\n+ * buffer previously used with the same dmabufs as \\a buffer is found in the\n+ * cache, return its index. Otherwise return the index of the first free V4L2\n+ * buffer and record its association with the dmabufs of \\a buffer.\n+ *\n+ * \\return The index of the best V4L2 buffer, or -ENOENT if no free V4L2 buffer\n+ * is available\n+ */\n+int V4L2BufferCache::get(const FrameBuffer &buffer)\n+{\n+\tbool hit = false;\n+\tint use = -1;\n+\n+\tfor (unsigned int index = 0; index < cache_.size(); index++) {\n+\t\tconst Entry &entry = cache_[index];\n+\n+\t\tif (!entry.free)\n+\t\t\tcontinue;\n+\n+\t\tif (use < 0)\n+\t\t\tuse = index;\n+\n+\t\t/* Try to find a cache hit by comparing the planes. */\n+\t\tif (cache_[index] == buffer) {\n+\t\t\thit = true;\n+\t\t\tuse = index;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!hit)\n+\t\tmissCounter_++;\n+\n+\tif (use < 0)\n+\t\treturn -ENOENT;\n+\n+\tcache_[use] = Entry(false, buffer);\n+\n+\treturn use;\n+}\n+\n+/**\n+ * \\brief Mark buffer \\a index as free in the cache\n+ * \\param[in] index The V4L2 buffer index\n+ */\n+void V4L2BufferCache::put(unsigned int index)\n+{\n+\tASSERT(index < cache_.size());\n+\tcache_[index].free = true;\n+}\n+\n+V4L2BufferCache::Entry::Entry()\n+\t: free(true)\n+{\n+}\n+\n+V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer)\n+\t: free(free)\n+{\n+\tfor (const FrameBuffer::Plane &plane : buffer.planes())\n+\t\tplanes_.emplace_back(plane);\n+}\n+\n+bool V4L2BufferCache::Entry::operator==(const FrameBuffer &buffer)\n+{\n+\tconst std::vector<FrameBuffer::Plane> &planes = buffer.planes();\n+\n+\tif (planes_.size() != planes.size())\n+\t\treturn false;\n+\n+\tfor (unsigned int i = 0; i < planes.size(); i++)\n+\t\tif (planes_[i].fd != planes[i].fd.fd() ||\n+\t\t    planes_[i].length != planes[i].length)\n+\t\t\treturn false;\n+\treturn true;\n+}\n+\n /**\n  * \\class V4L2DeviceFormat\n  * \\brief The V4L2 video device image format and sizes\n","prefixes":["libcamera-devel","v4","17/32"]}