[RFC,v1,22/54] libcamera: camera: Add buffer pool
diff mbox series

Message ID 20260629163017.863145-23-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • libcamera: Split requests and buffers
Related show

Commit Message

Barnabás Pőcze June 29, 2026, 4:29 p.m. UTC
Use the recently added `StreamData` to store a set of buffers for each
stream. Introduce the `Camera::addBuffer()` method that can be used by
an application to add buffers.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 include/libcamera/camera.h                    |  3 +
 include/libcamera/internal/camera.h           | 15 ++++
 include/libcamera/internal/pipeline_handler.h |  5 ++
 src/libcamera/camera.cpp                      | 51 +++++++++++
 src/libcamera/pipeline_handler.cpp            | 88 +++++++++++++++++++
 5 files changed, 162 insertions(+)

Patch
diff mbox series

diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
index 443116b588..471975a890 100644
--- a/include/libcamera/camera.h
+++ b/include/libcamera/camera.h
@@ -28,6 +28,7 @@ 
 
 namespace libcamera {
 
+class Fence;
 class FrameBuffer;
 class FrameBufferAllocator;
 class PipelineHandler;
@@ -152,6 +153,8 @@  public:
 	int start(const ControlList *controls = nullptr);
 	int stop();
 
+	int addBuffer(const Stream *stream, FrameBuffer *buffer, std::unique_ptr<Fence> &&fence = {});
+
 private:
 	LIBCAMERA_DISABLE_COPY(Camera)
 
diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h
index 43b6a6e4a2..6ba77eb882 100644
--- a/include/libcamera/internal/camera.h
+++ b/include/libcamera/internal/camera.h
@@ -15,14 +15,17 @@ 
 #include <stdint.h>
 #include <string>
 #include <unordered_map>
+#include <vector>
 
 #include <libcamera/base/class.h>
+#include <libcamera/base/event_notifier.h>
 
 #include <libcamera/camera.h>
 
 namespace libcamera {
 
 class CameraControlValidator;
+class Fence;
 class PipelineHandler;
 class Stream;
 
@@ -57,6 +60,15 @@  private:
 
 	struct StreamData {
 		bool active = false;
+		std::vector<FrameBuffer *> buffers;
+	};
+
+	struct PendingFence {
+		EventNotifier notifier;
+		const Stream *stream;
+		FrameBuffer *buffer;
+
+		PendingFence(const Stream *s, FrameBuffer *b);
 	};
 
 	bool isAcquired() const;
@@ -74,11 +86,14 @@  private:
 	std::string id_;
 	std::set<Stream *> streams_;
 	std::unordered_map<const Stream *, StreamData> streamData_;
+	std::list<PendingFence> pendingFences_;
 
 	bool disconnected_;
 	std::atomic<State> state_;
 
 	std::unique_ptr<CameraControlValidator> validator_;
+
+	friend PipelineHandler;
 };
 
 } /* namespace libcamera */
diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
index cc52c6b045..81ec359981 100644
--- a/include/libcamera/internal/pipeline_handler.h
+++ b/include/libcamera/internal/pipeline_handler.h
@@ -27,6 +27,7 @@  class Camera;
 class CameraConfiguration;
 class DeviceEnumerator;
 class DeviceMatch;
+class Fence;
 class FrameBuffer;
 class MediaDevice;
 class PipelineHandler;
@@ -69,6 +70,10 @@  public:
 	void completeRequest(Request *request);
 	void cancelRequest(Request *request);
 
+	void addBuffer(Camera *camera,
+		       const Stream *stream, FrameBuffer *buffer,
+		       std::unique_ptr<Fence> &&fence);
+
 	std::string configurationFile(const std::string &subdir,
 				      const std::string &name,
 				      bool silent = false) const;
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index aa5682f9d7..31ab59e0b7 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -27,6 +27,7 @@ 
 
 #include "libcamera/internal/camera.h"
 #include "libcamera/internal/camera_controls.h"
+#include "libcamera/internal/framebuffer.h"
 #include "libcamera/internal/pipeline_handler.h"
 #include "libcamera/internal/request.h"
 
@@ -746,6 +747,13 @@  void Camera::Private::setState(State state)
 {
 	state_.store(state, std::memory_order_release);
 }
