@@ -40,60 +40,61 @@ class Debayer;
class FrameBuffer;
class PixelFormat;
class Stream;
struct StreamConfiguration;
LOG_DECLARE_CATEGORY(SoftwareIsp)
class SoftwareIsp : public Object
{
public:
SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
ControlInfoMap *ipaControls);
~SoftwareIsp();
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);
uint32_t preferredInputStride(const PixelFormat &inputFormat, const Size &size);
int configure(const StreamConfiguration &inputCfg,
const std::vector<std::reference_wrapper<const StreamConfiguration>> &outputCfgs,
const ipa::softisp::IPAConfigInfo &configInfo,
ControlInfoMap *ipaControls);
+ Rectangle inputCrop() const;
int exportBuffers(const Stream *stream, unsigned int count,
std::vector<std::unique_ptr<FrameBuffer>> *buffers);
void processStats(const uint32_t frame, const uint32_t bufferId,
const ControlList &sensorControls);
int start();
void stop();
void queueRequest(const uint32_t frame, const ControlList &controls);
int queueBuffers(uint32_t frame, FrameBuffer *input,
const std::map<const Stream *, FrameBuffer *> &outputs);
void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output);
Signal<FrameBuffer *> inputBufferReady;
Signal<FrameBuffer *> outputBufferReady;
Signal<uint32_t, uint32_t> ispStatsReady;
Signal<uint32_t, const ControlList &> metadataReady;
Signal<const ControlList &> setSensorControls;
private:
void saveIspParams(uint32_t frame);
void setSensorCtrls(const ControlList &sensorControls);
void statsReady(uint32_t frame, uint32_t bufferId);
void inputReady(FrameBuffer *input);
void outputReady(FrameBuffer *output);
std::unique_ptr<Debayer> debayer_;
Thread ispWorkerThread_;
@@ -79,60 +79,71 @@ namespace libcamera {
* not apply any real lens shading correction.
*/
/**
* \var DebayerParams::lscLutVersion
* \brief Incremented on each \a lscLut change
*/
/**
* \class Debayer
* \brief Base debayering class
*
* Base class that provides functions for setting up the debayering process.
*/
LOG_DEFINE_CATEGORY(Debayer)
/**
* \brief Construct a Debayer object
* \param[in] cm The camera manager
*/
Debayer::Debayer(const CameraManager &cm)
: bench_(cm, "Debayer")
{
}
Debayer::~Debayer()
{
}
+/**
+ * \fn Rectangle Debayer::inputCrop() const
+ * \brief Retrieve the input region visible in the configured output
+ *
+ * The rectangle is in input-buffer coordinates and excludes any additional
+ * alignment performed for statistics sampling. It is valid after a successful
+ * configure() call.
+ *
+ * \return The configured image crop
+ */
+
/**
* \fn int Debayer::configure()
* \brief Configure the debayer object according to the passed in parameters
* \param[in] inputCfg The input configuration
* \param[in] outputCfgs The output configurations
* \param[in] ccmEnabled Whether a color correction matrix is applied
* \param[in] lscEnabled Whether lens shading correction grid is provided
*
* \return 0 on success, a negative errno on failure
*/
/**
* \fn Size Debayer::patternSize(PixelFormat inputFormat)
* \brief Get the width and height at which the bayer pattern repeats
* \param[in] inputFormat The input format
*
* Valid sizes are: 2x2, 4x2 or 4x4.
*
* \return Pattern size or an empty size for unsupported inputFormats
*/
/**
* \fn std::vector<PixelFormat> Debayer::formats(PixelFormat inputFormat)
* \brief Get the supported output formats
* \param[in] inputFormat The input format
*
* \return All supported output formats or an empty vector if there are none
*/
/**
@@ -27,60 +27,61 @@
namespace libcamera {
class CameraManager;
class FrameBuffer;
LOG_DECLARE_CATEGORY(Debayer)
class Debayer : public Object
{
public:
Debayer(const CameraManager &cm);
virtual ~Debayer() = 0;
virtual int configure(const StreamConfiguration &inputCfg,
const std::vector<std::reference_wrapper<const StreamConfiguration>> &outputCfgs,
bool ccmEnabled,
bool lscEnabled) = 0;
virtual std::vector<PixelFormat> formats(PixelFormat inputFormat) = 0;
virtual std::tuple<unsigned int, unsigned int>
strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
virtual uint32_t preferredInputStride([[maybe_unused]] const PixelFormat &inputFormat, [[maybe_unused]] const Size &size) { return 0; }
virtual void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams ¶ms) = 0;
virtual int start() { return 0; }
virtual void stop() {}
virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0;
+ virtual Rectangle inputCrop() const = 0;
virtual const SharedFD &getStatsFD() = 0;
unsigned int frameSize() { return outputConfig_.frameSize; }
Signal<FrameBuffer *> inputBufferReady;
Signal<FrameBuffer *> outputBufferReady;
struct DebayerInputConfig {
Size patternSize;
unsigned int bpp;
unsigned int stride;
std::vector<PixelFormat> outputFormats;
};
struct DebayerOutputConfig {
unsigned int stride;
unsigned int frameSize;
};
DebayerInputConfig inputConfig_;
DebayerOutputConfig outputConfig_;
Size outputSize_;
PixelFormat inputPixelFormat_;
PixelFormat outputPixelFormat_;
bool swapRedBlueGains_;
Benchmark bench_;
private:
virtual Size patternSize(PixelFormat inputFormat) = 0;
@@ -20,60 +20,64 @@
#include <libcamera/camera_manager.h>
#include "libcamera/internal/bayer_format.h"
#include "libcamera/internal/software_isp/debayer_params.h"
#include "libcamera/internal/software_isp/swstats_cpu.h"
#include "debayer.h"
namespace libcamera {
class DebayerCpuThread;
class DebayerCpu : public Debayer
{
public:
DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const CameraManager &cm);
~DebayerCpu();
int configure(const StreamConfiguration &inputCfg,
const std::vector<std::reference_wrapper<const StreamConfiguration>> &outputCfgs,
bool ccmEnabled,
bool lscEnabled) override;
Size patternSize(PixelFormat inputFormat) override;
std::vector<PixelFormat> formats(PixelFormat input) override;
std::tuple<unsigned int, unsigned int>
strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) override;
void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams ¶ms) override;
int start() override;
void stop() override;
SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) override;
+ Rectangle inputCrop() const override
+ {
+ return window_.translatedBy({ static_cast<int>(xShift_), 0 });
+ }
const SharedFD &getStatsFD() override { return stats_->getStatsFD(); }
private:
friend class DebayerCpuThread;
/**
* \brief Called to debayer 1 line of Bayer input data to output format
* \param[out] dst Pointer to the start of the output line to write
* \param[in] src The input data
*
* Input data is an array of (patternSize_.height + 1) src
* pointers each pointing to a line in the Bayer source. The middle
* element of the array will point to the actual line being processed.
* Earlier element(s) will point to the previous line(s) and later
* element(s) to the next line(s).
*
* These functions take an array of src pointers, rather than
* a single src pointer + a stride for the source, so that when the src
* is slow uncached memory it can be copied to faster memory before
* debayering. Debayering a standard 2x2 Bayer pattern requires access
* to the previous and next src lines for interpolating the missing
* colors. To allow copying the src lines only once 3 temporary buffers
* each holding a single line are used, re-using the oldest buffer for
* the next line and the pointers are swizzled so that:
* src[0] = previous-line, src[1] = currrent-line, src[2] = next-line.
* This way the 3 pointers passed to the debayer functions form
* a sliding window over the src avoiding the need to copy each
* line more than once.
*
* Similarly for bayer patterns which repeat every 4 lines, 5 src
@@ -309,61 +309,62 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,
height_ = inputCfg.size.height;
if (outputCfgs.size() != 1) {
LOG(Debayer, Error)
<< "Unsupported number of output streams: "
<< outputCfgs.size();
return -EINVAL;
}
const StreamConfiguration &outputCfg = outputCfgs[0];
SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
std::tie(outputConfig_.stride, outputConfig_.frameSize) =
strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
LOG(Debayer, Error)
<< "Invalid output size/stride: "
<< "\n " << outputCfg.size << " (" << outSizeRange << ")"
<< "\n " << outputCfg.stride << " (" << outputConfig_.stride << ")";
return -EINVAL;
}
outputPixelFormat_ = outputCfg.pixelFormat;
outputSize_ = outputCfg.size;
nativeOutputSize_ = outSizeRange.max;
/*
* The GPU scales the visible sensor crop to outputSize_. Statistics
* consume the original input buffer, so use sensor coordinates here.
*/
- stats_->setWindow(debayerCrop(inputCfg.size, nativeOutputSize_, outputSize_));
+ inputCrop_ = debayerCrop(inputCfg.size, nativeOutputSize_, outputSize_);
+ stats_->setWindow(inputCrop_);
inputBufferCount_ = inputCfg.bufferCount;
outputBufferCount_ = outputCfg.bufferCount;
return 0;
}
Size DebayerEGL::patternSize(PixelFormat inputFormat)
{
DebayerEGL::DebayerInputConfig config;
if (getInputConfig(inputFormat, config) != 0)
return {};
return config.patternSize;
}
std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)
{
DebayerEGL::DebayerInputConfig config;
if (getInputConfig(inputFormat, config) != 0)
return std::vector<PixelFormat>();
return config.outputFormats;
}
std::tuple<unsigned int, unsigned int>
DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
{
@@ -34,99 +34,101 @@ namespace libcamera {
#define DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS 4
#define DEBAYER_OPENGL_COORDS 4
class CameraManager;
class DebayerEGL : public Debayer
{
public:
DebayerEGL(std::unique_ptr<SwStatsCpu> stats, const CameraManager &cm, EGLDisplay display);
~DebayerEGL();
int configure(const StreamConfiguration &inputCfg,
const std::vector<std::reference_wrapper<const StreamConfiguration>> &outputCfgs,
bool ccmEnabled,
bool lscEnabled) override;
Size patternSize(PixelFormat inputFormat) override;
std::vector<PixelFormat> formats(PixelFormat input) override;
std::tuple<unsigned int, unsigned int> strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) override;
uint32_t preferredInputStride(const PixelFormat &inputFormat, const Size &size) override;
void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams ¶ms) override;
int start() override;
void stop() override;
const SharedFD &getStatsFD() override { return stats_->getStatsFD(); }
SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) override;
+ Rectangle inputCrop() const override { return inputCrop_; }
private:
static int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
int initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat);
int getShaderVariableLocations();
void setShaderVariableValues(eGLImage &eGLImageIn, const DebayerParams ¶ms);
int debayerGPU(FrameBuffer *input, FrameBuffer *output, const DebayerParams ¶ms, std::optional<MappedFrameBuffer> *mappedInputBuffer, std::optional<DmaSyncer> *inputBufferDmaSyncer);
eGLImage *getCachedInputFrameBuffer(FrameBuffer *input, std::optional<MappedFrameBuffer> *inMapped, std::optional<DmaSyncer> *inDmaSyncer);
eGLImage *getCachedOutputFrameBuffer(FrameBuffer *output);
/* Shader program identifiers */
GLuint vertexShaderId_ = 0;
GLuint fragmentShaderId_ = 0;
GLuint programId_ = 0;
/* Pointer to object representing input texture */
std::deque<std::pair<SharedFD, std::unique_ptr<eGLImage>>> eglImageInCache_;
std::deque<std::pair<SharedFD, std::unique_ptr<eGLImage>>> eglImageOutCache_;
unsigned int inputBufferCount_;
unsigned int outputBufferCount_;
/* LSC lookup table */
bool lscEnabled_;
std::optional<eGLImage> eglImageLscLookup_;
std::optional<uint64_t> lscLutVersion_;
/* Shader parameters */
float firstRed_x_;
float firstRed_y_;
GLint attributeVertex_;
GLint attributeTexture_;
GLint textureUniformStep_;
GLint textureUniformSize_;
GLint textureUniformStrideFactor_;
GLint textureUniformBayerFirstRed_;
GLint textureUniformProjMatrix_;
GLint textureUniformBayerDataIn_;
/* Per-frame AWB gains */
GLint awbUniformDataIn_;
/* Lens shading correction */
GLint textureUniformLsc_;
/* Represent per-frame CCM as a uniform vector of floats 3 x 3 */
GLint ccmUniformDataIn_;
/* Black Level compensation */
GLint blackLevelUniformDataIn_;
/* Gamma */
GLint gammaUniformDataIn_;
/* Contrast */
GLint contrastExpUniformDataIn_;
Size nativeOutputSize_;
+ Rectangle inputCrop_;
std::unique_ptr<SwStatsCpu> stats_;
eGL egl_;
uint32_t width_;
uint32_t height_;
GLint glFormat_;
unsigned int bytesPerPixel_;
uint32_t shaderStridePixels_;
};
} /* namespace libcamera */
@@ -279,60 +279,74 @@ uint32_t SoftwareIsp::preferredInputStride(const PixelFormat &inputFormat, const
* \brief Configure the SoftwareIsp object according to the passed in parameters
* \param[in] inputCfg The input configuration
* \param[in] outputCfgs The output configurations
* \param[in] configInfo The IPA configuration data, received from the pipeline handler
* \param[out] ipaControls The IPA controls to update
* \return 0 on success, a negative errno on failure
*/
int SoftwareIsp::configure(const StreamConfiguration &inputCfg,
const std::vector<std::reference_wrapper<const StreamConfiguration>> &outputCfgs,
const ipa::softisp::IPAConfigInfo &configInfo,
ControlInfoMap *ipaControls)
{
ASSERT(ipa_ && debayer_);
int ret = ipa_->configure(configInfo, ipaControls);
if (ret < 0)
return ret;
ret = debayer_->configure(inputCfg, outputCfgs, ccmEnabled_, lscEnabled_);
if (ret < 0)
return ret;
LOG(SoftwareIsp, Info)
<< "Input " << inputCfg.size
<< "-" << BayerFormat::fromPixelFormat(inputCfg.pixelFormat)
<< " stride " << inputCfg.stride;
return 0;
}
+/**
+ * \brief Retrieve the input region used to produce the configured output
+ *
+ * The rectangle is expressed in input-buffer pixels, before any Bayer
+ * alignment applied by the statistics engine. It is valid after configure()
+ * succeeds and describes the selected CPU or EGL debayer backend.
+ *
+ * \return The configured input crop
+ */
+Rectangle SoftwareIsp::inputCrop() const
+{
+ return debayer_->inputCrop();
+}
+
/**
* \brief Export the buffers from the Software ISP
* \param[in] stream Output stream exporting the buffers
* \param[in] count Number of buffers to allocate
* \param[out] buffers Vector to store the allocated buffers
* \return The number of allocated buffers on success or a negative error code
* otherwise
*/
int SoftwareIsp::exportBuffers(const Stream *stream, unsigned int count,
std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
ASSERT(debayer_ != nullptr);
/* single output for now */
if (stream == nullptr)
return -EINVAL;
return dmaHeap_.exportBuffers(count, { debayer_->frameSize() }, buffers);
}
/**
* \brief Queue a request and process the control list from the application
* \param[in] frame The number of the frame which will be processed next
* \param[in] controls The controls for the \a frame
*/
void SoftwareIsp::queueRequest(const uint32_t frame, const ControlList &controls)
{
ipa_->queueRequest(frame, controls);
}