[libcamera-devel] [RFC PATCH 1/8] libcamera: buffer: Create a MappedFrameBuffer

Laurent Pinchart laurent.pinchart at ideasonboard.com
Wed Jul 29 16:18:42 CEST 2020


Hi Kieran,

On Wed, Jul 29, 2020 at 03:09:59PM +0100, Kieran Bingham wrote:
> On 24/07/2020 17:58, Laurent Pinchart wrote:
> > On Mon, Jul 20, 2020 at 11:42:25PM +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.
> >>
> >> Mappings are removed upon destruction.
> >>
> >> Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
> >> ---
> >>
> >> This introduces an automatically mapping/unmapping MappedFrameBuffer
> >> object, with a move constructor (essential to prevent un-desirable
> >> unmapping).
> >>
> >>  include/libcamera/buffer.h | 23 ++++++++++++++++++++
> > 
> > Should this be an internal helper ? The main foreseen flow is for
> > applications to create frame buffers from dmabuf fds they recently
> > outside of libcamera, so I would expect them to handle memory mapping.
> > I'd thus rather start with an internal helper, which we could graduate
> > to a public API later if needed/desired.
> 
> I saw it as beneficial to be in the public-api, because FrameBuffer is
> in the public API.
> 
> If an application has created a FrameBuffer of their own, by the
> definition of it, it can be used to construct a MappedFrameBuffer.
> 
> We've already had a support-request in the past where someone had not
> mapped the FrameBuffer because it wasn't obvious, so I thought providing
> common helpers to map the Common FrameBuffer object was useful.
> 
> I can move it to private API all the same though.
> 
> As you say, it could be made public later.
> 
> This implies that we would create
> 
> 	include/libcamera/internal/buffer.h
> as well as
> 	include/libcamera/buffer.h
> 
> Where both would be implemented in:
> 
> 	src/libcamera/buffer.cpp
> 
> Is that acceptable? or should this become:
> 
> 	include/libcamera/internal/mapped_buffer.h
> 	src/libcamera/mapped_buffer.cpp

Both options are fine with me.

