[libcamera-devel] [PATCH v3 03/13] libcamera: buffer: Create a MappedBuffer
Kieran Bingham
kieran.bingham at ideasonboard.com
Wed Aug 5 13:30:47 CEST 2020
Hi Laurent,
On 05/08/2020 12:27, Kieran Bingham wrote:
> Hi Laurent,
>
> On 05/08/2020 01:29, Laurent Pinchart wrote:
>> Hi Kieran,
>>
>> Thank you for the patch.
>>
>> On Tue, Aug 04, 2020 at 10:47:01PM +0100, Kieran Bingham wrote:
>>> Provide a MappedFrameBuffer helper class which will map
>>> all of the Planes within a FrameBuffer and provide CPU addressable
>>> pointers for those planes.
>>>
>>> The MappedFrameBuffer implements the interface of the MappedBuffer
>>> allowing other buffer types to be constructed of the same form, with a
>>> common interface and cleanup.
>>>
>>> This allows MappedBuffer instances to be created from Camera3Buffer types.
>>>
>>> Mappings are removed upon destruction.
>>>
>>> Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
>>>
>>> ---
>>> v3
>>> - Documentation fixups
>>> ---
>>> include/libcamera/internal/buffer.h | 47 ++++++++
>>> src/libcamera/buffer.cpp | 162 +++++++++++++++++++++++++++-
>>> 2 files changed, 208 insertions(+), 1 deletion(-)
>>> create mode 100644 include/libcamera/internal/buffer.h
>>>
>>> diff --git a/include/libcamera/internal/buffer.h b/include/libcamera/internal/buffer.h
>>> new file mode 100644
>>> index 000000000000..e86a003cd8ba
>>> --- /dev/null
>>> +++ b/include/libcamera/internal/buffer.h
>>> @@ -0,0 +1,47 @@
>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>>> +/*
>>> + * Copyright (C) 2020, Google Inc.
>>> + *
>>> + * buffer.h - Internal buffer handling
>>> + */
>>> +#ifndef __LIBCAMERA_INTERNAL_BUFFER_H__
>>> +#define __LIBCAMERA_INTERNAL_BUFFER_H__
>>> +
>>> +#include <sys/mman.h>
>>> +#include <vector>
>>> +
>>> +#include <libcamera/buffer.h>
>>> +#include <libcamera/span.h>
>>> +
>>> +namespace libcamera {
>>> +
>>> +using MappedPlane = Span<uint8_t>;
>>> +
>>> +class MappedBuffer
>>> +{
>>> +public:
>>
>> How about moving MappedPlane here, and naming it Plane ?
>>
>> using Plane = Span<uint8_t>;
>>
>> This makes it clear that Plane is meant to be used with MappedBuffer.
>
> Defining it in the class namespace certainly makes sense. I was a bit
> weary of using the name Plane, as it could conflict with our other Plane
> types, but as it's scoped inside the MappedBuffer, I think that's OK,
> and after all - it is a Plane.
>
>
>>> + MappedBuffer();
>>
>> Do you want to allow construction of the base class, or only of derived
>> classes ? In the latter case, you should move this constructor to the
>> protected section below.
>
> Hrm, I thought I needed this public to support STL construction of
> vectors or emplace or something.
>
> Looks like it's working in protected, so I'll move it there.
>
>
>>> + ~MappedBuffer();
>>> +
>>> + MappedBuffer(MappedBuffer &&rhs);
>>> + MappedBuffer &operator=(MappedBuffer &&rhs);
>>
>> The parameter is customarily called other for the copy and move
>> constructors and assignment operators. rhs is usually used in
>> conjunction with lhs for non-member operators.
>
> Renamed.
>
>>
>>> +
>>> + bool isValid() const { return valid_; }
>>> + int error() const { return error_; }
>>> + const std::vector<MappedPlane> &maps() const { return maps_; }
>>> +
>>> +protected:
>>> + int error_;
>>> + bool valid_;
>>
>> Do you need both ? Could isValid() return error_ == 0 ?
>
>
> I can do that, I will initialise error_ to -ENOENT in
> MappedBuffer::MappedBuffer().
>
>
>
>>> + std::vector<MappedPlane> maps_;
>>> +};
>>> +
>>> +class MappedFrameBuffer : public MappedBuffer
>>> +{
>>> +public:
>>> + MappedFrameBuffer(const FrameBuffer *buffer, int flags);
>>> +};
>>> +
>>> +} /* namespace libcamera */
>>> +
>>> +#endif /* __LIBCAMERA_INTERNAL_BUFFER_H__ */
>>> diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
>>> index 8278f8a92af4..5f7dff60a48b 100644
>>> --- a/src/libcamera/buffer.cpp
>>> +++ b/src/libcamera/buffer.cpp
>>> @@ -6,6 +6,7 @@
>>> */
>>>
>>> #include <libcamera/buffer.h>
>>> +#include "libcamera/internal/buffer.h"
>>>
>>> #include <errno.h>
>>> #include <string.h>
>>> @@ -15,7 +16,8 @@
>>> #include "libcamera/internal/log.h"
>>>
>>> /**
>>> - * \file buffer.h
>>> + * \file libcamera/buffer.h
>>> + * \file libcamera/internal/buffer.h
>>> * \brief Buffer handling
>>> */
>>>
>>> @@ -290,4 +292,162 @@ int FrameBuffer::copyFrom(const FrameBuffer *src)
>>> return 0;
>>> }
>>>
>>> +/**
>>> + * \class MappedBuffer
>>> + * \brief Provide an interface to support managing memory mapped buffers
>>> + *
>>> + * The MappedBuffer interface provides access to a set of MappedPlanes which
>>> + * are available for access by a CPU.
>>
>> s/a CPU/the CPU/ ?
>>
>> You're clearly thinking as a kernel developer :-)
>
> Well, sometimes ;-) I mean 'the' cpu is 'a' cpu ;-)
>
> Also, I guess it depends on how you name the multiple cores/threads from
> userspace.
>
> /sys/bus/cpu/devices/cpuN implies that each available core is a 'CPU',
> and it would be accessible from each of those CPU's...
>
>
> Of course, Mapping on 'my' CPU wouldn't be available for access on
> 'your' CPU which is also 'a' CPU... so I think 'the CPU' is good.
>
>
>>> + *
>>> + * The MappedBuffer interface does not implement any valid constructor but
>>> + * defines the move operators and destructors for the derived implementations,
>>> + * which are able to construct according to their derived types and given
>>> + * flags.
>>
>> That's a bit of implementation details. Maybe "This class is not meant
>> to be constructed directly, and thus has no public default constructor.
>> Derived classes shall be used instead." ?
>>
>
>
> Replaced with:
>
>
> * The MappedBuffer interface provides access to a set of MappedPlanes which
> * are available for access by the CPU.
> *
> * The MappedBuffer is not meant to be constructed directly, but instead
> derived
> * classes should be used to implement the correct mapping of a source
> buffer.
> *
> * This allows treating CPU accessible memory through a generic interface
> * regardless of whether it originates from a libcamera FrameBuffer or other
> * source.
>
>
>>> + *
>>> + * This allows treating CPU accessible memory through a generic interface
>>> + * regardless of whether it originates from a libcamera FrameBuffer or other
>>> + * source.
>>> + */
>>> +
>>> +/**
>>> + * \brief Construct an empty MappedBuffer
>>> + *
>>> + * A default constructor is required to allow subclassing the MappedBuffer
>>> + * class. Construct an initialised, but invalid MappedBuffer.
>>
>> You can drop the first sentence, you're explaining C++ :-)
>
> Well someone's got to explain it to me ... it may as well be me ;-)
>
> (dropped).
>
>>> + */
>>> +MappedBuffer::MappedBuffer()
>>> + : error_(0), valid_(false)
>
>
> Actually, now that this is protected, all this really does is initialise
> the base class, and that's now only : error_(0).
>
> I've considered making this error_(-ENOENT), so it starts off invalid,
> but the pattern used in both existing constructors for this now set
> error_ = 0; and set the error if an error occurs...
>
> So I think this is better left as error_(0), and update the function doc
> to just:
>
> /**
> * \brief Construct an empty MappedBuffer
> *
> * Base class initialisation for MappedBuffer.
> */
>
>
>>> +{
>>> +}
>>> +
>>> +/**
>>> + * \brief Construct a MappedBuffer by taking the \a rhs mappings
>>
>> Construct the MappedBuffer with the contents of \a other using move
>> semantics
>>
>> (This is the usual way to document move constructors in
>> https://en.cppreference.com/, which we're mimicking in libcamera.)
>
> It's new to me that we are 'mimicking' en.cppreference.com.
> Maybe that needs to be documented somehow.
>
> I think I based my original text templates on the file_descriptor
> constructors you authored, which differ slightly:
>
> * \brief Move constructor, create a FileDescriptor by taking over \a other
>
> ...
>
>
>
>>> + * \param[in] rhs The other MappedBuffer
>>> + *
>>> + * Moving a MappedBuffer moves the mappings contained in the \a rhs to the new
>>> + * MappedBuffer and invalidates the \a rhs. No mappings are unmapped or
>>> + * destroyed in this process.
>>> + */
>>> +MappedBuffer::MappedBuffer(MappedBuffer &&rhs)
>>> +{
>>> + *this = std::move(rhs);
>>> +}
>>> +
>>> +/**
>>> + * \brief Move assingment operator, move a MappedBuffer by taking the \a rhs mappings
>>
>> s/assingment/assignement/
>>
>> But for the same reason as above,
>>
>> Replace the contents with those of \a other using move semantics
>
> Again, this was based upon text from file_descriptor:
>
> * \brief Move assignment operator, replace the wrapped file descriptor
> * by taking over \a other
>
> I think I like keeping the 'name of the constructor' to make it clear.
>
> It's not quickly clear to me looking at
> MappedBuffer::MappedBuffer(MappedBuffer &&rhs) 'which' type of
> constructor it is, so I think that's important.
>
>
>>> + * \param[in] rhs The other MappedBuffer
>>> + *
>>> + * Moving a MappedBuffer moves the mappings contained in the \a rhs to the new
>>
>> s/the \a rhs/\a other/
>> s/ new// (this is not a newly constructed instance)
>
> likewise here, the wording was based on text you already wrote.
>
> You wrote:
>
> * Moving a FileDescriptor moves the reference to the wrapped descriptor
> * owned by \a other to the new FileDescriptor.
>
>
> This is exhausting. Trying to emulate your text so you don't rewrite it,
> but then you rewrite it anyway ;-)
>
>
>
>>> + * MappedBuffer and invalidates the \a rhs. No mappings are unmapped or
>>
>> s/the \a rhs/\a other/
>
>
>
> Ok, so the new text for both of those is:
>
>
>
> /**
> * \brief Move constructor, construct the MappedBuffer with the contents
> of \a
> * other using move semantics
> * \param[in] other The other MappedBuffer
> *
> * Moving a MappedBuffer moves the mappings contained in the \a other to
> the new
> * MappedBuffer and invalidates the \a other.
> *
> * No mappings are unmapped or destroyed in this process.
> */
>
>
> /**
> * \brief Move assignment operator, replace the mappings with those of
> \a other
> * mappings
> * \param[in] other The other MappedBuffer
> *
> * Moving a MappedBuffer moves the mappings contained in the \a other to
> the new
> * MappedBuffer and invalidates the \a other.
> *
> * No mappings are unmapped or destroyed in this process.
> */
>
>
>
>>> + * destroyed in this process.
>>> + */
>>> +MappedBuffer &MappedBuffer::operator=(MappedBuffer &&rhs)
>>> +{
>>> + error_ = rhs.error_;
>>> + valid_ = rhs.valid_;
>>> + maps_ = std::move(rhs.maps_);
>>> + rhs.valid_ = false;
>>> + rhs.error_ = 0;
>>> +
>>> + return *this;
>>> +}
>>> +
>>> +MappedBuffer::~MappedBuffer()
>>> +{
>>> + for (MappedPlane map : maps_)
>>> + munmap(map.data(), map.size());
>>> +}
>>> +
>>> +/**
>>> + * \fn MappedBuffer::isValid()
>>> + * \brief Check if the MappedBuffer instance is valid
>>> + * \return True if the MappedBuffer has valid mappings, false otherwise
>>> + */
>>> +
>>> +/**
>>> + * \fn MappedBuffer::error()
>>> + * \brief Retrieve the map error status
>>> + *
>>> + * This function retrieves the error status from the MappedBuffer.
>>> + * The error status is a negative number as defined by errno.h. If
>>> + * no error occurred, this function returns 0.
>>> + *
>>> + * \return 0 on success or a negative error code otherwise
>>> + */
>>> +
>>> +/**
>>> + * \fn MappedBuffer::maps()
>>> + * \brief Retrieve the mapped planes
>>> + *
>>> + * This function retrieves the successfully mapped planes stored as a vector
>>> + * of Span<uint8_t> to provide access to the mapped memory.
>>> + *
>>> + * \return A vector of the mapped planes.
>>
>> s/.$//
>>
>
> Removed.
>
>>> + */
>>> +
>>> +/**
>>> + * \var MappedBuffer::valid_
>>> + * \brief Stores the status of the mapping
>>> + *
>>> + * MappedBuffer implementations shall set this to represent if the mapping
>>
>> s/implementations/derived classes/ ? Same below.
>>
>
> valid_ removed.
>
>
>>> + * was successfully completed without errors.
>>> + */
>>> +
>>> +/**
>>> + * \var MappedBuffer::error_
>>> + * \brief Stores the error value if present
>>> + *
>>> + * MappedBuffer implementations shall set this to a negative value as defined
>>> + * by errno.h if an error occured during the mapping process
>>> + */
>>> +
>>> +/**
>>> + * \var MappedBuffer::maps_
>>> + * \brief Stores the internal
>>> + *
>>> + * MappedBuffer implementations shall store the mappings they create in this
>>> + * vector which is parsed during destruct to unmap any memory mappings which
>>> + * completed successfully.
>>> + */
>>> +
>>> +/**
>>> + * \class MappedFrameBuffer
>>> + * \brief Maps a FrameBuffer using the MappedBuffer interface
>>
>> s/Maps/Map/
>>
>>> + *
>>> + * The MappedFrameBuffer interface maps a FrameBuffer instance
>>
>> s/$/./
>>
>> Or maybe drop the sentence as it duplicates the brief.
>>
>>> + *
>>> + * The MappedBuffer interface does not implement any constructor but defines
>>> + * the move operators and destructors for the derived implementations, which
>>> + * are able to construct according to their derived types and given flags.
>>
>> Is this a leftover from a copy & paste ? I think ou can drop this
>> paragraph.
>
>
> Dropping it leaves just the brief. Maybe that's fine. But it seems a bit
> brief ;-)
>
> I thought the point of this section is to expand on the purpose of the
> class as an overview, so I thought that paragraph was relevant as a summary.
Aha, I just realised this is \class MappedFrameBuffer not \class
MappedBuffer ;-)
So given the lightweight intention of that class, indeed a brief is
probably sufficient.
>
>>> + */
>>> +
>>> +/**
>>> + * \brief Map all planes of a FrameBuffer
>>> + * \param[in] buffer FrameBuffer to be mapped
>>> + * \param[in] flags Protection flags to apply to map
>>> + *
>>> + * Construct an object to map a frame buffer for CPU access.
>>> + * The flags are passed directly to mmap and should be either PROT_READ,
>>> + * PROT_WRITE, or a bitwise-or combination of both.
>>> + */
>>> +MappedFrameBuffer::MappedFrameBuffer(const FrameBuffer *buffer, int flags)
>>> +{
>>> + maps_.reserve(buffer->planes().size());
>>> +
>>> + for (const FrameBuffer::Plane &plane : buffer->planes()) {
>>> + void *address = mmap(nullptr, plane.length, flags,
>>> + MAP_SHARED, plane.fd.fd(), 0);
>>> +
>>
>> You can drop the blank line.
>
> I liked that one, but dropped.
>
>
>>
>>> + if (address == MAP_FAILED) {
>>> + error_ = -errno;
>>> + LOG(Buffer, Error) << "Failed to mmap plane";
>>> + break;
>>> + }
>>> +
>>> + maps_.emplace_back(static_cast<uint8_t *>(address), plane.length);
>>> + }
>>> +
>>> + valid_ = buffer->planes().size() == maps_.size();
>>> +}
>>> +
>>> } /* namespace libcamera */
>>
>
--
Regards
--
Kieran
More information about the libcamera-devel
mailing list