[{"id":3407,"web_url":"https://patchwork.libcamera.org/comment/3407/","msgid":"<20200111003422.GJ4859@pendragon.ideasonboard.com>","date":"2020-01-11T00:34:22","subject":"Re: [libcamera-devel] [PATCH v3 17/33] libcamera: v4l2_videodevice:\n\tAdd V4L2BufferCache to deal with index mapping","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nThank you for the patch.\n\nOn Fri, Jan 10, 2020 at 08:37:52PM +0100, Niklas Söderlund wrote:\n> In preparation for the FrameBuffer interface add a class that will deal\n> with keeping the cache between dmabuf file descriptors and V4L2 video\n> device buffer indexes.\n> \n> This initial implementation ensures that no hot association is lost\n> while its eviction strategy could be improved in the future.\n> \n> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\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(-)\n> \n> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> index 1f52fe0120831fa3..820b0cb297744828 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 MediaDevice;\n>  class MediaEntity;\n> @@ -105,6 +104,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:\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index f60490cc9a1b1771..474849846bddca8b 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>  \n>  #include \"log.h\"\n> @@ -135,6 +134,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_)\n\nI think this should be\n\n\tif (missCounter_ > cache_.size())\n\nas we will typically have an initial miss for every entry.\n\n> +\t\tLOG(V4L2, Debug) << \"Cached misses \" << missCounter_;\n\ns/Cached misses/Cache misses:/\n\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 miss = true;\n\nHow about calling this hit and initializing it to false, ...\n\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\tmiss = false;\n\n... and setting it to true here ...\n\n> +\t\t\tuse = index;\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +\n> +\tif (miss)\n\nand checking if (!hit) ? I think the logic would be slightly easier to\nread.\n\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","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3D1D4606AC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 11 Jan 2020 01:34:36 +0100 (CET)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 9018B52F;\n\tSat, 11 Jan 2020 01:34:35 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1578702875;\n\tbh=4YIhdY0FIoqy+xelTIlFzCNEfTjrxPiHMS/V79KBI6Q=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=PBVKdFycpHHMOPHXxOyeS+jzFwOo/LRv+Rucotgv++xpNqMByOKta88GjPC5Qoeno\n\tNDVm3OLd5novWRygYyB7lt7Q0y2SFyNIqxpqD3qvd3IN2Vg5hI/kP1aad5l3DPleJd\n\t5xHXjCSNPYEGplCmQa7Gyw0+lj7+aNq0AwEemm50=","Date":"Sat, 11 Jan 2020 02:34:22 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20200111003422.GJ4859@pendragon.ideasonboard.com>","References":"<20200110193808.2266294-1-niklas.soderlund@ragnatech.se>\n\t<20200110193808.2266294-18-niklas.soderlund@ragnatech.se>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20200110193808.2266294-18-niklas.soderlund@ragnatech.se>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v3 17/33] libcamera: v4l2_videodevice:\n\tAdd V4L2BufferCache 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":"Sat, 11 Jan 2020 00:34:36 -0000"}}]