{"id":1695,"url":"https://patchwork.libcamera.org/api/1.1/patches/1695/?format=json","web_url":"https://patchwork.libcamera.org/patch/1695/","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":"<20190713172351.25452-15-laurent.pinchart@ideasonboard.com>","date":"2019-07-13T17:23:49","name":"[libcamera-devel,v2,14/16] libcamera: stream: Map external buffers to indexes","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"34e9247936f94adf38db0e1fbdd3392f4e30d473","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/1.1/people/2/?format=json","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/1695/mbox/","series":[{"id":430,"url":"https://patchwork.libcamera.org/api/1.1/series/430/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=430","date":"2019-07-13T17:23:35","name":"Add support for external buffers","version":2,"mbox":"https://patchwork.libcamera.org/series/430/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/1695/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/1695/checks/","tags":{},"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 9B3CE6184A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 13 Jul 2019 19:24:48 +0200 (CEST)","from pendragon.ideasonboard.com (softbank126209254147.bbtec.net\n\t[126.209.254.147])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 741A82B2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 13 Jul 2019 19:24:47 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1563038688;\n\tbh=3dTyrFsQUbZh0XLYEO9BsnS5zVsLeirk24ORz+fMC6o=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=CiOmlmKBzSkWdhOWeTOcSNDJPxKnN6yVQc+J1m2YdJa4BTQbBQP6ViRs7K1oW2GEJ\n\te1MxQDi9FUzDk1Gw5AI57G5LIrDMo79zhlqcaIeNPs1N++gzISa87y8vGnOc6P+5cX\n\tOeQEOGWFlpE2dQoyI777zs9dDSLFXf3ZI0WDOWlQ=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Sat, 13 Jul 2019 20:23:49 +0300","Message-Id":"<20190713172351.25452-15-laurent.pinchart@ideasonboard.com>","X-Mailer":"git-send-email 2.21.0","In-Reply-To":"<20190713172351.25452-1-laurent.pinchart@ideasonboard.com>","References":"<20190713172351.25452-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH v2 14/16] libcamera: stream: Map external\n\tbuffers to indexes","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","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, 13 Jul 2019 17:24:48 -0000"},"content":"From: Jacopo Mondi <jacopo@jmondi.org>\n\nAdd and use an operation to assign to Buffer representing external\nmemory locations an index at queueRequest() time. The index is used to\nidentify the memory buffer to be queued to the video device once the\nbuffer will be queued in a Request.\n\nIn order to minimize relocations in the V4L2 backend, this method\nprovides a best-effort caching mechanisms that attempts to reuse\nBufferMemory previously mapped to the buffer's dmabuf file descriptors,\nif any.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n include/libcamera/buffer.h |  2 +\n include/libcamera/stream.h |  6 +++\n src/libcamera/camera.cpp   | 20 +++++++-\n src/libcamera/stream.cpp   | 96 ++++++++++++++++++++++++++++++++++++++\n 4 files changed, 123 insertions(+), 1 deletion(-)","diff":"diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h\nindex fc5c7d4c41b6..7b657509ab5f 100644\n--- a/include/libcamera/buffer.h\n+++ b/include/libcamera/buffer.h\n@@ -30,6 +30,8 @@ public:\n \tunsigned int length() const { return length_; }\n \n private:\n+\tfriend class Stream;\n+\n \tint mmap();\n \tint munmap();\n \ndiff --git a/include/libcamera/stream.h b/include/libcamera/stream.h\nindex 1883d9e9b9c5..2e619cdf0e89 100644\n--- a/include/libcamera/stream.h\n+++ b/include/libcamera/stream.h\n@@ -85,12 +85,18 @@ public:\n protected:\n \tfriend class Camera;\n \n+\tint mapBuffer(const Buffer *buffer);\n+\tvoid unmapBuffer(const Buffer *buffer);\n+\n \tvoid createBuffers(MemoryType memory, unsigned int count);\n \tvoid destroyBuffers();\n \n \tBufferPool bufferPool_;\n \tStreamConfiguration configuration_;\n \tMemoryType memoryType_;\n+\n+private:\n+\tstd::vector<std::pair<std::array<int, 3>, unsigned int>> bufferCache_;\n };\n \n } /* namespace libcamera */\ndiff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\nindex db15fd46cd51..f2deb38da367 100644\n--- a/src/libcamera/camera.cpp\n+++ b/src/libcamera/camera.cpp\n@@ -800,6 +800,7 @@ Request *Camera::createRequest(uint64_t cookie)\n  * \\retval -ENODEV The camera has been disconnected from the system\n  * \\retval -EACCES The camera is not running so requests can't be queued\n  * \\retval -EINVAL The request is invalid\n+ * \\retval -ENOMEM No buffer memory was available to handle the request\n  */\n int Camera::queueRequest(Request *request)\n {\n@@ -818,6 +819,16 @@ int Camera::queueRequest(Request *request)\n \t\t\treturn -EINVAL;\n \t\t}\n \n+\t\tif (stream->memoryType() == ExternalMemory) {\n+\t\t\tint index = stream->mapBuffer(buffer);\n+\t\t\tif (index < 0) {\n+\t\t\t\tLOG(Camera, Error) << \"No buffer memory available\";\n+\t\t\t\treturn -ENOMEM;\n+\t\t\t}\n+\n+\t\t\tbuffer->index_ = index;\n+\t\t}\n+\n \t\tbuffer->mem_ = &stream->buffers()[buffer->index_];\n \t}\n \n@@ -901,7 +912,14 @@ int Camera::stop()\n  */\n void Camera::requestComplete(Request *request)\n {\n-\trequestCompleted.emit(request, request->bufferMap_);\n+\tfor (auto it : request->buffers()) {\n+\t\tStream *stream = it.first;\n+\t\tBuffer *buffer = it.second;\n+\t\tif (stream->memoryType() == ExternalMemory)\n+\t\t\tstream->unmapBuffer(buffer);\n+\t}\n+\n+\trequestCompleted.emit(request, request->buffers());\n \tdelete request;\n }\n \ndiff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp\nindex e6aa1b643a37..729d36b31712 100644\n--- a/src/libcamera/stream.cpp\n+++ b/src/libcamera/stream.cpp\n@@ -13,6 +13,8 @@\n #include <iomanip>\n #include <sstream>\n \n+#include <libcamera/request.h>\n+\n #include \"log.h\"\n \n /**\n@@ -476,6 +478,8 @@ std::unique_ptr<Buffer> Stream::createBuffer(unsigned int index)\n  * will return a null pointer when called on streams using the ExternalMemory\n  * type.\n  *\n+ * \\sa Stream::mapBuffer()\n+ *\n  * \\return A newly created Buffer on success or nullptr otherwise\n  */\n std::unique_ptr<Buffer> Stream::createBuffer(const std::array<int, 3> &fds)\n@@ -521,6 +525,86 @@ std::unique_ptr<Buffer> Stream::createBuffer(const std::array<int, 3> &fds)\n  * \\return The memory type used by the stream\n  */\n \n+/**\n+ * \\brief Map a Buffer to a buffer memory index\n+ * \\param[in] buffer The buffer to map to a buffer memory index\n+ *\n+ * Streams configured to use externally allocated memory need to maintain a\n+ * best-effort association between the memory area the \\a buffer represents\n+ * and the associated buffer memory in the Stream's pool.\n+ *\n+ * The buffer memory to use, once the \\a buffer reaches the video device,\n+ * is selected using the index assigned to the \\a buffer and to minimize\n+ * relocations in the V4L2 back-end, this operation provides a best-effort\n+ * caching mechanism that associates to the dmabuf file descriptors contained\n+ * in the \\a buffer the index of the buffer memory that was lastly queued with\n+ * those file descriptors set.\n+ *\n+ * If the Stream uses internally allocated memory, the index of the memory\n+ * buffer to use will match the one request at Stream::createBuffer(unsigned int)\n+ * time, and no mapping is thus required.\n+ *\n+ * \\return The buffer memory index for the buffer on success, or a negative\n+ * error code otherwise\n+ * \\retval -ENOMEM No buffer memory was available to map the buffer\n+ */\n+int Stream::mapBuffer(const Buffer *buffer)\n+{\n+\tASSERT(memoryType_ == ExternalMemory);\n+\n+\tif (bufferCache_.empty())\n+\t\treturn -ENOMEM;\n+\n+\tconst std::array<int, 3> &dmabufs = buffer->dmabufs();\n+\n+\t/*\n+\t * Try to find a previously mapped buffer in the cache. If we miss, use\n+\t * the oldest entry in the cache.\n+\t */\n+\tauto map = std::find_if(bufferCache_.begin(), bufferCache_.end(),\n+\t\t\t\t[&](std::pair<std::array<int, 3>, unsigned int> &entry) {\n+\t\t\t\t\treturn entry.first == dmabufs;\n+\t\t\t\t});\n+\tif (map == bufferCache_.end())\n+\t\tmap = bufferCache_.begin();\n+\n+\t/*\n+\t * Update the dmabuf file descriptors of the entry. We can't assume that\n+\t * identical file descriptor numbers refer to the same dmabuf object as\n+\t * it may have been closed and its file descriptor reused. We thus need\n+\t * to update the plane's internally cached mmap()ed memory.\n+\t */\n+\tunsigned int index = map->second;\n+\tBufferMemory *mem = &bufferPool_.buffers()[index];\n+\tmem->planes().clear();\n+\n+\tfor (unsigned int i = 0; i < dmabufs.size(); ++i) {\n+\t\tif (dmabufs[i] == -1)\n+\t\t\tbreak;\n+\n+\t\tmem->planes().emplace_back();\n+\t\tmem->planes().back().setDmabuf(dmabufs[i], 0);\n+\t}\n+\n+\t/* Remove the buffer from the cache and return its index. */\n+\tbufferCache_.erase(map);\n+\treturn index;\n+}\n+\n+/**\n+ * \\brief Unmap a Buffer from its buffer memory\n+ * \\param[in] buffer The buffer to unmap\n+ *\n+ * This method releases the buffer memory entry that was mapped by mapBuffer(),\n+ * making it available for new mappings.\n+ */\n+void Stream::unmapBuffer(const Buffer *buffer)\n+{\n+\tASSERT(memoryType_ == ExternalMemory);\n+\n+\tbufferCache_.emplace_back(buffer->dmabufs(), buffer->index());\n+}\n+\n /**\n  * \\brief Create buffers for the stream\n  * \\param count The number of buffers to create\n@@ -536,6 +620,18 @@ void Stream::createBuffers(MemoryType memory, unsigned int count)\n \n \tmemoryType_ = memory;\n \tbufferPool_.createBuffers(count);\n+\n+\t/* Streams with internal memory usage do not need buffer mapping. */\n+\tif (memoryType_ == InternalMemory)\n+\t\treturn;\n+\n+\t/*\n+\t * Prepare for buffer mapping by adding all buffer memory entries to the\n+\t * cache.\n+\t */\n+\tbufferCache_.clear();\n+\tfor (unsigned int i = 0; i < bufferPool_.count(); ++i)\n+\t\tbufferCache_.emplace_back(std::array<int, 3>{ -1, -1, -1 }, i);\n }\n \n /**\n","prefixes":["libcamera-devel","v2","14/16"]}