From patchwork Thu Jul 4 22:53:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1627 Return-Path: Received: from relay5-d.mail.gandi.net (relay5-d.mail.gandi.net [217.70.183.197]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6E3B560C2C for ; Fri, 5 Jul 2019 00:52:31 +0200 (CEST) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay5-d.mail.gandi.net (Postfix) with ESMTPSA id 060D11C0008; Thu, 4 Jul 2019 22:52:30 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 5 Jul 2019 00:53:31 +0200 Message-Id: <20190704225334.26170-7-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190704225334.26170-1-jacopo@jmondi.org> References: <20190704225334.26170-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 6/9] libcamera: stream: Add operation to map buffers X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 04 Jul 2019 22:52:31 -0000 Add and operation to map external buffers provided by applications in a Request to the Stream's internal buffers used by the pipeline handlers to interact with the video device. Signed-off-by: Jacopo Mondi --- include/libcamera/buffer.h | 1 + include/libcamera/stream.h | 6 ++ src/libcamera/stream.cpp | 116 +++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index 260a62e9e77e..d5d3dc90a096 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -59,6 +59,7 @@ private: friend class BufferPool; friend class PipelineHandler; friend class Request; + friend class Stream; friend class V4L2VideoDevice; void cancel(); diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index 796f1aff2602..74415062cbdd 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -85,6 +85,8 @@ public: const StreamConfiguration &configuration() const { return configuration_; } MemoryType memoryType() const { return memoryType_; } + Buffer *mapBuffer(Buffer *applicationBuffer); + protected: friend class Camera; @@ -94,6 +96,10 @@ protected: BufferPool bufferPool_; StreamConfiguration configuration_; MemoryType memoryType_; + +private: + std::vector mappableBuffers_; + std::map bufferMap_; }; } /* namespace libcamera */ diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp index 97e0f429c9fb..4585c0db77a4 100644 --- a/src/libcamera/stream.cpp +++ b/src/libcamera/stream.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include "log.h" /** @@ -470,6 +472,26 @@ void Stream::createBuffers(unsigned int count) return; bufferPool_.createBuffers(count); + + /* Streams with internal memory usage do not need buffer mapping. */ + if (memoryType_ == InternalMemory) + return; + + /* + * Prepare for buffer mapping by queuing all buffers from the internal + * pool. Each external buffer presented by application will be mapped + * on an internal one. + */ + mappableBuffers_.clear(); + for (Buffer &buffer : bufferPool_.buffers()) { + /* + * Reserve all planes to support mapping multiplanar buffers. + * \todo I would use VIDEO_MAX_PLANES but it's a V4L2 thing. + */ + buffer.planes().resize(3); + + mappableBuffers_.push_back(&buffer); + } } /** @@ -480,6 +502,100 @@ void Stream::destroyBuffers() bufferPool_.destroyBuffers(); } +/** + * \brief Map an application provided buffer to a stream buffer + * \param applicationBuffer The application provided buffer + * + * If a Stream has been configured to use application provided buffers a + * mapping between the external buffers and the internal ones, which are + * actually used to interface with the video device, is required. + * + * The most commonly used mechanism to perform zero-copy memory sharing + * on Linux-based system is dmabuf, which allows user-space applications to + * share buffers by exchanging dmabuf generated file descriptors. This + * operations assumes that all application provided buffers have each of their + * used memory planes exported as dmabuf file descriptor, to copy them in + * the buffer to be then queued on the video device by pipeline handlers. + * + * Perform mapping by maintaining a cache in a map associating the dmabuf file + * descriptor of the application provided buffer to one of the stream's internal + * buffers to provide pipeline handlers the buffer to use to interact with video + * devices. + * + * Once the buffer completes, the mapping should be reverted to return to + * the application the buffer it first provided here. + * + * \return The stream buffer which maps to the application provided buffer + */ +Buffer *Stream::mapBuffer(Buffer *applicationBuffer) +{ + unsigned int key = applicationBuffer->planes()[0].dmabuf(); + + /* + * Buffer hit: the application buffer has already been mapped, return + * the assigned stream buffer + */ + auto mapped = bufferMap_.find(key); + if (mapped != bufferMap_.end()) { + Buffer *buffer = mapped->second; + + /* + * Keep the mappableBuffers_ vector warm: move the hit buffer to + * the vector end as on a buffer miss buffers are below evicted + * from the vector head. + */ + auto it = std::find(mappableBuffers_.begin(), + mappableBuffers_.end(), buffer); + + ASSERT(it != mappableBuffers_.end()); + std::rotate(it, it + 1, mappableBuffers_.end()); + + return buffer; + } + + /* + * Buffer miss: assign to the application buffer the stream buffer + * at mappableBuffers_ begin, then move it to the end. + */ + Buffer *buffer = *(mappableBuffers_.begin()); + std::rotate(mappableBuffers_.begin(), mappableBuffers_.begin() + 1, + mappableBuffers_.end()); + + + /* Remove the [key, buffer] entry buffer from the buffer map */ + auto deadBuf = std::find_if(bufferMap_.begin(), bufferMap_.end(), + [&](std::pair &map) { + return bufferMap_[map.first] == buffer; + }); + if (deadBuf != bufferMap_.end()) + bufferMap_.erase(deadBuf); + + /* + * Assign the buffer by copying the dmabuf file descriptors from the + * application provided buffer. + */ + for (unsigned int i = 0; i < applicationBuffer->planes().size(); ++i) { + int fd = applicationBuffer->planes()[i].dmabuf(); + + /* + * The ARC camera stack seems to report more planes that the + * ones it actually uses. + */ + if (fd < 0) + break; + + buffer->planes()[i].setDmabuf(fd, 0); + } + + /* Pipeline handlers use request_ at buffer completion time. */ + buffer->request_ = applicationBuffer->request(); + + /* And finally, store the mapping for later re-use and return it. */ + bufferMap_[key] = buffer; + + return buffer; +} + /** * \var Stream::bufferPool_ * \brief The pool of buffers associated with the stream