summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacopo Mondi <jacopo@jmondi.org>2019-06-28 15:11:34 +0200
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2019-07-14 16:01:09 +0300
commit9ed9d9b3c1a5e6a51e1aa869c758458c57c1a0f1 (patch)
tree88b7d397bcbded92bfda1d11e43a42cf191a02e0
parent689e8916caf11942286a1f1264e55dcb709d3939 (diff)
libcamera: stream: Map external buffers to indexes
Add and use an operation to assign to Buffer representing external memory locations an index at queueRequest() time. The index is used to identify the memory buffer to be queued to the video device once the buffer will be queued in a Request. In order to minimize relocations in the V4L2 backend, this method provides a best-effort caching mechanisms that attempts to reuse BufferMemory previously mapped to the buffer's dmabuf file descriptors, if any. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
-rw-r--r--include/libcamera/buffer.h2
-rw-r--r--include/libcamera/stream.h6
-rw-r--r--src/libcamera/camera.cpp20
-rw-r--r--src/libcamera/stream.cpp96
4 files changed, 123 insertions, 1 deletions
diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
index fc5c7d4c..7b657509 100644
--- a/include/libcamera/buffer.h
+++ b/include/libcamera/buffer.h
@@ -30,6 +30,8 @@ public:
unsigned int length() const { return length_; }
private:
+ friend class Stream;
+
int mmap();
int munmap();
diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
index 1883d9e9..2e619cdf 100644
--- a/include/libcamera/stream.h
+++ b/include/libcamera/stream.h
@@ -85,12 +85,18 @@ public:
protected:
friend class Camera;
+ int mapBuffer(const Buffer *buffer);
+ void unmapBuffer(const Buffer *buffer);
+
void createBuffers(MemoryType memory, unsigned int count);
void destroyBuffers();
BufferPool bufferPool_;
StreamConfiguration configuration_;
MemoryType memoryType_;
+
+private:
+ std::vector<std::pair<std::array<int, 3>, unsigned int>> bufferCache_;
};
} /* namespace libcamera */
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index db15fd46..f2deb38d 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -800,6 +800,7 @@ Request *Camera::createRequest(uint64_t cookie)
* \retval -ENODEV The camera has been disconnected from the system
* \retval -EACCES The camera is not running so requests can't be queued
* \retval -EINVAL The request is invalid
+ * \retval -ENOMEM No buffer memory was available to handle the request
*/
int Camera::queueRequest(Request *request)
{
@@ -818,6 +819,16 @@ int Camera::queueRequest(Request *request)
return -EINVAL;
}
+ if (stream->memoryType() == ExternalMemory) {
+ int index = stream->mapBuffer(buffer);
+ if (index < 0) {
+ LOG(Camera, Error) << "No buffer memory available";
+ return -ENOMEM;
+ }
+
+ buffer->index_ = index;
+ }
+
buffer->mem_ = &stream->buffers()[buffer->index_];
}
@@ -901,7 +912,14 @@ int Camera::stop()
*/
void Camera::requestComplete(Request *request)
{
- requestCompleted.emit(request, request->bufferMap_);
+ for (auto it : request->buffers()) {
+ Stream *stream = it.first;
+ Buffer *buffer = it.second;
+ if (stream->memoryType() == ExternalMemory)
+ stream->unmapBuffer(buffer);
+ }
+
+ requestCompleted.emit(request, request->buffers());
delete request;
}
diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
index 98178e06..ed91be7f 100644
--- a/src/libcamera/stream.cpp
+++ b/src/libcamera/stream.cpp
@@ -13,6 +13,8 @@
#include <iomanip>
#include <sstream>
+#include <libcamera/request.h>
+
#include "log.h"
/**
@@ -476,6 +478,8 @@ std::unique_ptr<Buffer> Stream::createBuffer(unsigned int index)
* will return a null pointer when called on streams using the InternalMemory
* type.
*
+ * \sa Stream::mapBuffer()
+ *
* \return A newly created Buffer on success or nullptr otherwise
*/
std::unique_ptr<Buffer> Stream::createBuffer(const std::array<int, 3> &fds)
@@ -522,6 +526,86 @@ std::unique_ptr<Buffer> Stream::createBuffer(const std::array<int, 3> &fds)
*/
/**
+ * \brief Map a Buffer to a buffer memory index
+ * \param[in] buffer The buffer to map to a buffer memory index
+ *
+ * Streams configured to use externally allocated memory need to maintain a
+ * best-effort association between the memory area the \a buffer represents
+ * and the associated buffer memory in the Stream's pool.
+ *
+ * The buffer memory to use, once the \a buffer reaches the video device,
+ * is selected using the index assigned to the \a buffer and to minimize
+ * relocations in the V4L2 back-end, this operation provides a best-effort
+ * caching mechanism that associates to the dmabuf file descriptors contained
+ * in the \a buffer the index of the buffer memory that was lastly queued with
+ * those file descriptors set.
+ *
+ * If the Stream uses internally allocated memory, the index of the memory
+ * buffer to use will match the one request at Stream::createBuffer(unsigned int)
+ * time, and no mapping is thus required.
+ *
+ * \return The buffer memory index for the buffer on success, or a negative
+ * error code otherwise
+ * \retval -ENOMEM No buffer memory was available to map the buffer
+ */
+int Stream::mapBuffer(const Buffer *buffer)
+{
+ ASSERT(memoryType_ == ExternalMemory);
+
+ if (bufferCache_.empty())
+ return -ENOMEM;
+
+ const std::array<int, 3> &dmabufs = buffer->dmabufs();
+
+ /*
+ * Try to find a previously mapped buffer in the cache. If we miss, use
+ * the oldest entry in the cache.
+ */
+ auto map = std::find_if(bufferCache_.begin(), bufferCache_.end(),
+ [&](std::pair<std::array<int, 3>, unsigned int> &entry) {
+ return entry.first == dmabufs;
+ });
+ if (map == bufferCache_.end())
+ map = bufferCache_.begin();
+
+ /*
+ * Update the dmabuf file descriptors of the entry. We can't assume that
+ * identical file descriptor numbers refer to the same dmabuf object as
+ * it may have been closed and its file descriptor reused. We thus need
+ * to update the plane's internally cached mmap()ed memory.
+ */
+ unsigned int index = map->second;
+ BufferMemory *mem = &bufferPool_.buffers()[index];
+ mem->planes().clear();
+
+ for (unsigned int i = 0; i < dmabufs.size(); ++i) {
+ if (dmabufs[i] == -1)
+ break;
+
+ mem->planes().emplace_back();
+ mem->planes().back().setDmabuf(dmabufs[i], 0);
+ }
+
+ /* Remove the buffer from the cache and return its index. */
+ bufferCache_.erase(map);
+ return index;
+}
+
+/**
+ * \brief Unmap a Buffer from its buffer memory
+ * \param[in] buffer The buffer to unmap
+ *
+ * This method releases the buffer memory entry that was mapped by mapBuffer(),
+ * making it available for new mappings.
+ */
+void Stream::unmapBuffer(const Buffer *buffer)
+{
+ ASSERT(memoryType_ == ExternalMemory);
+
+ bufferCache_.emplace_back(buffer->dmabufs(), buffer->index());
+}
+
+/**
* \brief Create buffers for the stream
* \param[in] count The number of buffers to create
* \param[in] memory The stream memory type
@@ -536,6 +620,18 @@ void Stream::createBuffers(MemoryType memory, unsigned int count)
memoryType_ = memory;
bufferPool_.createBuffers(count);
+
+ /* Streams with internal memory usage do not need buffer mapping. */
+ if (memoryType_ == InternalMemory)
+ return;
+
+ /*
+ * Prepare for buffer mapping by adding all buffer memory entries to the
+ * cache.
+ */
+ bufferCache_.clear();
+ for (unsigned int i = 0; i < bufferPool_.count(); ++i)
+ bufferCache_.emplace_back(std::array<int, 3>{ -1, -1, -1 }, i);
}
/**