[libcamera-devel,09/11] libcamera: software_isp: add Simple SoftwareIsp implementation
diff mbox series

Message ID 20231214121350.206015-10-hdegoede@redhat.com
State Superseded
Headers show
Series
  • libcamera: introduce Software ISP and Software IPA
Related show

Commit Message

Hans de Goede Dec. 14, 2023, 12:13 p.m. UTC
From: Andrey Konovalov <andrey.konovalov@linaro.org>

The implementation of SoftwareIsp handles creation of Soft IPA
and interactions with it, so that the pipeline handler wouldn't
need to care about the Soft IPA.

Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 include/libcamera/internal/meson.build        |   1 +
 .../internal/software_isp/meson.build         |   1 +
 .../internal/software_isp/swisp_simple.h      |  67 ++++++
 src/libcamera/meson.build                     |   1 +
 src/libcamera/software_isp/meson.build        |  19 ++
 src/libcamera/software_isp/swisp_simple.cpp   | 219 ++++++++++++++++++
 6 files changed, 308 insertions(+)
 create mode 100644 include/libcamera/internal/software_isp/swisp_simple.h
 create mode 100644 src/libcamera/software_isp/swisp_simple.cpp

Patch
diff mbox series

diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index b780777c..eeae801c 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -50,3 +50,4 @@  libcamera_internal_headers = files([
 ])
 
 subdir('converter')