> >>  src/libcamera/buffer.cpp   | 43 ++++++++++++++++++++++++++++++++++++++
> >>  2 files changed, 66 insertions(+)
> >>
> >> diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> >> index 6bb2e4f8558f..881d736da2db 100644
> >> --- a/include/libcamera/buffer.h
> >> +++ b/include/libcamera/buffer.h
> >> @@ -71,6 +71,29 @@ private:
> >>  	unsigned int cookie_;
> >>  };
> >>  
> >> +class MappedFrameBuffer {
> >> +public:
> >> +	MappedFrameBuffer(const FrameBuffer *buffer, int flags);
> >> +	~MappedFrameBuffer();
> >> +
> >> +	/* Move constructor only, copying is not permitted. */
> > 
> > We usually don't put comments in the header files. This should go to the
> > \class documentation block below.
> > 
> >> +	MappedFrameBuffer(MappedFrameBuffer &&rhs);
> > 
> > Do you actually need this ? If it's only for the purpose of preventing
> > copy-construction, you can simply write
> 
> It was to self implement the Move constructor (and disable the copy
> constructors yes).
> 
> I think I had originally already put in the update to the rhs, which is
> why I implemented it rather than using the default, but yet that change
> didn't seem to make it into this patch ... :S
> 
> > 	MappedFrameBuffer(const MappedFrameBuffer &other) = delete;
> > 	MappedFrameBuffer operator=(const MappedFrameBuffer &other) = delete;
> > 
> > The C++ rules governing implicitly-defined and defaulted copy and move
> > constructors and assignment operators are not the easiest to follow.
> > It's usually best to be explicit. If you need move semantics,
> > 
> > 	MappedFrameBuffer(const MappedFrameBuffer &other) = delete;
> > 	MappedFrameBuffer operator=(const MappedFrameBuffer &other) = delete;
> > 
> > 	MappedFrameBuffer(MappedFrameBuffer &&other);
> > 	MappedFrameBuffer operator=(MappedFrameBuffer &&other);
> > 
> > (either neither or both of the move constructor and the move assignment
> > operator should be defined) otherwise you can leave the latter two out.
> > 
> >> +
> >> +	struct MappedPlane {
> >> +		void *address;
> >> +		size_t length;
> >> +	};
> > 
> > Would it make sense to use Span<uint8_t> to replace MappedPlane ?
> 
> Aha, I like that.
> 
> I like the type naming though, so should it be
> 
> using MappedPlane = Span<uint8_t>;
> 
> Or would you prefer to use the Span directly?

I don't mind much. Aliases require looking up what they correspond to,
but it can also clarify the code as the name better matches the purpose.
Up to you.

> >> +
> >> +	bool isValid() const { return valid_; }
> >> +	int error() const { return error_; }
> >> +	const std::vector<MappedPlane> &maps() const { return maps_; }
> >> +
> >> +private:
> >> +	int error_;
> >> +	bool valid_;
> >> +	std::vector<MappedPlane> maps_;
> >> +};
> >> +
> >>  } /* namespace libcamera */
> >>  
> >>  #endif /* __LIBCAMERA_BUFFER_H__ */
> >> diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> >> index 1a1d4bac7aed..28b43d937f57 100644
> >> --- a/src/libcamera/buffer.cpp
> >> +++ b/src/libcamera/buffer.cpp
> >> @@ -290,4 +290,47 @@ int FrameBuffer::copyFrom(const FrameBuffer *src)
> >>  	return 0;
> >>  }
> > 
> > Missing
> > 
> > /**
> >  * \class MappedFrameBuffer
> >  * ...
> >  */
> 
> Sure.
> 
> >> +/**
> >> + * \brief Map all planes of a FrameBuffer
> > 
> > As this is a constructor, I think it should be documented as "Construct
> > ..." to follow the usual pattern.
> > 
> >> + * \param[in] src Buffer to be mapped
> > 
> > s/src/buffer/
> > 
> >> + * \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)
> >> +	: error_(0)
> >> +{
> >> +	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);
> >> +
> >> +		if (address == MAP_FAILED) {
> >> +			error_ = -errno;
> >> +			LOG(Buffer, Error) << "Failed to mmap plane";
> >> +			continue;
> > 
> > Shouldn't you break ?
> 
> It will still fail the valid_ conditional below, but yes - I think break
> would be better.
> 
> >> +		}
> >> +
> >> +		maps_.push_back({address, plane.length});
> >> +	}
> >> +
> >> +	valid_ = buffer->planes().size() == maps_.size();
> >> +}
> >> +
> >> +MappedFrameBuffer::~MappedFrameBuffer()
> >> +{
> >> +	for (MappedPlane map : maps_)
> > 
> > 	for (MappedPlane &map : maps_)
> 
> Good spot.
> 
> >> +		munmap(map.address, map.length);
> >> +}
> >> +
> >> +MappedFrameBuffer::MappedFrameBuffer(MappedFrameBuffer &&rhs)
> >> +{
> >> +	error_ = rhs.error_;
> >> +	valid_ = rhs.valid_;
> >> +	maps_ = std::move(rhs.maps_);
> > 
> > This is the default move constructor, so you could simply write
> > 
> > 	MappedFrameBuffer(MappedFrameBuffer &&other) = default;
> > 
> > in the class definition. However, I think you should keep this, and add
> > 
> > 	rhs.error_ = 0;
> > 	rhs.valid_ = false;
> > 
> > Note that you can implement the move constructor in terms of the move
> > assignment operator:
> > 
> > 	*this = std::move(other);
> 
> Ok, so I think we need to keep this and have it as:
> 
> MappedFrameBuffer::MappedFrameBuffer(MappedFrameBuffer &&rhs)
> {
> 	*this = std::move(other);
>  	rhs.error_ = 0;
>  	rhs.valid_ = false;
> }
> 
> MappedFrameBuffer operator=(MappedFrameBuffer &&rhs)
> {
> 	*this = std::move(other);

Won't this cause a recursive call to the move assignement operator ?

>  	rhs.error_ = 0;
>  	rhs.valid_ = false;
> }
> 
> I was sure I already had the resetting of rhs, but I guess I dropped it
> somewhere.
> 
> It's a shame to have to duplicate for the assignment operator, but I
> don't think that's too bad.
> 
> >> +}
> >> +
> >>  } /* namespace libcamera */

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list