From patchwork Mon Jun 29 16:30:17 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 27132 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E0207C3306 for ; Mon, 29 Jun 2026 16:31:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7101F65FDA; Mon, 29 Jun 2026 18:31:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="PAjTMNpp"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9229065F7C for ; Mon, 29 Jun 2026 18:30:34 +0200 (CEST) Received: from pb-laptop.local (185.221.140.128.nat.pool.zt.hu [185.221.140.128]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6CDBB8D4 for ; Mon, 29 Jun 2026 18:29:51 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1782750591; bh=EAh0Od/mEcAreY6/dYpAHoMsLxIMULNBQsY6rjYG4HM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=PAjTMNppzc96DM9V7twSUHpRq7Ec6TVkeL+sz+4QudV565RmIjxndeoslDVrG8HNg r/gf8EA/u6r1Tf17+094V7OoVFU+pDP+065631qAKZJ0qNJg//jZ5dQ6weHQw57nbs 4F89WW/djUJMxtjQIpeUayiXnAhAomwn9ZAmtWV4= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v1 54/54] test: fence: Enable Date: Mon, 29 Jun 2026 18:30:17 +0200 Message-ID: <20260629163017.863145-55-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260629163017.863145-1-barnabas.pocze@ideasonboard.com> References: <20260629163017.863145-1-barnabas.pocze@ideasonboard.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Enable the "fence" test and adjust it for the new buffer pool interface. Signed-off-by: Barnabás Pőcze --- test/fence.cpp | 214 +++++++++++++++++++++++------------------------ test/meson.build | 2 +- 2 files changed, 104 insertions(+), 112 deletions(-) diff --git a/test/fence.cpp b/test/fence.cpp index 8095b22895..50ec6cbf11 100644 --- a/test/fence.cpp +++ b/test/fence.cpp @@ -36,10 +36,9 @@ protected: int run() override; private: - int validateExpiredRequest(Request *request); + int validateExpiredBuffer(FrameBuffer *buffer); int validateRequest(Request *request); void requestComplete(Request *request); - void requestRequeue(Request *request); void signalFence(); @@ -55,15 +54,11 @@ private: Stream *stream_; bool expectedCompletionResult_ = true; - bool setFence_ = true; - /* - * Request IDs track the number of requests that have completed. They - * are one-based, and don't wrap. - */ unsigned int completedRequestId_; - unsigned int signalledRequestId_; - unsigned int expiredRequestId_; + unsigned int queuedRequests_ = 0; + FrameBuffer *testBuffer_ = nullptr; + unsigned testBufferSeen_ = 0; unsigned int nbuffers_; int efd2_; @@ -123,38 +118,30 @@ int FenceTest::init() if (allocator_->allocate(stream_) < 0) return TestFail; - nbuffers_ = allocator_->buffers(stream_).size(); + const auto &buffers = allocator_->buffers(stream_); + nbuffers_ = buffers.size(); if (nbuffers_ < 2) { cerr << "Not enough buffers available" << endl; return TestFail; } completedRequestId_ = 0; + queuedRequests_ = 0; /* - * All but two requests are queued without a fence. Request - * expiredRequestId_ will be queued with a fence that we won't signal - * (which is then expected to expire), and request signalledRequestId_ - * will be queued with a fence that gets signalled. Select nbuffers_ - * and nbuffers_ * 2 for those two requests, to space them by a few - * frames while still not requiring a long time for the test to - * complete. + * The buffer to use for testing. It will be queued 3 times: + * * first, without any fence + * * second, with a fence that is signalled + * * third,with a fence that won't be signalled */ - expiredRequestId_ = nbuffers_; - signalledRequestId_ = nbuffers_ * 2; + testBuffer_ = buffers.front().get(); + testBufferSeen_ = 0; return TestPass; } -int FenceTest::validateExpiredRequest(Request *request) +int FenceTest::validateExpiredBuffer(FrameBuffer *buffer) { - /* The last request is expected to fail. */ - if (request->status() != Request::RequestCancelled) { - cerr << "The last request should have failed: " << endl; - return TestFail; - } - - FrameBuffer *buffer = request->buffers().begin()->second; std::unique_ptr fence = buffer->releaseFence(); if (!fence) { cerr << "The expired fence should be present" << endl; @@ -198,50 +185,21 @@ int FenceTest::validateRequest(Request *request) return TestPass; } -void FenceTest::requestRequeue(Request *request) +void FenceTest::requestComplete(Request *request) { const Request::BufferMap &buffers = request->buffers(); const Stream *stream = buffers.begin()->first; FrameBuffer *buffer = buffers.begin()->second; - request->reuse(); - - if (completedRequestId_ == signalledRequestId_ - nbuffers_ && setFence_) { - /* - * This is the request that will be used to test fence - * signalling when it completes next time. Add a fence to it, - * using efd2_. The main loop will signal the fence by using a - * timer to write to the efd2_ file descriptor before the fence - * expires. - */ - std::unique_ptr fence = - std::make_unique(std::move(eventFd2_)); - request->addBuffer(stream, buffer, std::move(fence)); - } else { - /* All the other requests continue to operate without fences. */ - request->addBuffer(stream, buffer); - } - - camera_->queueRequest(request); -} + completedRequestId_ += 1; -void FenceTest::requestComplete(Request *request) -{ - completedRequestId_++; + if (buffer == testBuffer_) + testBufferSeen_ += 1; - /* - * Request expiredRequestId_ is expected to fail as its fence has not - * been signalled. - * - * Validate the fence status but do not re-queue it. - */ - if (completedRequestId_ == expiredRequestId_) { - if (validateExpiredRequest(request) != TestPass) - expectedCompletionResult_ = false; - - dispatcher_->interrupt(); - return; - } + cout << "completedRequestId:" << completedRequestId_ << " " + << "buffer:" << buffer << " " + << "testBufferSeen:" << testBufferSeen_ + << endl; /* Validate all other requests. */ if (validateRequest(request) != TestPass) { @@ -251,12 +209,41 @@ void FenceTest::requestComplete(Request *request) return; } - requestRequeue(request); + if (completedRequestId_ % nbuffers_ == 0) { + for (const auto &b : allocator_->buffers(stream_)) { + std::unique_ptr fence; + + if (b.get() == testBuffer_) { + if (testBufferSeen_ == 1) { + /* This fence will be signalled. */ + assert(eventFd2_.isValid()); + fence = std::make_unique(std::move(eventFd2_)); + } else if (testBufferSeen_ == 2) { + /* This fence won't be signalled. */ + assert(eventFd_.isValid()); + fence = std::make_unique(std::move(eventFd_)); + } + } + + cout << "adding buffer:" << b.get() << " fence:" << (fence ? fence->fd().get() : -1) << endl; + camera_->addBuffer(stream_, b.get(), std::move(fence)); + } + } + + if (testBufferSeen_ == 1 && completedRequestId_ == 2 * nbuffers_ - 1) { + cout << "signalling fence:" << efd2_ << endl; + signalFence(); + } + + request->reuse(); + + if (queuedRequests_ < 3 * nbuffers_ - 1) { + cout << "queueing request:" << request << endl; + request->enableStream(stream, true); + camera_->queueRequest(request); + queuedRequests_ += 1; + } - /* - * Interrupt the dispatcher to return control to the main loop and - * activate the fenceTimer. - */ dispatcher_->interrupt(); } @@ -269,9 +256,6 @@ void FenceTest::signalFence() ret = write(efd2_, &value, sizeof(value)); if (ret != sizeof(value)) cerr << "Failed to signal fence" << endl; - - setFence_ = false; - dispatcher_->processEvents(); } int FenceTest::run() @@ -283,26 +267,7 @@ int FenceTest::run() return TestFail; } - int ret; - if (i == expiredRequestId_ - 1) { - /* This request will have a fence, and it will expire. */ - std::unique_ptr fence = - std::make_unique(std::move(eventFd_)); - if (!fence->isValid()) { - cerr << "Fence should be valid" << endl; - return TestFail; - } - - ret = request->addBuffer(stream_, buffer.get(), std::move(fence)); - } else { - /* All other requests will have no Fence. */ - ret = request->addBuffer(stream_, buffer.get()); - } - - if (ret) { - cerr << "Failed to associate buffer with request" << endl; - return TestFail; - } + request->enableStream(stream_, true); requests_.push_back(std::move(request)); } @@ -314,8 +279,14 @@ int FenceTest::run() return TestFail; } - for (std::unique_ptr &request : requests_) { - if (camera_->queueRequest(request.get())) { + for (const auto &[i, buffer] : utils::enumerate(allocator_->buffers(stream_))) { + int ret = camera_->addBuffer(stream_, buffer.get()); + if (ret) { + cerr << "Failed to associate buffer with request" << endl; + return TestFail; + } + + if (camera_->queueRequest(requests_[i].get())) { cerr << "Failed to queue request" << endl; return TestFail; } @@ -323,38 +294,59 @@ int FenceTest::run() expectedCompletionResult_ = true; - /* This timer serves to signal fences associated with "signalledRequestId_" */ - Timer fenceTimer; - fenceTimer.timeout.connect(this, &FenceTest::signalFence); - /* * Loop long enough for all requests to complete, allowing 500ms per * request. */ Timer timer; - timer.start(500ms * (signalledRequestId_ + 1)); - while (timer.isRunning() && expectedCompletionResult_ && - completedRequestId_ <= signalledRequestId_ + 1) { - if (completedRequestId_ == signalledRequestId_ - 1 && setFence_) - /* - * The request just before signalledRequestId_ has just - * completed. Request signalledRequestId_ has been - * queued with a fence, and libcamera is likely already - * waiting on the fence, or will soon. Start the timer - * to signal the fence in 10 msec. - */ - fenceTimer.start(10ms); + timer.start(500ms * (3 + 1) * nbuffers_); + for (;;) { + if (!timer.isRunning() || !expectedCompletionResult_) + break; + + if (completedRequestId_ == 3 * nbuffers_ - 1 && testBufferSeen_ == 2) + break; dispatcher_->processEvents(); } camera_->requestCompleted.disconnect(); - if (camera_->stop()) { + bool testBufferFound = false; + camera_->bufferCompleted.connect(this, [&](Request *request, [[maybe_unused]] const Stream *stream, FrameBuffer *buffer) { + if (request) + return; + + if (buffer == testBuffer_) { + if (validateExpiredBuffer(buffer) != TestPass) + expectedCompletionResult_ = false; + testBufferFound = true; + } + }); + + int ret = camera_->stop(); + camera_->bufferCompleted.disconnect(this); + + if (ret) { cerr << "Failed to stop camera" << endl; return TestFail; } + if (testBufferSeen_ != 2) { + cerr << "Test buffer not seen exactly twice" << endl; + return TestFail; + } + + if (completedRequestId_ != 3 * nbuffers_ - 1) { + cerr << "Test buffer not seen exactly twice" << endl; + return TestFail; + } + + if (!testBufferFound) { + cerr << "Buffer with non-signalled fence not returned" << endl; + return TestFail; + } + return expectedCompletionResult_ ? TestPass : TestFail; } diff --git a/test/meson.build b/test/meson.build index b51bd6ef66..e4450625ee 100644 --- a/test/meson.build +++ b/test/meson.build @@ -80,7 +80,7 @@ internal_tests = [ ] internal_non_parallel_tests = [ - # {'name': 'fence', 'sources': ['fence.cpp']}, + {'name': 'fence', 'sources': ['fence.cpp']}, {'name': 'mapped-buffer', 'sources': ['mapped-buffer.cpp']}, ]