From patchwork Wed Oct 9 07:41:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Harvey Yang X-Patchwork-Id: 21557 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 1C62CC32DE for ; Wed, 9 Oct 2024 07:47:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C72A265372; Wed, 9 Oct 2024 09:47:02 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="Cemb5dEo"; dkim-atps=neutral Received: from mail-pl1-x629.google.com (mail-pl1-x629.google.com [IPv6:2607:f8b0:4864:20::629]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 06CD965372 for ; Wed, 9 Oct 2024 09:46:57 +0200 (CEST) Received: by mail-pl1-x629.google.com with SMTP id d9443c01a7336-20c593d6b1cso13713525ad.0 for ; Wed, 09 Oct 2024 00:46:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1728460015; x=1729064815; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=1jCHNJHRlUl6YhNHIyh2oLEZ9RXC+uN9lCYMqUib2tg=; b=Cemb5dEonj3IEN+YjMNCfHX/6XdFUsWBS1/4rElENluycL9cxuscImQAi9CYTzW1h+ XORyGeuyx7fwGmoHo7pE9nMTzxCi48pujZdSZdZDGx5THUoEmKo8wc41wspZaZ17YdWr vBRABNNH8eYOjruK2ClLY5l2MTzXSni/W5s+s= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728460015; x=1729064815; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1jCHNJHRlUl6YhNHIyh2oLEZ9RXC+uN9lCYMqUib2tg=; b=YMidVPThteqatbB+rpEiYsyNViCxbTrc1QNNlx70/Nxs0PWBcdgsKPLpBYUQAJxqM2 tvckko+o8hFa7Sh+BbCY/2iXxU6WdtLHFN6Qch1xWx6h+K20R0eeDx+/NBLxdI9daLJz QUTfhIv43y1zfYQmmysjNbNmOXWlkqXU7uWKuVe8Dtz6aLvaiDOKLHFBLCfXxLWLIcue naXbW/Y1flIMH3gzn26PQcdibKELchDCtzs1DeiyM1cvuE2D1ICdaIsmUbXX1ts7bvyd n7/TkWvPLGidZW2XQDhLUWiR2EO7JF3Ik6KXouauR7c55kTUAEs2MMuntSnWlm6S8rl1 x+/A== X-Gm-Message-State: AOJu0YzxmtDRHPdFGIyJCK+hUuK1PdzVOpA+Xv327N6ASjVBov3ukWyP SdfriyvhWBRpJDbvgcJ/LDAe7ETrvrcKiiyJgLyK54IrnxkSzNVCWWGndsTDxCSrl0unppJxpl8 W/A== X-Google-Smtp-Source: AGHT+IEZf+5PvCmrrbWy3OGP1dJ6Kf3KUG1/Dh5z8gwbe1pQAGZeDHFQgp8JVnlK9gX4w6uFMFuXDQ== X-Received: by 2002:a17:902:f68a:b0:206:aac4:b844 with SMTP id d9443c01a7336-20c636dcbf4mr22252365ad.6.1728460015087; Wed, 09 Oct 2024 00:46:55 -0700 (PDT) Received: from chenghaoyang-low.c.googlers.com.com (199.211.81.34.bc.googleusercontent.com. [34.81.211.199]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2e2a57077c2sm944414a91.17.2024.10.09.00.46.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Oct 2024 00:46:54 -0700 (PDT) From: Harvey Yang To: libcamera-devel@lists.libcamera.org Cc: Harvey Yang , Han-Lin Chen Subject: [PATCH 5/5] libcamera: Add InfoFrame implememtation Date: Wed, 9 Oct 2024 07:41:23 +0000 Message-ID: <20241009074642.2965791-6-chenghaoyang@chromium.org> X-Mailer: git-send-email 2.47.0.rc0.187.ge670bccf7e-goog In-Reply-To: <20241009074642.2965791-1-chenghaoyang@chromium.org> References: <20241009074642.2965791-1-chenghaoyang@chromium.org> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" InfoFrame consists of an InfoFrame class which is used as an extension of FrameBuffer, with extra information of pixel format, size, and memory address if it's mmapped. InfoFramePool is introduced to handle InfoFrames more easily, which supports a pool implementation of InfoFrames, creates frames by DmaBufAllocator, and mmap/munmap all frames of the pool. Signed-off-by: Han-Lin Chen Co-developed-by: Harvey Yang Signed-off-by: Harvey Yang --- include/libcamera/internal/info_frame.h | 105 ++++++++ include/libcamera/internal/meson.build | 1 + src/libcamera/info_frame.cpp | 302 ++++++++++++++++++++++++ src/libcamera/meson.build | 1 + 4 files changed, 409 insertions(+) create mode 100644 include/libcamera/internal/info_frame.h create mode 100644 src/libcamera/info_frame.cpp diff --git a/include/libcamera/internal/info_frame.h b/include/libcamera/internal/info_frame.h new file mode 100644 index 000000000..29b445bb3 --- /dev/null +++ b/include/libcamera/internal/info_frame.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023, Google Inc. + * + * info_frame.h - InfoFrame and InfoFramePool + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "libcamera/internal/dma_buf_allocator.h" +#include "libcamera/internal/mailbox.h" +#include "libcamera/internal/pool.h" + +namespace libcamera { + +class InfoFrame +{ +public: + struct Plane { + uint8_t *address; + }; + + InfoFrame(); + InfoFrame(const PixelFormat &format, const Size &size, FrameBuffer *buffers, + unsigned int strideAlign = 1, unsigned int scanAlign = 1, + std::array planes = {}); + + uint8_t *address(unsigned int plane) const; + + PixelFormat format() const { return format_; } + Size size() const { return size_; } + FrameBuffer *buffer() const { return buffer_; } + unsigned int numPlanes() const { return buffer_ ? buffer_->planes().size() : 0; } + unsigned int strideAlign() const { return strideAlign_; } + unsigned int scanAlign() const { return scanAlign_; } + +private: + Size size_; + PixelFormat format_; + FrameBuffer *buffer_ = nullptr; + + unsigned int strideAlign_ = 1; + unsigned int scanAlign_ = 1; + + std::array planes_; +}; + +class InfoFramePool +{ +public: + struct MappedBufferInfo { + uint8_t *address = nullptr; + size_t dmabufLength = 0; + }; + + InfoFramePool(); + ~InfoFramePool(); + + int createBuffers(DmaBufAllocator *dmaHeap, const PixelFormat &format, + const Size &size, uint32_t count, + unsigned int strideAlign = 1, unsigned scanAlign = 1); + + void release(); + + void fetch(SharedMailBox &mailBox); + + int mmap(); + int munmap(); + + bool mapped() const { return 0 != mappedBuffers_.size(); } + + size_t size() { return pool_.size(); } + std::vector> &content() + { + return pool_.content(); + } + +private: + LIBCAMERA_DISABLE_COPY_AND_MOVE(InfoFramePool) + + void setBuffers(const PixelFormat &format, const Size &size, + std::vector> &buffers, + unsigned int align, unsigned int scanAlign); + + InfoFrame get(); + void put(InfoFrame &frameInfo); + + Size size_; + PixelFormat format_; + Pool> pool_; + unsigned int strideAlign_; + unsigned int scanAlign_; + + std::unordered_map mappedBuffers_; +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index b78e51d0b..8983bcd21 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -21,6 +21,7 @@ libcamera_internal_headers = files([ 'dma_buf_allocator.h', 'formats.h', 'framebuffer.h', + 'info_frame.h', 'ipa_data_serializer.h', 'ipa_manager.h', 'ipa_module.h', diff --git a/src/libcamera/info_frame.cpp b/src/libcamera/info_frame.cpp new file mode 100644 index 000000000..5407fadd9 --- /dev/null +++ b/src/libcamera/info_frame.cpp @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Google Inc. + * + * info_frame.cpp - InfoFrame and InfoFramePool + */ + +#include "libcamera/internal/info_frame.h" + +#include +#include + +#include "libcamera/internal/dma_buf_allocator.h" +#include "libcamera/internal/formats.h" +#include "libcamera/internal/pool.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(InfoFrame) + +/** + * \class InfoFrame + * \brief A wrapper class that consists of a FrameBuffer and extra information, + * including PixelFormat, size, and memory address if it's mmap'ed. + */ + +/** + * \class InfoFrame::Plane + * \brief Per-plane frame mmap'ed address + * + * Frames are stored in memory in one or multiple planes. The + * InfoFrame::Plane structure stores per-plane mmap'ed address. + */ + +/** + * \var InfoFrame::Plane::address + * \brief mmap'ed address of a plane + */ + +InfoFrame::InfoFrame() = default; + +/** + * \brief Create an InfoFrame instance + * \param[in] format PixelFormat of the frame + * \param[in] size Size of the frame + * \param[in] buffer Pointer to the buffer. InfoFrame doesn't take the ownership + * \param[in] strideAlign The stride alignment, in bytes (1 for default alignment) + * \param[in] scanAlign The scanline alignment, in bytes (1 for default alignment) + * \param[in] planes mmap'ed addresses of planes + */ +InfoFrame::InfoFrame(const PixelFormat &format, const Size &size, + FrameBuffer *buffer, unsigned int strideAlign, + unsigned int scanAlign, std::array planes) + : size_(size), format_(format), buffer_(buffer), + strideAlign_(strideAlign), scanAlign_(scanAlign), + planes_(planes) + +{ +} + +/** + * \brief Get the mmap'ed address of \a plane + * \param[in] plane The \a plane'th plane + * \return Return the mmap'ed address of the plane + */ +uint8_t *InfoFrame::address(unsigned int plane) const +{ + return (plane < numPlanes()) ? planes_[plane].address : nullptr; +} + +/** + * \fn InfoFrame::format() + * \return Return the PixelFormat of the frame + */ + +/** + * \fn InfoFrame::size() + * \return Return the size of the frame + */ + +/** + * \fn InfoFrame::buffer() + * \return Return the pointer to the buffer + */ + +/** + * \fn InfoFrame::numPlanes() + * \return Return the number of planes of the buffer + */ + +/** + * \fn InfoFrame::strideAlign() + * \return Return the stride alignment, in bytes (1 for default alignment) + */ + +/** + * \fn InfoFrame::scanAlign() + * \return Return the scanline alignment, in bytes (1 for default alignment) + */ + +/** + * \class InfoFramePool + * \brief A buffer pool that allows users to allocate and request InfoFrame + * buffers from DmaBufAllocator + */ + +/** + * \struct InfoFramePool::MappedBufferInfo + * \brief Contains the information of the mmap'ed buffers, including address + * and length + */ + +/** + * \var InfoFramePool::MappedBufferInfo::address + * \brief mmap'ed address of a buffer + */ + +/** + * \var InfoFramePool::MappedBufferInfo::dmabufLength + * \brief Length of the DMA buffer + */ + +InfoFramePool::InfoFramePool() = default; + +InfoFramePool::~InfoFramePool() +{ + release(); +} + +/** + * \brief Create DMA buffers + * \param[in] dmaHeap The DmaBufAllocator to allocate buffers from + * \param[in] format PixelFormat of each frame + * \param[in] size Size of each frame + * \param[in] count Number of frames to create + * \param[in] strideAlign The stride alignment, in bytes (1 for default alignment) + * \param[in] scanAlign The scanline alignment, in bytes (1 for default alignment) + * + * \return 0 on success or a negative error code otherwise + */ +int InfoFramePool::createBuffers(DmaBufAllocator *dmaHeap, + const PixelFormat &format, const Size &size, + unsigned int count, + unsigned int strideAlign, unsigned scanAlign) +{ + release(); + + const PixelFormatInfo &info = PixelFormatInfo::info(format); + uint32_t frameSize = info.frameSize(size, strideAlign, scanAlign); + + std::vector> buffers; + buffers.reserve(count); + for (unsigned int i = 0; i < count; i++) { + SharedFD fd(dmaHeap->alloc(("frame-" + std::to_string(i)).c_str(), + frameSize)); + if (!fd.isValid()) { + buffers.clear(); + return -EBUSY; + } + + uint32_t offset = 0; + std::vector planes; + + for (unsigned int j = 0; j < info.numPlanes(); j++) { + planes.emplace_back(FrameBuffer::Plane{ + fd, offset, + info.planeSize(size, j, strideAlign, scanAlign), + info.stride(size.width, j, strideAlign) }); + offset += planes.back().length; + } + + buffers.emplace_back(std::make_unique(planes)); + } + + setBuffers(format, size, buffers, strideAlign, scanAlign); + + return 0; +} + +/** + * \brief Release all allocated buffers. InfoFramePool can create another set + * of buffers again. + */ +void InfoFramePool::release() +{ + if (munmap()) + LOG(InfoFrame, Error) << "Failed to unmap mapped buffers"; + + pool_.release(); +} + +/** + * \brief Fetch an available InfoFrame from the pool + * \param[out] mailBox The shared pointer of MailBox to store the fetched + * InfoFrame + * + * When \a mailBox is reset, the recycler will return the InfoFrame back to the + * InfoFramePool. + */ +void InfoFramePool::fetch(SharedMailBox &mailBox) +{ + auto recycler = [this](InfoFrame &info) { + this->put(info); + }; + + mailBox->put(get(), recycler); +} + +/** + * \brief mmap all allocated buffers and set the addresses in `InfoFrame`s + */ +int InfoFramePool::mmap() +{ + if (!mappedBuffers_.empty()) + return 0; + + for (auto &buffer : pool_.content()) { + for (const FrameBuffer::Plane &plane : buffer->planes()) { + const int fd = plane.fd.get(); + if (mappedBuffers_.find(fd) == mappedBuffers_.end()) { + const size_t length = lseek(fd, 0, SEEK_END); + mappedBuffers_[fd] = MappedBufferInfo{ nullptr, length }; + } + } + } + + for (auto &[fd, info] : mappedBuffers_) { + void *address = ::mmap(nullptr, info.dmabufLength, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + info.address = static_cast(address); + } + return 0; +} + +/** + * \brief munmap all allocated buffers and reset addresses + */ +int InfoFramePool::munmap() +{ + if (mappedBuffers_.empty()) + return 0; + + for (auto &[_, info] : mappedBuffers_) { + ::munmap(info.address, info.dmabufLength); + info.address = nullptr; + } + + mappedBuffers_.clear(); + return 0; +} + +/** + * \fn InfoFramePool::mapped() + * \return True if mmap() was called on the allocated buffers, false otherwise + */ + +/** + * \fn InfoFramePool::size() + * \return Return the number of `InfoFrame`s allocated + */ + +/** + * \fn InfoFramePool::content() + * \return Return frame buffers allocated in the pool + */ + +void InfoFramePool::setBuffers(const PixelFormat &format, const Size &size, + std::vector> &buffers, + unsigned int strideAlign, unsigned int scanAlign) +{ + if (munmap()) + LOG(InfoFrame, Error) << "Failed to unmap buffers"; + + size_ = size; + format_ = format; + pool_.setData(buffers); + strideAlign_ = strideAlign; + scanAlign_ = scanAlign; +} + +InfoFrame InfoFramePool::get() +{ + FrameBuffer *buffer = pool_.get(); + + std::array planes; + for (size_t i = 0; i < buffer->planes().size() && i < 3; i++) { + const int fd = buffer->planes()[i].fd.get(); + const unsigned int offset = buffer->planes()[i].offset; + + if (mappedBuffers_.count(fd)) + planes[i].address = mappedBuffers_[fd].address + offset; + } + + return InfoFrame(format_, size_, buffer, strideAlign_, scanAlign_, planes); +} + +void InfoFramePool::put(InfoFrame &info) +{ + pool_.put(info.buffer()); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index f4403687a..39483b458 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -29,6 +29,7 @@ libcamera_internal_sources = files([ 'device_enumerator_sysfs.cpp', 'dma_buf_allocator.cpp', 'formats.cpp', + 'info_frame.cpp', 'ipa_controls.cpp', 'ipa_data_serializer.cpp', 'ipa_interface.cpp',