[libcamera-devel] [PATCH v2 14/16] libcamera: stream: Map external buffers to indexes

Niklas Söderlund niklas.soderlund at ragnatech.se
Sun Jul 14 13:00:14 CEST 2019


Hi Jacopo and Laurent.

Thanks for your work.

On 2019-07-13 20:23:49 +0300, Laurent Pinchart wrote:
> From: Jacopo Mondi <jacopo at jmondi.org>
> 
> 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 at jmondi.org>
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

Reviewed-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>

> ---
>  include/libcamera/buffer.h |  2 +
>  include/libcamera/stream.h |  6 +++
>  src/libcamera/camera.cpp   | 20 +++++++-
>  src/libcamera/stream.cpp   | 96 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 123 insertions(+), 1 deletion(-)
> 
> diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> index fc5c7d4c41b6..7b657509ab5f 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 1883d9e9b9c5..2e619cdf0e89 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 db15fd46cd51..f2deb38da367 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 e6aa1b643a37..729d36b31712 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 ExternalMemory
>   * 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)
> @@ -521,6 +525,86 @@ std::unique_ptr<Buffer> Stream::createBuffer(const std::array<int, 3> &fds)
>   * \return The memory type used by the stream
>   */
>  
> +/**
> + * \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 count The number of buffers to create
> @@ -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);
>  }
>  
>  /**
> -- 
> Regards,
> 
> Laurent Pinchart
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

-- 
Regards,
Niklas Söderlund


More information about the libcamera-devel mailing list