Patch Detail
Show a patch.
GET /api/patches/27132/?format=api
{ "id": 27132, "url": "https://patchwork.libcamera.org/api/patches/27132/?format=api", "web_url": "https://patchwork.libcamera.org/patch/27132/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260629163017.863145-55-barnabas.pocze@ideasonboard.com>", "date": "2026-06-29T16:30:17", "name": "[RFC,v1,54/54] test: fence: Enable", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "0b9978efcd917146cf3164010152820b7d5793ff", "submitter": { "id": 216, "url": "https://patchwork.libcamera.org/api/people/216/?format=api", "name": "Barnabás Pőcze", "email": "barnabas.pocze@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/27132/mbox/", "series": [ { "id": 6025, "url": "https://patchwork.libcamera.org/api/series/6025/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=6025", "date": "2026-06-29T16:29:23", "name": "libcamera: Split requests and buffers", "version": 1, "mbox": "https://patchwork.libcamera.org/series/6025/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/27132/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/27132/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id E0207C3306\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Jun 2026 16:31:43 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7101F65FDA;\n\tMon, 29 Jun 2026 18:31:43 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9229065F7C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Jun 2026 18:30:34 +0200 (CEST)", "from pb-laptop.local (185.221.140.128.nat.pool.zt.hu\n\t[185.221.140.128])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 6CDBB8D4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Jun 2026 18:29:51 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"PAjTMNpp\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1782750591;\n\tbh=EAh0Od/mEcAreY6/dYpAHoMsLxIMULNBQsY6rjYG4HM=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=PAjTMNppzc96DM9V7twSUHpRq7Ec6TVkeL+sz+4QudV565RmIjxndeoslDVrG8HNg\n\tr/gf8EA/u6r1Tf17+094V7OoVFU+pDP+065631qAKZJ0qNJg//jZ5dQ6weHQw57nbs\n\t4F89WW/djUJMxtjQIpeUayiXnAhAomwn9ZAmtWV4=", "From": "=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>", "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", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "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>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Enable the \"fence\" test and adjust it for the new buffer pool interface.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n test/fence.cpp | 214 +++++++++++++++++++++++------------------------\n test/meson.build | 2 +-\n 2 files changed, 104 insertions(+), 112 deletions(-)", "diff": "diff --git a/test/fence.cpp b/test/fence.cpp\nindex 8095b22895..50ec6cbf11 100644\n--- a/test/fence.cpp\n+++ b/test/fence.cpp\n@@ -36,10 +36,9 @@ protected:\n \tint run() override;\n \n private:\n-\tint validateExpiredRequest(Request *request);\n+\tint validateExpiredBuffer(FrameBuffer *buffer);\n \tint validateRequest(Request *request);\n \tvoid requestComplete(Request *request);\n-\tvoid requestRequeue(Request *request);\n \n \tvoid signalFence();\n \n@@ -55,15 +54,11 @@ private:\n \tStream *stream_;\n \n \tbool expectedCompletionResult_ = true;\n-\tbool setFence_ = true;\n \n-\t/*\n-\t * Request IDs track the number of requests that have completed. They\n-\t * are one-based, and don't wrap.\n-\t */\n \tunsigned int completedRequestId_;\n-\tunsigned int signalledRequestId_;\n-\tunsigned int expiredRequestId_;\n+\tunsigned int queuedRequests_ = 0;\n+\tFrameBuffer *testBuffer_ = nullptr;\n+\tunsigned testBufferSeen_ = 0;\n \tunsigned int nbuffers_;\n \n \tint efd2_;\n@@ -123,38 +118,30 @@ int FenceTest::init()\n \tif (allocator_->allocate(stream_) < 0)\n \t\treturn TestFail;\n \n-\tnbuffers_ = allocator_->buffers(stream_).size();\n+\tconst auto &buffers = allocator_->buffers(stream_);\n+\tnbuffers_ = buffers.size();\n \tif (nbuffers_ < 2) {\n \t\tcerr << \"Not enough buffers available\" << endl;\n \t\treturn TestFail;\n \t}\n \n \tcompletedRequestId_ = 0;\n+\tqueuedRequests_ = 0;\n \n \t/*\n-\t * All but two requests are queued without a fence. Request\n-\t * expiredRequestId_ will be queued with a fence that we won't signal\n-\t * (which is then expected to expire), and request signalledRequestId_\n-\t * will be queued with a fence that gets signalled. Select nbuffers_\n-\t * and nbuffers_ * 2 for those two requests, to space them by a few\n-\t * frames while still not requiring a long time for the test to\n-\t * complete.\n+\t * The buffer to use for testing. It will be queued 3 times:\n+\t * * first, without any fence\n+\t * * second, with a fence that is signalled\n+\t * * third,with a fence that won't be signalled\n \t */\n-\texpiredRequestId_ = nbuffers_;\n-\tsignalledRequestId_ = nbuffers_ * 2;\n+\ttestBuffer_ = buffers.front().get();\n+\ttestBufferSeen_ = 0;\n \n \treturn TestPass;\n }\n \n-int FenceTest::validateExpiredRequest(Request *request)\n+int FenceTest::validateExpiredBuffer(FrameBuffer *buffer)\n {\n-\t/* The last request is expected to fail. */\n-\tif (request->status() != Request::RequestCancelled) {\n-\t\tcerr << \"The last request should have failed: \" << endl;\n-\t\treturn TestFail;\n-\t}\n-\n-\tFrameBuffer *buffer = request->buffers().begin()->second;\n \tstd::unique_ptr<Fence> fence = buffer->releaseFence();\n \tif (!fence) {\n \t\tcerr << \"The expired fence should be present\" << endl;\n@@ -198,50 +185,21 @@ int FenceTest::validateRequest(Request *request)\n \treturn TestPass;\n }\n \n-void FenceTest::requestRequeue(Request *request)\n+void FenceTest::requestComplete(Request *request)\n {\n \tconst Request::BufferMap &buffers = request->buffers();\n \tconst Stream *stream = buffers.begin()->first;\n \tFrameBuffer *buffer = buffers.begin()->second;\n \n-\trequest->reuse();\n-\n-\tif (completedRequestId_ == signalledRequestId_ - nbuffers_ && setFence_) {\n-\t\t/*\n-\t\t * This is the request that will be used to test fence\n-\t\t * signalling when it completes next time. Add a fence to it,\n-\t\t * using efd2_. The main loop will signal the fence by using a\n-\t\t * timer to write to the efd2_ file descriptor before the fence\n-\t\t * expires.\n-\t\t */\n-\t\tstd::unique_ptr<Fence> fence =\n-\t\t\tstd::make_unique<Fence>(std::move(eventFd2_));\n-\t\trequest->addBuffer(stream, buffer, std::move(fence));\n-\t} else {\n-\t\t/* All the other requests continue to operate without fences. */\n-\t\trequest->addBuffer(stream, buffer);\n-\t}\n-\n-\tcamera_->queueRequest(request);\n-}\n+\tcompletedRequestId_ += 1;\n \n-void FenceTest::requestComplete(Request *request)\n-{\n-\tcompletedRequestId_++;\n+\tif (buffer == testBuffer_)\n+\t\ttestBufferSeen_ += 1;\n \n-\t/*\n-\t * Request expiredRequestId_ is expected to fail as its fence has not\n-\t * been signalled.\n-\t *\n-\t * Validate the fence status but do not re-queue it.\n-\t */\n-\tif (completedRequestId_ == expiredRequestId_) {\n-\t\tif (validateExpiredRequest(request) != TestPass)\n-\t\t\texpectedCompletionResult_ = false;\n-\n-\t\tdispatcher_->interrupt();\n-\t\treturn;\n-\t}\n+\tcout << \"completedRequestId:\" << completedRequestId_ << \" \"\n+\t << \"buffer:\" << buffer << \" \"\n+\t << \"testBufferSeen:\" << testBufferSeen_\n+\t << endl;\n \n \t/* Validate all other requests. */\n \tif (validateRequest(request) != TestPass) {\n@@ -251,12 +209,41 @@ void FenceTest::requestComplete(Request *request)\n \t\treturn;\n \t}\n \n-\trequestRequeue(request);\n+\tif (completedRequestId_ % nbuffers_ == 0) {\n+\t\tfor (const auto &b : allocator_->buffers(stream_)) {\n+\t\t\tstd::unique_ptr<Fence> fence;\n+\n+\t\t\tif (b.get() == testBuffer_) {\n+\t\t\t\tif (testBufferSeen_ == 1) {\n+\t\t\t\t\t/* This fence will be signalled. */\n+\t\t\t\t\tassert(eventFd2_.isValid());\n+\t\t\t\t\tfence = std::make_unique<Fence>(std::move(eventFd2_));\n+\t\t\t\t} else if (testBufferSeen_ == 2) {\n+\t\t\t\t\t/* This fence won't be signalled. */\n+\t\t\t\t\tassert(eventFd_.isValid());\n+\t\t\t\t\tfence = std::make_unique<Fence>(std::move(eventFd_));\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tcout << \"adding buffer:\" << b.get() << \" fence:\" << (fence ? fence->fd().get() : -1) << endl;\n+\t\t\tcamera_->addBuffer(stream_, b.get(), std::move(fence));\n+\t\t}\n+\t}\n+\n+\tif (testBufferSeen_ == 1 && completedRequestId_ == 2 * nbuffers_ - 1) {\n+\t\tcout << \"signalling fence:\" << efd2_ << endl;\n+\t\tsignalFence();\n+\t}\n+\n+\trequest->reuse();\n+\n+\tif (queuedRequests_ < 3 * nbuffers_ - 1) {\n+\t\tcout << \"queueing request:\" << request << endl;\n+\t\trequest->enableStream(stream, true);\n+\t\tcamera_->queueRequest(request);\n+\t\tqueuedRequests_ += 1;\n+\t}\n \n-\t/*\n-\t * Interrupt the dispatcher to return control to the main loop and\n-\t * activate the fenceTimer.\n-\t */\n \tdispatcher_->interrupt();\n }\n \n@@ -269,9 +256,6 @@ void FenceTest::signalFence()\n \tret = write(efd2_, &value, sizeof(value));\n \tif (ret != sizeof(value))\n \t\tcerr << \"Failed to signal fence\" << endl;\n-\n-\tsetFence_ = false;\n-\tdispatcher_->processEvents();\n }\n \n int FenceTest::run()\n@@ -283,26 +267,7 @@ int FenceTest::run()\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tint ret;\n-\t\tif (i == expiredRequestId_ - 1) {\n-\t\t\t/* This request will have a fence, and it will expire. */\n-\t\t\tstd::unique_ptr<Fence> fence =\n-\t\t\t\tstd::make_unique<Fence>(std::move(eventFd_));\n-\t\t\tif (!fence->isValid()) {\n-\t\t\t\tcerr << \"Fence should be valid\" << endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\n-\t\t\tret = request->addBuffer(stream_, buffer.get(), std::move(fence));\n-\t\t} else {\n-\t\t\t/* All other requests will have no Fence. */\n-\t\t\tret = request->addBuffer(stream_, buffer.get());\n-\t\t}\n-\n-\t\tif (ret) {\n-\t\t\tcerr << \"Failed to associate buffer with request\" << endl;\n-\t\t\treturn TestFail;\n-\t\t}\n+\t\trequest->enableStream(stream_, true);\n \n \t\trequests_.push_back(std::move(request));\n \t}\n@@ -314,8 +279,14 @@ int FenceTest::run()\n \t\treturn TestFail;\n \t}\n \n-\tfor (std::unique_ptr<Request> &request : requests_) {\n-\t\tif (camera_->queueRequest(request.get())) {\n+\tfor (const auto &[i, buffer] : utils::enumerate(allocator_->buffers(stream_))) {\n+\t\tint ret = camera_->addBuffer(stream_, buffer.get());\n+\t\tif (ret) {\n+\t\t\tcerr << \"Failed to associate buffer with request\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tif (camera_->queueRequest(requests_[i].get())) {\n \t\t\tcerr << \"Failed to queue request\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n@@ -323,38 +294,59 @@ int FenceTest::run()\n \n \texpectedCompletionResult_ = true;\n \n-\t/* This timer serves to signal fences associated with \"signalledRequestId_\" */\n-\tTimer fenceTimer;\n-\tfenceTimer.timeout.connect(this, &FenceTest::signalFence);\n-\n \t/*\n \t * Loop long enough for all requests to complete, allowing 500ms per\n \t * request.\n \t */\n \tTimer timer;\n-\ttimer.start(500ms * (signalledRequestId_ + 1));\n-\twhile (timer.isRunning() && expectedCompletionResult_ &&\n-\t completedRequestId_ <= signalledRequestId_ + 1) {\n-\t\tif (completedRequestId_ == signalledRequestId_ - 1 && setFence_)\n-\t\t\t/*\n-\t\t\t * The request just before signalledRequestId_ has just\n-\t\t\t * completed. Request signalledRequestId_ has been\n-\t\t\t * queued with a fence, and libcamera is likely already\n-\t\t\t * waiting on the fence, or will soon. Start the timer\n-\t\t\t * to signal the fence in 10 msec.\n-\t\t\t */\n-\t\t\tfenceTimer.start(10ms);\n+\ttimer.start(500ms * (3 + 1) * nbuffers_);\n+\tfor (;;) {\n+\t\tif (!timer.isRunning() || !expectedCompletionResult_)\n+\t\t\tbreak;\n+\n+\t\tif (completedRequestId_ == 3 * nbuffers_ - 1 && testBufferSeen_ == 2)\n+\t\t\tbreak;\n \n \t\tdispatcher_->processEvents();\n \t}\n \n \tcamera_->requestCompleted.disconnect();\n \n-\tif (camera_->stop()) {\n+\tbool testBufferFound = false;\n+\tcamera_->bufferCompleted.connect(this, [&](Request *request, [[maybe_unused]] const Stream *stream, FrameBuffer *buffer) {\n+\t\tif (request)\n+\t\t\treturn;\n+\n+\t\tif (buffer == testBuffer_) {\n+\t\t\tif (validateExpiredBuffer(buffer) != TestPass)\n+\t\t\t\texpectedCompletionResult_ = false;\n+\t\t\ttestBufferFound = true;\n+\t\t}\n+\t});\n+\n+\tint ret = camera_->stop();\n+\tcamera_->bufferCompleted.disconnect(this);\n+\n+\tif (ret) {\n \t\tcerr << \"Failed to stop camera\" << endl;\n \t\treturn TestFail;\n \t}\n \n+\tif (testBufferSeen_ != 2) {\n+\t\tcerr << \"Test buffer not seen exactly twice\" << endl;\n+\t\treturn TestFail;\n+\t}\n+\n+\tif (completedRequestId_ != 3 * nbuffers_ - 1) {\n+\t\tcerr << \"Test buffer not seen exactly twice\" << endl;\n+\t\treturn TestFail;\n+\t}\n+\n+\tif (!testBufferFound) {\n+\t\tcerr << \"Buffer with non-signalled fence not returned\" << endl;\n+\t\treturn TestFail;\n+\t}\n+\n \treturn expectedCompletionResult_ ? TestPass : TestFail;\n }\n \ndiff --git a/test/meson.build b/test/meson.build\nindex b51bd6ef66..e4450625ee 100644\n--- a/test/meson.build\n+++ b/test/meson.build\n@@ -80,7 +80,7 @@ internal_tests = [\n ]\n \n internal_non_parallel_tests = [\n- # {'name': 'fence', 'sources': ['fence.cpp']},\n+ {'name': 'fence', 'sources': ['fence.cpp']},\n {'name': 'mapped-buffer', 'sources': ['mapped-buffer.cpp']},\n ]\n \n", "prefixes": [ "RFC", "v1", "54/54" ] }