+subdir('software_isp')
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
index b5a0d737..cf21ace5 100644
--- a/include/libcamera/internal/software_isp/meson.build
+++ b/include/libcamera/internal/software_isp/meson.build
@@ -4,6 +4,7 @@  libcamera_internal_headers += files([
     'debayer.h',
     'debayer_cpu.h',
     'debayer_params.h',
+    'swisp_simple.h',
     'swisp_stats.h',
     'swstats.h',
     'swstats_cpu.h',
diff --git a/include/libcamera/internal/software_isp/swisp_simple.h b/include/libcamera/internal/software_isp/swisp_simple.h
new file mode 100644
index 00000000..ee66e193
--- /dev/null
+++ b/include/libcamera/internal/software_isp/swisp_simple.h
@@ -0,0 +1,67 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ *
+ * swisp_simple.h - Simple software ISP implementation
+ */
+
+#pragma once
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/thread.h>
+
+#include <libcamera/pixel_format.h>
+
+#include <libcamera/ipa/soft_ipa_interface.h>
+#include <libcamera/ipa/soft_ipa_proxy.h>
+
+#include "libcamera/internal/software_isp.h"
+#include "libcamera/internal/software_isp/debayer_cpu.h"
+
+namespace libcamera {
+
+class SwIspSimple : public SoftwareIsp
+{
+public:
+	SwIspSimple(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
+	~SwIspSimple() {}
+
+	int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
+	bool isValid() const;
+
+	std::vector<PixelFormat> formats(PixelFormat input);
+	SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
+
+	std::tuple<unsigned int, unsigned int>
+	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
+
+	int configure(const StreamConfiguration &inputCfg,
+		      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs);
+	int exportBuffers(unsigned int output, unsigned int count,
+			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
+	void processStats(const ControlList &sensorControls);
+
+	int start();
+	void stop();
+
+	int queueBuffers(FrameBuffer *input,
+			 const std::map<unsigned int, FrameBuffer *> &outputs);
+
+	Signal<const ControlList &> &getSignalSetSensorControls();
+
+	void process(FrameBuffer *input, FrameBuffer *output);
+
+private:
+	void statsReady(int dummy);
+	void inputReady(FrameBuffer *input);
+	void outputReady(FrameBuffer *output);
+
+	std::unique_ptr<DebayerCpu> debayer_;
+	Thread ispWorkerThread_;
+	/* FIXME debayerParams should come from the IPA */
+	DebayerParams debayerParams_;
+
+	std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index b3606969..e758ac9c 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -70,6 +70,7 @@  subdir('converter')
 subdir('ipa')
 subdir('pipeline')
 subdir('proxy')
+subdir('software_isp')
 
 null_dep = dependency('', required : false)
 
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
index 6d7a44d7..9464f2fd 100644
--- a/src/libcamera/software_isp/meson.build
+++ b/src/libcamera/software_isp/meson.build
@@ -6,3 +6,22 @@  libcamera_sources += files([
 	'swstats.cpp',
 	'swstats_cpu.cpp',
 ])
+
+# Software ISP is enabled for 'simple' pipeline handler.
+# The 'pipelines' option value should be
+# 'simple/<Software ISP implementation>' e.g.:
+#   -Dpipelines=simple/simple
+# The source file should be named swisp_<Software ISP implementation>.cpp,
+# e.g. 'swisp_simple.cpp'.
+
+foreach pipeline : pipelines
+    pipeline = pipeline.split('/')
+    if pipeline.length() == 2 and pipeline[0] == 'simple'
+        libcamera_sources += files([
+            'swisp_' + pipeline[1] + '.cpp',
+        ])
+        # the 'break' below can be removed if/when multiple
+        # Software ISP implementations are allowed in single build
+        break
+    endif
+endforeach
diff --git a/src/libcamera/software_isp/swisp_simple.cpp b/src/libcamera/software_isp/swisp_simple.cpp
new file mode 100644
index 00000000..ff05b6fe
--- /dev/null
+++ b/src/libcamera/software_isp/swisp_simple.cpp
@@ -0,0 +1,219 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ *
+ * swisp_simple.cpp - Simple software ISP implementation
+ */
+
+#include "libcamera/internal/software_isp/swisp_simple.h"
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libcamera/formats.h>
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/framebuffer.h"
+#include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/mapped_framebuffer.h"
+
+namespace libcamera {
+
+SwIspSimple::SwIspSimple(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
+	: SoftwareIsp(pipe, sensorControls), debayer_(nullptr), debayerParams_{256, 256, 0.5f}
+{
+	std::unique_ptr<SwStatsCpu> stats;
+
+	stats = std::make_unique<SwStatsCpu>();
+	if (!stats) {
+		LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
+		return;
+	}
+
+	stats->statsReady.connect(this, &SwIspSimple::statsReady);
+
+	debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
+	if (!debayer_) {
+		LOG(SoftwareIsp, Error) << "Failed to create DebayerCpu object";
+		return;
+	}
+
+	debayer_->inputBufferReady.connect(this, &SwIspSimple::inputReady);
+	debayer_->outputBufferReady.connect(this, &SwIspSimple::outputReady);
+
+	ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
+	if (!ipa_) {
+		LOG(SoftwareIsp, Error)
+			<< "Creating IPA for software ISP failed";
+		debayer_.reset();
+		return;
+	}
+
+	int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
+			     debayer_->getStatsFD(),
+			     sensorControls);
+	if (ret) {
+		LOG(SoftwareIsp, Error) << "IPA init failed";
+		debayer_.reset();
+		return;
+	}
+
+	ipa_->configure(sensorControls);
+
+	debayer_->moveToThread(&ispWorkerThread_);
+}
+
+void SwIspSimple::processStats(const ControlList &sensorControls)
+{
+	ASSERT(ipa_);
+	ipa_->processStats(sensorControls);
+}
+
+Signal<const ControlList &> &SwIspSimple::getSignalSetSensorControls()
+{
+	ASSERT(ipa_);
+	return ipa_->setSensorControls;
+}
+
+bool SwIspSimple::isValid() const
+{
+	return !!debayer_;
+}
+
+std::vector<PixelFormat> SwIspSimple::formats(PixelFormat inputFormat)
+{
+	ASSERT(debayer_ != nullptr);
+
+	return debayer_->formats(inputFormat);
+}
+
+SizeRange SwIspSimple::sizes(PixelFormat inputFormat, const Size &inputSize)
+{
+	ASSERT(debayer_ != nullptr);
+
+	return debayer_->sizes(inputFormat, inputSize);
+}
+
+std::tuple<unsigned int, unsigned int>
+SwIspSimple::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
+{
+	ASSERT(debayer_ != nullptr);
+
+	return debayer_->strideAndFrameSize(outputFormat, size);
+}
+
+int SwIspSimple::configure(const StreamConfiguration &inputCfg,
+			   const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)
+{
+	ASSERT(debayer_ != nullptr);
+
+	return debayer_->configure(inputCfg, outputCfgs);
+}
+
+int SwIspSimple::exportBuffers(unsigned int output, unsigned int count,
+			       std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+	ASSERT(debayer_ != nullptr);
+
+	/* single output for now */
+	if (output >= 1)
+		return -EINVAL;
+
+	/* TODO: allocate from dma_heap; memfd buffs aren't allowed in FrameBuffer */
+	for (unsigned int i = 0; i < count; i++) {
+		std::string name = "frame-" + std::to_string(i);
+
+		const int ispFd = memfd_create(name.c_str(), 0);
+		int ret = ftruncate(ispFd, debayer_->frameSize());
+		if (ret < 0) {
+			LOG(SoftwareIsp, Error)
+				<< "ftruncate() for memfd failed "
+				<< strerror(-ret);
+			return ret;
+		}
+
+		FrameBuffer::Plane outPlane;
+		outPlane.fd = SharedFD(std::move(ispFd));
+		outPlane.offset = 0;
+		outPlane.length = debayer_->frameSize();
+
+		std::vector<FrameBuffer::Plane> planes{ outPlane };
+		buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));
+	}
+
+	return count;
+}
+
+int SwIspSimple::queueBuffers(FrameBuffer *input,
+			      const std::map<unsigned int, FrameBuffer *> &outputs)
+{
+	unsigned int mask = 0;
+
+	/*
+	 * Validate the outputs as a sanity check: at least one output is
+	 * required, all outputs must reference a valid stream and no two
+	 * outputs can reference the same stream.
+	 */
+	if (outputs.empty())
+		return -EINVAL;
+
+	for (auto [index, buffer] : outputs) {
+		if (!buffer)
+			return -EINVAL;
+		if (index >= 1) /* only single stream atm */
+			return -EINVAL;
+		if (mask & (1 << index))
+			return -EINVAL;
+
+		mask |= 1 << index;
+	}
+
+	process(input, outputs.at(0));
+
+	return 0;
+}
+
+int SwIspSimple::start()
+{
+	int ret = ipa_->start();
+	if (ret)
+		return ret;
+
+	ispWorkerThread_.start();
+	return 0;
+}
+
+void SwIspSimple::stop()
+{
+	ispWorkerThread_.exit();
+	ispWorkerThread_.wait();
+
+	ipa_->stop();
+}
+
+void SwIspSimple::process(FrameBuffer *input, FrameBuffer *output)
+{
+	debayer_->invokeMethod(&DebayerCpu::process,
+			       ConnectionTypeQueued, input, output, debayerParams_);
+}
+
+void SwIspSimple::statsReady(int dummy)
+{
+	ispStatsReady.emit(dummy);
+}
+
+void SwIspSimple::inputReady(FrameBuffer *input)
+{
+	inputBufferReady.emit(input);
+}
+
+void SwIspSimple::outputReady(FrameBuffer *output)
+{
+	outputBufferReady.emit(output);
+}
+
+REGISTER_SOFTWAREISP(SwIspSimple)
+
+} /* namespace libcamera */