[libcamera-devel] [RFC PATCH 1/8] libcamera: buffer: Create a MappedFrameBuffer
Kieran Bingham
kieran.bingham at ideasonboard.com
Wed Jul 29 16:09:59 CEST 2020
Hi Laurent,
On 24/07/2020 17:58, Laurent Pinchart wrote:
> Hi Kieran,
>
> Thank you for the patch.
>
> 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
--
Kieran
>> 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?
>
>> +
>> + 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);
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.
--
Kieran
>
>> +}
>> +
>> } /* namespace libcamera */
>
--
Regards
--
Kieran
More information about the libcamera-devel
mailing list