+
+Camera::Private::PendingFence::PendingFence(const Stream *s, FrameBuffer *b)
+	: notifier(b->_d()->fence()->fd().get(), EventNotifier::Read),
+	  stream(s),
+	  buffer(b)
+{
+}
 #endif /* __DOXYGEN_PUBLIC__ */
 
 /**
@@ -1061,6 +1069,9 @@  int Camera::release()
 		d->pipe_->invokeMethod(&PipelineHandler::release,
 				       ConnectionTypeBlocking, this);
 
+	// \todo clear buffer pool
+	// \todo empty `pendingBuffers_` and report "cancelled" fences
+
 	d->setState(Private::CameraAvailable);
 
 	return 0;
@@ -1448,6 +1459,9 @@  int Camera::start(const ControlList *controls)
  * This function stops capturing and processing requests immediately. All
  * pending requests are cancelled and complete synchronously in an error state.
  *
+ * All buffers in the camera's buffer pool are returned via the Camera::bufferCompleted
+ * event synchronously with the \a request parameter equal to \a nullptr.
+ *
  * \context This function may be called in any camera state as defined in \ref
  * camera_operation, and shall be synchronized by the caller with other
  * functions that affect the camera state. If called when the camera isn't
@@ -1486,6 +1500,43 @@  int Camera::stop()
 	return 0;
 }
 
+/**
+ * \fn Camera::addBuffer()
+ * \brief Add a buffer to buffer pool of the camera
+ * \param[in] stream The stream
+ * \param[in] buffer The buffer
+ * \param[in] fence The fence for \a buffer
+ *
+ * \context This function may only be called when the camera is
+ * in the Running state as defined in \ref camera_operation.
+ *
+ * \return 0 on success or a negative error code otherwise
+ *
+ * \internal
+ * \todo Add `addBuffers()` that accepts a list of (stream, buffer, fence) tuples.
+ */
+int Camera::addBuffer(const Stream *stream, FrameBuffer *buffer, std::unique_ptr<Fence> &&fence)
+{
+	Private *const d = _d();
+
+	int ret = d->isAccessAllowed(Private::CameraRunning);
+	if (ret < 0)
+		return ret;
+
+	auto it = d->streamData_.find(stream);
+	if (it == d->streamData_.end() || !it->second.active)
+		return -EINVAL;
+	if (!buffer)
+		return -EINVAL;
+	if (buffer->_d()->fence())
+		return -EINVAL;
+
+	d->pipe_->invokeMethod(&PipelineHandler::addBuffer, ConnectionTypeQueued,
+			       this, stream, buffer, std::move(fence));
+
+	return 0;
+}
+
 /**
  * \brief Handle request completion and notify application
  * \param[in] request The request that has completed
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index 99f35d1e42..54ef8296a4 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -22,6 +22,7 @@ 
 #include "libcamera/internal/camera.h"
 #include "libcamera/internal/camera_manager.h"
 #include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/framebuffer.h"
 #include "libcamera/internal/media_device.h"
 #include "libcamera/internal/request.h"
 #include "libcamera/internal/tracepoints.h"
@@ -394,6 +395,24 @@  void PipelineHandler::stop(Camera *camera)
 		doQueueRequest(request);
 	}
 
+	const auto returnBuffer = [&](FrameBuffer *buffer) {
+		ASSERT(!buffer->_d()->stream_);
+		buffer->_d()->cancel();
+		camera->bufferCompleted.emit(nullptr, buffer);
+	};
+
+	for (auto &pf : data->pendingFences_)
+		returnBuffer(pf.buffer);
+
+	data->pendingFences_.clear();
+
+	for (auto &[stream, streamData] : data->streamData_) {
+		for (FrameBuffer *buffer : streamData.buffers)
+			returnBuffer(buffer);
+
+		streamData.buffers.clear();
+	}
+
 	/* Make sure no requests are pending. */
 	ASSERT(data->queuedRequests_.empty());
 	ASSERT(data->waitingRequests_.empty());
@@ -611,6 +630,75 @@  void PipelineHandler::cancelRequest(Request *request)
 	completeRequest(request);
 }
 
+/**
+ * \fn PipelineHandler::addBuffer()
+ * \brief Add buffers to the buffer pool of the camera
+ * \param[in] camera The camera
+ * \param[in] stream The stream of \a camera
+ * \param[in] buffer The buffer
+ * \param[in] fence The fence for \a buffer
+ *
+ * \context This function may only be called from the CameraManager thread.
+ */
+void PipelineHandler::addBuffer(Camera *camera,
+				const Stream *stream, FrameBuffer *buffer,
+				std::unique_ptr<Fence> &&fence)
+{
+	Camera::Private *const d = camera->_d();
+
+	auto it = d->streamData_.find(stream);
+	ASSERT(it != d->streamData_.end());
+
+	[[maybe_unused]] const auto checkUnique = [&] {
+		for (const auto &[_, data] : d->streamData_) {
+			for (const auto *b : data.buffers) {
+				if (b == buffer)
+					return false;
+			}
+		}
+
+		for (const auto &pf : d->pendingFences_) {
+			if (pf.buffer == buffer)
+				return false;
+
+			if (fence && fence->isValid()) {
+				if (pf.buffer->_d()->fence()->fd().get() == fence->fd().get())
+					return false;
+			}
+		}
+
+		return true;
+	};
+	ASSERT(checkUnique() && "buffer or fence is already present in the pool");
+
+	if (fence && fence->isValid()) {
+		buffer->_d()->setFence(std::move(fence));
+
+		auto it2 = d->pendingFences_.emplace(
+			d->pendingFences_.end(),
+			stream,
+			buffer
+		);
+
+		LOG(Pipeline, Debug)
+			<< "Waiting on fence:" << buffer->_d()->fence()->fd().get()
+			<< " for stream:" << stream << " buffer:" << buffer;
+
+		it2->notifier.activated.connect(this, [=] {
+			LOG(Pipeline, Debug)
+				<< "Activated fence:" << it2->buffer->_d()->fence()->fd().get()
+				<< " for stream:" << it2->stream << " buffer:" << it2->buffer;
+
+			std::ignore = it2->buffer->releaseFence();
+			it->second.buffers.push_back(it2->buffer);
+			d->pendingFences_.erase(it2);
+			/* Lambda is now destroy, no captured variable should be accessed. */
+		});
+	} else {
+		it->second.buffers.push_back(buffer);
+	}
+}
+
 /**
  * \brief Retrieve the absolute path to a platform configuration file
  * \param[in] subdir The pipeline handler specific subdirectory name