[libcamera-devel] [PATCH v3 03/13] libcamera: buffer: Create a MappedBuffer
Kieran Bingham
kieran.bingham at ideasonboard.com
Wed Aug 5 13:27:01 CEST 2020
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.
>> + */
>> +
>> +/**
>> + * \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