[libcamera-devel] [PATCH 18/30] libcamera: v4l2_videodevice: Add V4L2BufferCache to deal with index mapping

Niklas Söderlund niklas.soderlund at ragnatech.se
Wed Nov 27 00:36:08 CET 2019


In preparation for the FrameBuffer interface add a class which will deal
with keeping the cache between dmafds and V4L2 video device buffer
indexes.

This initial implement ensures that no hot association is lost while its
eviction strategy could be improved in the future.

Signed-off-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>
---
 src/libcamera/include/v4l2_videodevice.h |  20 ++++-
 src/libcamera/v4l2_videodevice.cpp       | 105 ++++++++++++++++++++++-
 2 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
index 34bbff41760753bd..254f8797af42dd8a 100644
--- a/src/libcamera/include/v4l2_videodevice.h
+++ b/src/libcamera/include/v4l2_videodevice.h
@@ -12,6 +12,7 @@
 
 #include <linux/videodev2.h>
 
+#include <libcamera/buffer.h>
 #include <libcamera/geometry.h>
 #include <libcamera/pixelformats.h>
 #include <libcamera/signal.h>
@@ -22,7 +23,6 @@
 
 namespace libcamera {
 
-class Buffer;
 class BufferMemory;
 class BufferPool;
 class EventNotifier;
@@ -105,6 +105,24 @@ struct V4L2Capability final : v4l2_capability {
 	}
 };
 
+class V4L2BufferCache
+{
+public:
+	V4L2BufferCache(unsigned int size);
+	V4L2BufferCache(const std::vector<FrameBuffer *> buffers);
+
+	int fetch(const FrameBuffer *buffer);
+	void put(unsigned int index);
+
+private:
+	struct CacheInfo {
+		bool free;
+		std::vector<FrameBuffer::Plane> last;
+	};
+
+	std::vector<CacheInfo> cache_;
+};
+
 class V4L2DeviceFormat
 {
 public:
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index a05dd6a1f7d86eaa..c82f2829601bd14c 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -19,7 +19,6 @@
 
 #include <linux/drm_fourcc.h>
 
-#include <libcamera/buffer.h>
 #include <libcamera/event_notifier.h>
 
 #include "log.h"
@@ -134,6 +133,110 @@ LOG_DECLARE_CATEGORY(V4L2)
  * \return True if the video device provides Streaming I/O IOCTLs
  */
 
+/**
+ * \class V4L2BufferCache
+ * \brief Hot cache of associations between V4L2 index and FrameBuffer
+ *
+ * There is performance to be gained if the same V4L2 buffer index can be
+ * reused for the same FrameBuffer object as the kernel don't have to redo
+ * the mapping. The V4L2BufferCache tries to keep a hot-cache of mappings
+ * between the two.
+ *
+ * If there is a cache miss is not critical, everything still works as expected.
+ */
+
+/**
+ * \brief Create a empty cache of a given size
+ * \param[in] size Size of cache to create
+ *
+ * Create a cold cache with \a size entries. The cache will be populated as
+ * it's being used.
+ */
+V4L2BufferCache::V4L2BufferCache(unsigned int size)
+{
+	cache_.resize(size, { .free = true, .last = {} });
+}
+
+/**
+ * \brief Create a pre-populated cache
+ * \param[in] buffers Array of buffers to pre-populated with
+ *
+ * Create a warm cache from \a buffers.
+ */
+V4L2BufferCache::V4L2BufferCache(const std::vector<FrameBuffer *> buffers)
+{
+	for (const FrameBuffer *buffer : buffers)
+		cache_.push_back({ .free = true, .last = buffer->planes() });
+}
+
+/**
+ * \brief Fetch a index from the cache
+ * \param[in] buffer FrameBuffer to match
+ *
+ * Try to find \a buffer in cache and if it's free reuse the last used index
+ * for this buffer. If the buffer have never been seen or if have been evinced
+ * from the cache the first free index is pieced instead. Likewise if the last
+ * used index is in use a new free index is picked.
+ *
+ * When an index is picked it is marked as in-use and returned to the caller.
+ * The association is also recorded so it if possible can reused the next time
+ * the FrameBuffer is seen.
+ *
+ * \return V4L2 buffer index
+ */
+int V4L2BufferCache::fetch(const FrameBuffer *buffer)
+{
+	int use = -1;
+
+	for (unsigned int index = 0; index < cache_.size(); index++) {
+		if (!cache_[index].free)
+			continue;
+
+		if (use < 0)
+			use = index;
+
+		/* Try to find a cache hit by comparing the planes. */
+		std::vector<FrameBuffer::Plane> planes = buffer->planes();
+		if (cache_[index].last.size() != planes.size())
+			continue;
+
+		bool match = true;
+		for (unsigned int i = 0; i < planes.size(); i++) {
+			if (cache_[index].last[i].fd != planes[i].fd ||
+			    cache_[index].last[i].length != planes[i].length) {
+				match = false;
+				break;
+			}
+		}
+
+		if (!match)
+			continue;
+
+		use = index;
+		break;
+	}
+
+	if (use < 0)
+		return -ENOENT;
+
+	cache_[use].free = false;
+	cache_[use].last = buffer->planes();
+
+	return use;
+}
+
+/**
+ * \brief Pit a V4L2 index back in the cache
+ * \param[in] index V4L2 index
+ *
+ * Mark the \a index as free in the cache so it can be reused.
+ */
+void V4L2BufferCache::put(unsigned int index)
+{
+	ASSERT(index < cache_.size());
+	cache_[index].free = true;
+}
+
 /**
  * \class V4L2DeviceFormat
  * \brief The V4L2 video device image format and sizes
-- 
2.24.0



More information about the libcamera-devel mailing list