[{"id":3286,"web_url":"https://patchwork.libcamera.org/comment/3286/","msgid":"<20191228005234.GD5747@pendragon.ideasonboard.com>","date":"2019-12-28T00:52:34","subject":"Re: [libcamera-devel] [PATCH v3 6/6] v4l2: v4l2-compat: add buffer\n\tstate tracking to V4L2CameraProxy","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Mon, Dec 23, 2019 at 01:26:20AM -0600, Paul Elder wrote:\n> Add a way for V4L2CameraProxy to cache the state of all the completed\n> buffers as v4l2_buffers. This reduces the number of cross-thread calls,\n> since the newly added V4L2CameraProxy::updateBuffers(), which goes\n> through V4L2Camera::completedBuffers(), does not need to be called\n> across the thread boundary.\n> \n> Also move the v4l2_buffer flag-setting logic to V4L2CameraProxy.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> New in v3\n\nAny reason not to squash this with 5/6 ?\n\n> ---\n>  src/v4l2/v4l2_camera.cpp       | 36 ++++++---------\n>  src/v4l2/v4l2_camera.h         |  7 ++-\n>  src/v4l2/v4l2_camera_proxy.cpp | 80 +++++++++++++++++++++++++++-------\n>  src/v4l2/v4l2_camera_proxy.h   |  3 ++\n>  4 files changed, 84 insertions(+), 42 deletions(-)\n> \n> diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp\n> index 2d33be9f..403e24f6 100644\n> --- a/src/v4l2/v4l2_camera.cpp\n> +++ b/src/v4l2/v4l2_camera.cpp\n> @@ -70,6 +70,19 @@ void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig)\n>  \t*streamConfig = config_->at(0);\n>  }\n>  \n> +std::vector<FrameMetadata> V4L2Camera::completedBuffers()\n> +{\n> +\tstd::vector<FrameMetadata> v;\n> +\n> +\tbufferLock_.lock();\n> +\tfor (std::unique_ptr<FrameMetadata> &fmd : completedBuffers_)\n> +\t\tv.push_back(*fmd.get());\n> +\tcompletedBuffers_.clear();\n> +\tbufferLock_.unlock();\n> +\n> +\treturn v;\n> +}\n> +\n>  void V4L2Camera::requestComplete(Request *request)\n>  {\n>  \tif (request->status() == Request::RequestCancelled)\n> @@ -80,7 +93,7 @@ void V4L2Camera::requestComplete(Request *request)\n>  \tBuffer *buffer = request->buffers().begin()->second;\n>  \tstd::unique_ptr<FrameMetadata> fmd =\n>  \t\tutils::make_unique<FrameMetadata>(buffer);\n> -\tcompletedBuffers_.push(std::move(fmd));\n> +\tcompletedBuffers_.push_back(std::move(fmd));\n>  \tbufferLock_.unlock();\n>  \n>  \tbufferSema_.release();\n> @@ -225,24 +238,3 @@ void V4L2Camera::qbuf(int *ret, unsigned int index)\n>  \n>  \t*ret = 0;\n>  }\n> -\n> -int V4L2Camera::dqbuf(struct v4l2_buffer *arg, bool nonblock)\n> -{\n> -\tif (nonblock && !bufferSema_.tryAcquire())\n> -\t\treturn -EAGAIN;\n> -\telse\n> -\t\tbufferSema_.acquire();\n> -\n> -\tbufferLock_.lock();\n> -\tFrameMetadata *fmd = completedBuffers_.front().get();\n> -\tcompletedBuffers_.pop();\n> -\tbufferLock_.unlock();\n> -\n> -\targ->bytesused = fmd->bytesused();\n> -\targ->field = V4L2_FIELD_NONE;\n> -\targ->timestamp.tv_sec = fmd->timestamp() / 1000000000;\n> -\targ->timestamp.tv_usec = fmd->timestamp() % 1000000;\n> -\targ->sequence = fmd->sequence();\n> -\n> -\treturn 0;\n> -}\n> diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\n> index 13418b6b..43ab8d02 100644\n> --- a/src/v4l2/v4l2_camera.h\n> +++ b/src/v4l2/v4l2_camera.h\n> @@ -11,7 +11,6 @@\n>  #include <deque>\n>  #include <linux/videodev2.h>\n>  #include <mutex>\n> -#include <queue>\n>  \n>  #include <libcamera/buffer.h>\n>  #include <libcamera/camera.h>\n> @@ -51,6 +50,7 @@ public:\n>  \tvoid open(int *ret);\n>  \tvoid close(int *ret);\n>  \tvoid getStreamConfig(StreamConfiguration *streamConfig);\n> +\tstd::vector<FrameMetadata> completedBuffers();\n>  \n>  \tvoid mmap(void **ret, unsigned int index);\n>  \n> @@ -63,8 +63,8 @@ public:\n>  \tvoid streamOff(int *ret);\n>  \n>  \tvoid qbuf(int *ret, unsigned int index);\n> -\tint dqbuf(struct v4l2_buffer *arg, bool nonblock);\n>  \n> +\tSemaphore bufferSema_;\n>  private:\n>  \tvoid requestComplete(Request *request);\n>  \n> @@ -74,11 +74,10 @@ private:\n>  \tunsigned int bufferCount_;\n>  \tbool isRunning_;\n>  \n> -\tSemaphore bufferSema_;\n>  \tstd::mutex bufferLock_;\n>  \n>  \tstd::deque<std::unique_ptr<Request>> pendingRequests_;\n> -\tstd::queue<std::unique_ptr<FrameMetadata>> completedBuffers_;\n> +\tstd::deque<std::unique_ptr<FrameMetadata>> completedBuffers_;\n>  };\n>  \n>  #endif /* __V4L2_CAMERA_H__ */\n> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> index b0acd477..4e303500 100644\n> --- a/src/v4l2/v4l2_camera_proxy.cpp\n> +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> @@ -101,6 +101,9 @@ void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags,\n>  \tvoid *val;\n>  \tvcam_->invokeMethod(&V4L2Camera::mmap, ConnectionTypeBlocking,\n>  \t\t\t    &val, index);\n> +\n> +\tbuffers_[index].flags |= V4L2_BUF_FLAG_MAPPED;\n\nThat's half of the job, you need to clear it on unmap() :-)\n\n> +\n>  \treturn val;\n>  }\n>  \n> @@ -173,6 +176,35 @@ void V4L2CameraProxy::querycap(std::shared_ptr<Camera> camera)\n>  \tmemset(capabilities_.reserved, 0, sizeof(capabilities_.reserved));\n>  }\n>  \n> +void V4L2CameraProxy::updateBuffers()\n> +{\n> +\tstd::vector<FrameMetadata> completedBuffers = vcam_->completedBuffers();\n> +\tfor (FrameMetadata &fmd : completedBuffers) {\n> +\t\t/* \\todo is this index valid if the buffer status != success? */\n\nWe'll have to rework this once Niklas' buffer rework lands. For now it\nshould be OK (but let's keep the \\todo as a reminder).\n\n> +\t\tstruct v4l2_buffer &buf = buffers_[fmd.index()];\n> +\n> +\t\tswitch (fmd.status()) {\n> +\t\tcase Buffer::Status::BufferSuccess:\n> +\t\t\tbuf.index = fmd.index();\n> +\t\t\tbuf.bytesused = fmd.bytesused();\n> +\t\t\tbuf.field = V4L2_FIELD_NONE;\n> +\t\t\tbuf.timestamp.tv_sec = fmd.timestamp() / 1000000000;\n> +\t\t\tbuf.timestamp.tv_usec = fmd.timestamp() % 1000000;\n> +\t\t\tbuf.sequence = fmd.sequence();\n> +\n> +\t\t\tbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> +\t\t\tbuf.length = curV4L2Format_.fmt.pix.sizeimage;\n> +\t\t\tbuf.memory = V4L2_MEMORY_MMAP;\n> +\t\t\tbuf.m.offset = buf.index * curV4L2Format_.fmt.pix.sizeimage;\n> +\t\t\tbreak;\n> +\t\tcase Buffer::Status::BufferError:\n> +\t\t\tbuf.flags |= V4L2_BUF_FLAG_ERROR;\n> +\t\tdefault:\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +}\n> +\n>  int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)\n>  {\n>  \tLOG(V4L2Compat, Debug) << \"Servicing vidioc_querycap\";\n> @@ -344,13 +376,21 @@ int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg)\n>  \t    arg->index >= stream->buffers().size())\n>  \t\treturn -EINVAL;\n>  \n> -\tunsigned int index = arg->index;\n> -\tmemset(arg, 0, sizeof(*arg));\n> -\targ->index = index;\n> -\targ->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> -\targ->length = curV4L2Format_.fmt.pix.sizeimage;\n> -\targ->memory = V4L2_MEMORY_MMAP;\n> -\targ->m.offset = arg->index * curV4L2Format_.fmt.pix.sizeimage;\n> +\t/* \\todo make updateBuffers() get only one buffer? */\n\nNo need to, keeping everything up-to-date unconditionally isn't a bad\nidea.\n\n> +\tupdateBuffers();\n> +\n> +\tif (buffers_.size() <= arg->index) {\n> +\t\tstruct v4l2_buffer buf;\n> +\t\tbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> +\t\tbuf.length = curV4L2Format_.fmt.pix.sizeimage;\n> +\t\tbuf.memory = V4L2_MEMORY_MMAP;\n> +\t\tbuf.m.offset = arg->index * curV4L2Format_.fmt.pix.sizeimage;\n> +\n> +\t\tbuffers_.resize(arg->index + 1);\n> +\t\tbuffers_[arg->index] = buf;\n> +\t}\n\nDoesn't this belong to vidioc_reqbufs() ? I think you should only keep\nupdateBuffers() and the below memcpy() here.\n\n> +\n> +\tmemcpy(arg, &buffers_[arg->index], sizeof(*arg));\n>  \n>  \treturn 0;\n>  }\n> @@ -388,19 +428,23 @@ int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg)\n>  \t    !validateMemoryType(arg->memory))\n>  \t\treturn -EINVAL;\n>  \n> -\targ->index = currentBuf_;\n> -\tcurrentBuf_ = (currentBuf_ + 1) % bufferCount_;\n> +\tif (nonBlocking_ && !vcam_->bufferSema_.tryAcquire())\n> +\t\treturn -EAGAIN;\n> +\telse\n> +\t\tvcam_->bufferSema_.acquire();\n>  \n> -\tint ret = vcam_->dqbuf(arg, nonBlocking_);\n> -\tif (ret < 0)\n> -\t\treturn ret;\n> +\tupdateBuffers();\n>  \n> -\targ->flags &= ~V4L2_BUF_FLAG_QUEUED;\n> -\targ->flags |= V4L2_BUF_FLAG_DONE;\n> +\tmemcpy(arg, &buffers_[arg->index], sizeof(*arg));\n>  \n> -\targ->length = sizeimage_;\n> +\tstruct v4l2_buffer &buf = buffers_[arg->index];\n\nYou can move this above the memcpy() and use buf in the memcpy().\n\n> +\targ->index = currentBuf_;\n> +\tcurrentBuf_ = (currentBuf_ + 1) % bufferCount_;\n> +\tbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;\n> +\tbuf.flags |= V4L2_BUF_FLAG_DONE;\n\nThe DONE flag should be moved to updateBuffers().\n\n> +\tbuf.length = sizeimage_;\n\nAnd length too I think. But do we really need to update it ?\n\nThe memcpy() should be moved here, otherwise you'll return stale values\nfor the QUEUED flag.\n\n>  \n> -\treturn ret;\n> +\treturn 0;\n>  }\n>  \n>  int V4L2CameraProxy::vidioc_streamon(int *arg)\n> @@ -426,6 +470,10 @@ int V4L2CameraProxy::vidioc_streamoff(int *arg)\n>  \tint ret;\n>  \tvcam_->invokeMethod(&V4L2Camera::streamOff,\n>  \t\t\t    ConnectionTypeBlocking, &ret);\n> +\n> +\tfor (struct v4l2_buffer &buf : buffers_)\n> +\t\tbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;\n\nShould you also clear the DONE flag ?\n\n> +\n>  \treturn ret;\n>  }\n>  \n> diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h\n> index 51fdbe19..19688717 100644\n> --- a/src/v4l2/v4l2_camera_proxy.h\n> +++ b/src/v4l2/v4l2_camera_proxy.h\n> @@ -40,6 +40,7 @@ private:\n>  \tvoid setFmtFromConfig(StreamConfiguration &streamConfig);\n>  \tunsigned int calculateSizeImage(StreamConfiguration &streamConfig);\n>  \tvoid querycap(std::shared_ptr<Camera> camera);\n> +\tvoid updateBuffers();\n>  \n>  \tint vidioc_querycap(struct v4l2_capability *arg);\n>  \tint vidioc_enum_fmt(struct v4l2_fmtdesc *arg);\n> @@ -64,6 +65,8 @@ private:\n>  \tunsigned int currentBuf_;\n>  \tunsigned int sizeimage_;\n>  \n> +\tstd::vector<struct v4l2_buffer> buffers_;\n> +\n>  \tstd::unique_ptr<V4L2Camera> vcam_;\n>  };\n>","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 02CAA6046A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 28 Dec 2019 01:52:46 +0100 (CET)","from pendragon.ideasonboard.com\n\t(host-149-154-202-187.dynamic.voo.be [149.154.202.187])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 6C110DD;\n\tSat, 28 Dec 2019 01:52:46 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1577494366;\n\tbh=gK2CrZSpFOtw8mnYxMwyFjk90ji3+Rm80JY0tA2xzJE=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=cUsKOkfHysu68wXcHU4u0yz/9u5JpHROT05p6ynaPwiOyFFyEOY9wzbBjDr53/SQ3\n\tXxjlZ8FZqVYmOrQ3OcAPZmSlqGeBWUPWQt85AGU7NwBUA1jAntrUjsLrgqc4knmUbV\n\tjUXaCa91x4H3Imffi/hobs3ovvHQY6lcCau2N5Ik=","Date":"Sat, 28 Dec 2019 02:52:34 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20191228005234.GD5747@pendragon.ideasonboard.com>","References":"<20191223072620.13022-1-paul.elder@ideasonboard.com>\n\t<20191223072620.13022-7-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20191223072620.13022-7-paul.elder@ideasonboard.com>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v3 6/6] v4l2: v4l2-compat: add buffer\n\tstate tracking to V4L2CameraProxy","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sat, 28 Dec 2019 00:52:47 -0000"}}]