From patchwork Thu Mar 5 20:38:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2965 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B6075627BA for ; Thu, 5 Mar 2020 21:38:20 +0100 (CET) X-Halon-ID: 3fc7466c-5f21-11ea-9f85-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 3fc7466c-5f21-11ea-9f85-005056917a89; Thu, 05 Mar 2020 21:38:19 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 5 Mar 2020 21:38:02 +0100 Message-Id: <20200305203808.536050-2-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> References: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 1/7] test: camera: buffer_import: Fix error messages X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Mar 2020 20:38:20 -0000 The error message for when a device name can't be resolved to a video device is wrong and applies to the next operation below it. Move it to its correct location and add a new error message to highlight the resolution failure. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- test/camera/buffer_import.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp index ab6e74bd1671e6f5..6997ea78c9f608c9 100644 --- a/test/camera/buffer_import.cpp +++ b/test/camera/buffer_import.cpp @@ -74,13 +74,16 @@ public: video_ = V4L2VideoDevice::fromEntityName(media_.get(), videoDeviceName); if (!video_) { + std::cout << "Failed to get video device from entity " + << videoDeviceName << std::endl; + return TestFail; + } + + if (video_->open()) { std::cout << "Unable to open " << videoDeviceName << std::endl; return TestFail; } - if (video_->open()) - return TestFail; - /* Configure the format. */ V4L2DeviceFormat format; if (video_->getFormat(&format)) { From patchwork Thu Mar 5 20:38:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2966 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8C947628A7 for ; Thu, 5 Mar 2020 21:38:21 +0100 (CET) X-Halon-ID: 40195de4-5f21-11ea-9f85-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 40195de4-5f21-11ea-9f85-005056917a89; Thu, 05 Mar 2020 21:38:20 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 5 Mar 2020 21:38:03 +0100 Message-Id: <20200305203808.536050-3-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> References: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 2/7] test: Extract BufferSource class out of camera tests to libtest X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Mar 2020 20:38:22 -0000 The BufferSource class can be used by other tests other then the camera buffer importer test, move it to libtest. The only changes to BufferSource is for it to be allowed to be split in a header and source file. This change makes it necessary for libtest to have access to internal libcamera headers. As the internal headers already are accessible to all test cases this does not increase the exposure of libcamera internals to the test cases. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- test/camera/buffer_import.cpp | 95 +------------------------------- test/libtest/buffer_source.cpp | 98 ++++++++++++++++++++++++++++++++++ test/libtest/buffer_source.h | 32 +++++++++++ test/libtest/meson.build | 11 ++-- 4 files changed, 138 insertions(+), 98 deletions(-) create mode 100644 test/libtest/buffer_source.cpp create mode 100644 test/libtest/buffer_source.h diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp index 6997ea78c9f608c9..3f392cdc0732941f 100644 --- a/test/camera/buffer_import.cpp +++ b/test/camera/buffer_import.cpp @@ -16,6 +16,7 @@ #include "media_device.h" #include "v4l2_videodevice.h" +#include "buffer_source.h" #include "camera_test.h" #include "test.h" @@ -23,100 +24,6 @@ using namespace libcamera; namespace { -/* A provider of external buffers, suitable for import by a Camera. */ -class BufferSource -{ -public: - BufferSource() - : video_(nullptr) - { - } - - ~BufferSource() - { - if (video_) { - video_->releaseBuffers(); - video_->close(); - } - - delete video_; - video_ = nullptr; - - if (media_) - media_->release(); - } - - int allocate(const StreamConfiguration &config) - { - /* Locate and open the video device. */ - std::string videoDeviceName = "vivid-000-vid-out"; - - std::unique_ptr enumerator = - DeviceEnumerator::create(); - if (!enumerator) { - std::cout << "Failed to create device enumerator" << std::endl; - return TestFail; - } - - if (enumerator->enumerate()) { - std::cout << "Failed to enumerate media devices" << std::endl; - return TestFail; - } - - DeviceMatch dm("vivid"); - dm.add(videoDeviceName); - - media_ = enumerator->search(dm); - if (!media_) { - std::cout << "No vivid output device available" << std::endl; - return TestSkip; - } - - video_ = V4L2VideoDevice::fromEntityName(media_.get(), videoDeviceName); - if (!video_) { - std::cout << "Failed to get video device from entity " - << videoDeviceName << std::endl; - return TestFail; - } - - if (video_->open()) { - std::cout << "Unable to open " << videoDeviceName << std::endl; - return TestFail; - } - - /* Configure the format. */ - V4L2DeviceFormat format; - if (video_->getFormat(&format)) { - std::cout << "Failed to get format on output device" << std::endl; - return TestFail; - } - - format.size = config.size; - format.fourcc = V4L2VideoDevice::toV4L2Fourcc(config.pixelFormat, false); - if (video_->setFormat(&format)) { - std::cout << "Failed to set format on output device" << std::endl; - return TestFail; - } - - if (video_->exportBuffers(config.bufferCount, &buffers_) < 0) { - std::cout << "Failed to export buffers" << std::endl; - return TestFail; - } - - return TestPass; - } - - const std::vector> &buffers() - { - return buffers_; - } - -private: - std::shared_ptr media_; - V4L2VideoDevice *video_; - std::vector> buffers_; -}; - class BufferImportTest : public CameraTest, public Test { public: diff --git a/test/libtest/buffer_source.cpp b/test/libtest/buffer_source.cpp new file mode 100644 index 0000000000000000..066049d342a491f0 --- /dev/null +++ b/test/libtest/buffer_source.cpp @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * A provider of external buffers, suitable for use in tests. + */ + +#include "buffer_source.h" + +#include + +#include "device_enumerator.h" + +#include "test.h" + +BufferSource::BufferSource() + : video_(nullptr) +{ +} + +BufferSource::~BufferSource() +{ + if (video_) { + video_->releaseBuffers(); + video_->close(); + } + + delete video_; + video_ = nullptr; + + if (media_) + media_->release(); +} + +int BufferSource::allocate(const StreamConfiguration &config) +{ + /* Locate and open the video device. */ + std::string videoDeviceName = "vivid-000-vid-out"; + + std::unique_ptr enumerator = + DeviceEnumerator::create(); + if (!enumerator) { + std::cout << "Failed to create device enumerator" << std::endl; + return TestFail; + } + + if (enumerator->enumerate()) { + std::cout << "Failed to enumerate media devices" << std::endl; + return TestFail; + } + + DeviceMatch dm("vivid"); + dm.add(videoDeviceName); + + media_ = enumerator->search(dm); + if (!media_) { + std::cout << "No vivid output device available" << std::endl; + return TestSkip; + } + + video_ = V4L2VideoDevice::fromEntityName(media_.get(), videoDeviceName); + if (!video_) { + std::cout << "Failed to get video device from entity " + << videoDeviceName << std::endl; + return TestFail; + } + + if (video_->open()) { + std::cout << "Unable to open " << videoDeviceName << std::endl; + return TestFail; + } + + /* Configure the format. */ + V4L2DeviceFormat format; + if (video_->getFormat(&format)) { + std::cout << "Failed to get format on output device" << std::endl; + return TestFail; + } + + format.size = config.size; + format.fourcc = V4L2VideoDevice::toV4L2Fourcc(config.pixelFormat, false); + if (video_->setFormat(&format)) { + std::cout << "Failed to set format on output device" << std::endl; + return TestFail; + } + + if (video_->exportBuffers(config.bufferCount, &buffers_) < 0) { + std::cout << "Failed to export buffers" << std::endl; + return TestFail; + } + + return TestPass; +} + +const std::vector> &BufferSource::buffers() +{ + return buffers_; +} diff --git a/test/libtest/buffer_source.h b/test/libtest/buffer_source.h new file mode 100644 index 0000000000000000..2d8fc5acf6d78771 --- /dev/null +++ b/test/libtest/buffer_source.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * buffer_source.h - libcamera camera test helper to create FrameBuffers + */ +#ifndef __LIBCAMERA_BUFFER_SOURCE_TEST_H__ +#define __LIBCAMERA_BUFFER_SOURCE_TEST_H__ + +#include + +#include "media_device.h" +#include "v4l2_videodevice.h" + +using namespace libcamera; + +class BufferSource +{ +public: + BufferSource(); + ~BufferSource(); + + int allocate(const StreamConfiguration &config); + const std::vector> &buffers(); + +private: + std::shared_ptr media_; + V4L2VideoDevice *video_; + std::vector> buffers_; +}; + +#endif /* __LIBCAMERA_BUFFER_SOURCE_TEST_H__ */ diff --git a/test/libtest/meson.build b/test/libtest/meson.build index 3e798ef3810e9b0d..33565e0eb3b66d6a 100644 --- a/test/libtest/meson.build +++ b/test/libtest/meson.build @@ -1,14 +1,11 @@ libtest_sources = files([ + 'buffer_source.cpp', 'camera_test.cpp', 'test.cpp', ]) -libtest = static_library('libtest', libtest_sources, - dependencies : libcamera_dep) - libtest_includes = include_directories('.') -test_libraries = [libtest] test_includes_public = [ libtest_includes, @@ -18,3 +15,9 @@ test_includes_internal = [ test_includes_public, libcamera_internal_includes, ] + +libtest = static_library('libtest', libtest_sources, + dependencies : libcamera_dep, + include_directories : test_includes_internal) + +test_libraries = [libtest] From patchwork Thu Mar 5 20:38:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2967 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 18759627BA for ; Thu, 5 Mar 2020 21:38:22 +0100 (CET) X-Halon-ID: 40a2bab1-5f21-11ea-9f85-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 40a2bab1-5f21-11ea-9f85-005056917a89; Thu, 05 Mar 2020 21:38:21 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 5 Mar 2020 21:38:04 +0100 Message-Id: <20200305203808.536050-4-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> References: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 3/7] libcamera: V4L2BufferCache: Mark Entry::operator==() as const X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Mar 2020 20:38:22 -0000 The comparison operator does not change any state, mark it as const. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/libcamera/include/v4l2_videodevice.h | 2 +- src/libcamera/v4l2_videodevice.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index fcf072641420dacf..359b366454e4e782 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -122,7 +122,7 @@ private: Entry(); Entry(bool free, const FrameBuffer &buffer); - bool operator==(const FrameBuffer &buffer); + bool operator==(const FrameBuffer &buffer) const; bool free; diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 99470ce11421c77c..d88cb0bd0771e545 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -256,7 +256,7 @@ V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer) planes_.emplace_back(plane); } -bool V4L2BufferCache::Entry::operator==(const FrameBuffer &buffer) +bool V4L2BufferCache::Entry::operator==(const FrameBuffer &buffer) const { const std::vector &planes = buffer.planes(); From patchwork Thu Mar 5 20:38:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2968 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7924560427 for ; Thu, 5 Mar 2020 21:38:22 +0100 (CET) X-Halon-ID: 40f1f818-5f21-11ea-9f85-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 40f1f818-5f21-11ea-9f85-005056917a89; Thu, 05 Mar 2020 21:38:21 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 5 Mar 2020 21:38:05 +0100 Message-Id: <20200305203808.536050-5-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> References: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 4/7] libcamera: V4L2BufferCache: Use the entry reference X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Mar 2020 20:38:22 -0000 Instead of looking up the index in the storage vector use the reference to it created at the beginning of the loop. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/libcamera/v4l2_videodevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index d88cb0bd0771e545..268de60bc7965f58 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -216,7 +216,7 @@ int V4L2BufferCache::get(const FrameBuffer &buffer) use = index; /* Try to find a cache hit by comparing the planes. */ - if (cache_[index] == buffer) { + if (entry == buffer) { hit = true; use = index; break; From patchwork Thu Mar 5 20:38:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2969 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1A8FF60427 for ; Thu, 5 Mar 2020 21:38:23 +0100 (CET) X-Halon-ID: 413d1dc8-5f21-11ea-9f85-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 413d1dc8-5f21-11ea-9f85-005056917a89; Thu, 05 Mar 2020 21:38:22 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 5 Mar 2020 21:38:06 +0100 Message-Id: <20200305203808.536050-6-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> References: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 5/7] libcamera: V4L2BufferCache: Check for hot hit first X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Mar 2020 20:38:23 -0000 Check for a hot cache hit before updating which buffer is best to evict in case no hot hit is found. This doesn't change the behaviour, but follows a more logical flow. Suggested-by: Jacopo Mondi Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/libcamera/v4l2_videodevice.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 268de60bc7965f58..c495de85f26efe14 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -212,15 +212,15 @@ int V4L2BufferCache::get(const FrameBuffer &buffer) if (!entry.free) continue; - if (use < 0) - use = index; - /* Try to find a cache hit by comparing the planes. */ if (entry == buffer) { hit = true; use = index; break; } + + if (use < 0) + use = index; } if (!hit) From patchwork Thu Mar 5 20:38:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2970 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0823A60427 for ; Thu, 5 Mar 2020 21:38:23 +0100 (CET) X-Halon-ID: 41966b75-5f21-11ea-9f85-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 41966b75-5f21-11ea-9f85-005056917a89; Thu, 05 Mar 2020 21:38:22 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 5 Mar 2020 21:38:07 +0100 Message-Id: <20200305203808.536050-7-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> References: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 6/7] test: v4l2_videodevice: Add test for V4L2BufferCache X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Mar 2020 20:38:24 -0000 Add test to test the different modes and situations the V4L2BufferCache can be put in. The tests verify that a FrameBuffer used with the cache results in a V4L2 video device index, and that the cache implementation is capable of keeping buffers in a hot state. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v1 - Update comments in code. - Use std::mt19937 PRNG instead of rand() - Print randomize and print std::mt19937 initial seed to be able to reproduce a test run with the same random sequences. - Add const std::vector> &buffers "alias" for source.buffers() to make code more readable. - Make use of libtest common implementation of BufferSource. --- test/v4l2_videodevice/buffer_cache.cpp | 215 +++++++++++++++++++++++++ test/v4l2_videodevice/meson.build | 1 + 2 files changed, 216 insertions(+) create mode 100644 test/v4l2_videodevice/buffer_cache.cpp diff --git a/test/v4l2_videodevice/buffer_cache.cpp b/test/v4l2_videodevice/buffer_cache.cpp new file mode 100644 index 0000000000000000..0a8cb0d28ca9b204 --- /dev/null +++ b/test/v4l2_videodevice/buffer_cache.cpp @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * Test the buffer cache different operation modes + */ + +#include +#include +#include + +#include + +#include "buffer_source.h" + +#include "test.h" + +using namespace libcamera; + +namespace { + +class BufferCacheTest : public Test +{ +public: + /* + * Test that a cache with the same size as there are buffers results in + * a sequential run over; 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, ... + * + * The test is only valid when the cache size is as least as big as the + * number of buffers. + */ + int testSequential(V4L2BufferCache *cache, + const std::vector> &buffers) + { + for (unsigned int i = 0; i < buffers.size() * 100; i++) { + int nBuffer = i % buffers.size(); + int index = cache->get(*buffers[nBuffer].get()); + + if (index != nBuffer) { + std::cout << "Expected index " << nBuffer + << " got " << index << std::endl; + return TestFail; + } + + cache->put(index); + } + + return TestPass; + } + + /* + * Test that randomly putting buffers to the cache always results in a + * valid index. + */ + int testRandom(V4L2BufferCache *cache, + const std::vector> &buffers) + { + std::uniform_int_distribution<> dist(0, buffers.size() - 1); + + for (unsigned int i = 0; i < buffers.size() * 100; i++) { + int nBuffer = dist(generator_); + int index = cache->get(*buffers[nBuffer].get()); + + if (index < 0) { + std::cout << "Failed lookup from cache" + << std::endl; + return TestFail; + } + + cache->put(index); + } + + return TestPass; + } + + /* + * Test that using a buffer more frequently keeps it hot in the cache at + * all times. + */ + int testHot(V4L2BufferCache *cache, + const std::vector> &buffers, + unsigned int hotFrequency) + { + /* Run the random test on the cache to make it messy. */ + if (testRandom(cache, buffers) != TestPass) + return TestFail; + + std::uniform_int_distribution<> dist(0, buffers.size() - 1); + + /* Pick a hot buffer at random and store its index. */ + int hotBuffer = dist(generator_); + int hotIndex = cache->get(*buffers[hotBuffer].get()); + cache->put(hotIndex); + + /* + * Queue hot buffer at the requested frequency and make sure + * it stays hot. + */ + for (unsigned int i = 0; i < buffers.size() * 100; i++) { + int nBuffer, index; + bool hotQueue = i % hotFrequency == 0; + + if (hotQueue) + nBuffer = hotBuffer; + else + nBuffer = dist(generator_); + + index = cache->get(*buffers[nBuffer].get()); + + if (index < 0) { + std::cout << "Failed lookup from cache" + << std::endl; + return TestFail; + } + + if (hotQueue && index != hotIndex) { + std::cout << "Hot buffer got cold" + << std::endl; + return TestFail; + } + + cache->put(index); + } + + return TestPass; + } + + int init() override + { + std::random_device rd; + unsigned int seed = rd(); + + std::cout << "Random seed is " << seed << std::endl; + + generator_.seed(seed); + + return TestPass; + } + + int run() override + { + const unsigned int numBuffers = 8; + + StreamConfiguration cfg; + cfg.pixelFormat = V4L2_PIX_FMT_YUYV; + cfg.size = Size(600, 800); + cfg.bufferCount = numBuffers; + + BufferSource source; + int ret = source.allocate(cfg); + if (ret != TestPass) + return ret; + + const std::vector> &buffers = + source.buffers(); + + if (buffers.size() != numBuffers) { + std::cout << "Got " << buffers.size() + << " buffers, expected " << numBuffers + << std::endl; + return TestFail; + } + + /* + * Test cache of same size as there are buffers, the cache is + * created from a list of buffers and will be pre-populated. + */ + V4L2BufferCache cacheFromBuffers(buffers); + + if (testSequential(&cacheFromBuffers, buffers) != TestPass) + return TestFail; + + if (testRandom(&cacheFromBuffers, buffers) != TestPass) + return TestFail; + + if (testHot(&cacheFromBuffers, buffers, numBuffers) != TestPass) + return TestFail; + + /* + * Test cache of same size as there are buffers, the cache is + * not pre-populated. + */ + V4L2BufferCache cacheFromNumbers(numBuffers); + + if (testSequential(&cacheFromNumbers, buffers) != TestPass) + return TestFail; + + if (testRandom(&cacheFromNumbers, buffers) != TestPass) + return TestFail; + + if (testHot(&cacheFromNumbers, buffers, numBuffers) != TestPass) + return TestFail; + + /* + * Test cache half the size of number of buffers used, the cache + * is not pre-populated. + */ + V4L2BufferCache cacheHalf(numBuffers / 2); + + if (testRandom(&cacheHalf, buffers) != TestPass) + return TestFail; + + if (testHot(&cacheHalf, buffers, numBuffers / 2) != TestPass) + return TestFail; + + return TestPass; + } + +private: + std::mt19937 generator_; +}; + +} /* namespace */ + +TEST_REGISTER(BufferCacheTest); diff --git a/test/v4l2_videodevice/meson.build b/test/v4l2_videodevice/meson.build index 5c52da7219c21cc3..685fcf6d16d72182 100644 --- a/test/v4l2_videodevice/meson.build +++ b/test/v4l2_videodevice/meson.build @@ -5,6 +5,7 @@ v4l2_videodevice_tests = [ [ 'controls', 'controls.cpp' ], [ 'formats', 'formats.cpp' ], [ 'request_buffers', 'request_buffers.cpp' ], + [ 'buffer_cache', 'buffer_cache.cpp' ], [ 'stream_on_off', 'stream_on_off.cpp' ], [ 'capture_async', 'capture_async.cpp' ], [ 'buffer_sharing', 'buffer_sharing.cpp' ], From patchwork Thu Mar 5 20:38:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2971 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BB0F360427 for ; Thu, 5 Mar 2020 21:38:24 +0100 (CET) X-Halon-ID: 420dd68b-5f21-11ea-9f85-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 420dd68b-5f21-11ea-9f85-005056917a89; Thu, 05 Mar 2020 21:38:23 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 5 Mar 2020 21:38:08 +0100 Message-Id: <20200305203808.536050-8-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> References: <20200305203808.536050-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 7/7] libcamera: V4L2BufferCache: Improve cache eviction strategy X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Mar 2020 20:38:25 -0000 The strategy used to find a free cache entry in the first implementation was not the smartest, it picked the first free entry. This lead to unwanted performance issues as the cache was not used as good as it could for imported buffers. Improve this by adding a last usage sequence number to the cache entries and change the eviction strategy to use the oldest free entry instead of the first one it finds. Signed-off-by: Niklas Söderlund Reviewed-by: Naushir Patuck Reviewed-by: Laurent Pinchart --- * Changes since v3 - Group C++ header includes. - s/UINT32_MAX/UINT64_MAX/ - Use std::memory_order_acq_rel instead of std::memory_order_seq_cst order to fetch_add() of the atomic sequence number. * Changes since v2 - Use a sequence number instead of timestamp to judge age of a cached entry. --- src/libcamera/include/v4l2_videodevice.h | 7 +++++-- src/libcamera/v4l2_videodevice.cpp | 23 +++++++++++++++-------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index 359b366454e4e782..04802012d5315d67 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -7,11 +7,12 @@ #ifndef __LIBCAMERA_V4L2_VIDEODEVICE_H__ #define __LIBCAMERA_V4L2_VIDEODEVICE_H__ +#include +#include #include #include #include -#include #include #include @@ -120,11 +121,12 @@ private: { public: Entry(); - Entry(bool free, const FrameBuffer &buffer); + Entry(bool free, uint64_t lastUsed, const FrameBuffer &buffer); bool operator==(const FrameBuffer &buffer) const; bool free; + uint64_t lastUsed; private: struct Plane { @@ -140,6 +142,7 @@ private: std::vector planes_; }; + std::atomic_uint64_t lastUsedCounter_; std::vector cache_; /* \todo Expose the miss counter through an instrumentation API. */ unsigned int missCounter_; diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index c495de85f26efe14..ca0d147f5fc1ea58 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -162,7 +162,7 @@ LOG_DECLARE_CATEGORY(V4L2) * buffer import, with buffers added to the cache as they are queued. */ V4L2BufferCache::V4L2BufferCache(unsigned int numEntries) - : missCounter_(0) + : lastUsedCounter_(1), missCounter_(0) { cache_.resize(numEntries); } @@ -176,10 +176,12 @@ V4L2BufferCache::V4L2BufferCache(unsigned int numEntries) * allocated. */ V4L2BufferCache::V4L2BufferCache(const std::vector> &buffers) - : missCounter_(0) + : lastUsedCounter_(1), missCounter_(0) { for (const std::unique_ptr &buffer : buffers) - cache_.emplace_back(true, buffer->planes()); + cache_.emplace_back(true, + lastUsedCounter_.fetch_add(1, std::memory_order_acq_rel), + buffer->planes()); } V4L2BufferCache::~V4L2BufferCache() @@ -205,6 +207,7 @@ int V4L2BufferCache::get(const FrameBuffer &buffer) { bool hit = false; int use = -1; + uint64_t oldest = UINT64_MAX; for (unsigned int index = 0; index < cache_.size(); index++) { const Entry &entry = cache_[index]; @@ -219,8 +222,10 @@ int V4L2BufferCache::get(const FrameBuffer &buffer) break; } - if (use < 0) + if (entry.lastUsed < oldest) { use = index; + oldest = entry.lastUsed; + } } if (!hit) @@ -229,7 +234,9 @@ int V4L2BufferCache::get(const FrameBuffer &buffer) if (use < 0) return -ENOENT; - cache_[use] = Entry(false, buffer); + cache_[use] = Entry(false, + lastUsedCounter_.fetch_add(1, std::memory_order_acq_rel), + buffer); return use; } @@ -245,12 +252,12 @@ void V4L2BufferCache::put(unsigned int index) } V4L2BufferCache::Entry::Entry() - : free(true) + : free(true), lastUsed(0) { } -V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer) - : free(free) +V4L2BufferCache::Entry::Entry(bool free, uint64_t lastUsed, const FrameBuffer &buffer) + : free(free), lastUsed(lastUsed) { for (const FrameBuffer::Plane &plane : buffer.planes()) planes_.emplace_back(plane);