[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