[v16,6/6] libcamera: software_isp: debayer_egl: Add LSC support
diff mbox series

Message ID 20261022094048.20277-7-mzamazal@redhat.com
State New
Headers show
Series
  • LSC for SoftISP simple pipeline
Related show

Commit Message

Milan Zamazal Oct. 22, 2026, 9:40 a.m. UTC
From: Xander Pronk <xander.c.pronk@gmail.com>

Add support for passing the LSC table from debayerParams to the shaders.

We use 8-bit texture with the original UQ<2, 6> values to avoid trouble
with floats.  32-bit floats don't work in my environment and 16-bit
floats may not be supported in all OpenGL environments.

When LSC is disabled by the runtime control, we provide a constant 1.0
correction table, i.e. a no-op table.  This is preferred to having a
runtime flag, to not disturb GPU computation with runtime switches,
assuming that disabling LSC in runtime is not a common use case.

The LSC texture is created once and then updated.  The texture creation
cannot be done in `configure', because of a different thread.

Co-developed-by: Rick ten Wolde <rick_libcamera@wolde.info>
Signed-off-by: Rick ten Wolde <rick_libcamera@wolde.info>
Signed-off-by: Xander Pronk <xander.c.pronk@gmail.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 include/libcamera/internal/egl.h              |  4 +--
 .../internal/software_isp/debayer_params.h    |  2 ++
 src/libcamera/egl.cpp                         |  4 +--
 src/libcamera/software_isp/debayer.cpp        |  5 ++++
 src/libcamera/software_isp/debayer_egl.cpp    | 30 ++++++++++++++++++-
 src/libcamera/software_isp/debayer_egl.h      |  7 +++++
 6 files changed, 47 insertions(+), 5 deletions(-)

Comments

Kieran Bingham Oct. 22, 2026, 9:57 a.m. UTC | #1
Quoting Milan Zamazal (2026-10-22 10:40:48)
> From: Xander Pronk <xander.c.pronk@gmail.com>
> 
> Add support for passing the LSC table from debayerParams to the shaders.
> 
> We use 8-bit texture with the original UQ<2, 6> values to avoid trouble
> with floats.  32-bit floats don't work in my environment and 16-bit
> floats may not be supported in all OpenGL environments.
> 
> When LSC is disabled by the runtime control, we provide a constant 1.0
> correction table, i.e. a no-op table.  This is preferred to having a
> runtime flag, to not disturb GPU computation with runtime switches,
> assuming that disabling LSC in runtime is not a common use case.
> 
> The LSC texture is created once and then updated.  The texture creation
> cannot be done in `configure', because of a different thread.
> 
> Co-developed-by: Rick ten Wolde <rick_libcamera@wolde.info>
> Signed-off-by: Rick ten Wolde <rick_libcamera@wolde.info>
> Signed-off-by: Xander Pronk <xander.c.pronk@gmail.com>
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> ---
>  include/libcamera/internal/egl.h              |  4 +--
>  .../internal/software_isp/debayer_params.h    |  2 ++
>  src/libcamera/egl.cpp                         |  4 +--
>  src/libcamera/software_isp/debayer.cpp        |  5 ++++
>  src/libcamera/software_isp/debayer_egl.cpp    | 30 ++++++++++++++++++-
>  src/libcamera/software_isp/debayer_egl.h      |  7 +++++
>  6 files changed, 47 insertions(+), 5 deletions(-)
> 
> diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h
> index dc4c6fdb6..d6fb6fe06 100644
> --- a/include/libcamera/internal/egl.h
> +++ b/include/libcamera/internal/egl.h
> @@ -111,8 +111,8 @@ public:
>  
>         int createInputDMABufTexture2D(eGLImage &eglImage, int fd);
>         int createOutputDMABufTexture2D(eGLImage &eglImage, int fd);
> -       void createTexture2D(eGLImage &eglImage, void *data, GLint filter);
> -       void updateTexture2D(eGLImage &eglImage, void *data);
> +       void createTexture2D(eGLImage &eglImage, const void *data, GLint filter);
> +       void updateTexture2D(eGLImage &eglImage, const void *data);
>         void createOutputTexture2D(eGLImage &eglImage);
>  
>         int attachTextureToFBO(eGLImage &eglImage);
> diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
> index 2ae1614b3..300c3f2d6 100644
> --- a/include/libcamera/internal/software_isp/debayer_params.h
> +++ b/include/libcamera/internal/software_isp/debayer_params.h
> @@ -34,6 +34,8 @@ struct DebayerParams {
>          **/
>         static constexpr unsigned int kLscGridSize = 17;
>         static constexpr unsigned int kLscValuesPerCell = 4;
> +       static constexpr unsigned int kLscBytesPerCell =
> +               kLscValuesPerCell * sizeof(uint8_t);
>         using LscLookupTable =
>                 std::array<uint8_t, kLscGridSize * kLscGridSize * kLscValuesPerCell>;
>         static constexpr auto identityLscLut = [] {
> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
> index 4c89eea05..b3d3b9abb 100644
> --- a/src/libcamera/egl.cpp
> +++ b/src/libcamera/egl.cpp
> @@ -286,7 +286,7 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)
>   * is useful for uploading static data like lookup tables or uniform color
>   * matrices to the GPU.
>   */
> -void eGL::createTexture2D(eGLImage &eglImage, void *data, GLint filter)
> +void eGL::createTexture2D(eGLImage &eglImage, const void *data, GLint filter)
>  {
>         assertThread();
>  
> @@ -339,7 +339,7 @@ EGLDisplay eGL::probeDisplay()
>   *
>   * Updates a 2D texture in VRAM.
>   */
> -void eGL::updateTexture2D(eGLImage &eglImage, void *data)
> +void eGL::updateTexture2D(eGLImage &eglImage, const void *data)
>  {
>         assertThread();
>  
> diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
> index 8295b8fc2..4f6659000 100644
> --- a/src/libcamera/software_isp/debayer.cpp
> +++ b/src/libcamera/software_isp/debayer.cpp
> @@ -53,6 +53,11 @@ namespace libcamera {
>   * \brief Number of pixel values per each of the lens shading grid areas
>   */
>  
> +/**
> + * \var DebayerParams::kLscBytesPerCell
> + * \brief Number of bytes per each of the lens shading grid areas
> + */
> +
>  /**
>   * \typedef DebayerParams::LscLookupTable
>   * \brief Lookup table for lens shading correction
> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
> index bae63e076..33933b11d 100644
> --- a/src/libcamera/software_isp/debayer_egl.cpp
> +++ b/src/libcamera/software_isp/debayer_egl.cpp
> @@ -115,6 +115,8 @@ int DebayerEGL::getShaderVariableLocations(void)
>         textureUniformBayerFirstRed_ = glGetUniformLocation(programId_, "tex_bayer_first_red");
>         textureUniformProjMatrix_ = glGetUniformLocation(programId_, "proj_matrix");
>  
> +       textureUniformLsc_ = glGetUniformLocation(programId_, "lsc_tex");
> +
>         LOG(Debayer, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
>                             << " tex_y " << textureUniformBayerDataIn_
>                             << " awb " << awbUniformDataIn_
> @@ -126,7 +128,8 @@ int DebayerEGL::getShaderVariableLocations(void)
>                             << " tex_size " << textureUniformSize_
>                             << " stride_factor " << textureUniformStrideFactor_
>                             << " tex_bayer_first_red " << textureUniformBayerFirstRed_
> -                           << " proj_matrix " << textureUniformProjMatrix_;
> +                           << " proj_matrix " << textureUniformProjMatrix_
> +                           << " tex_lsc " << textureUniformLsc_;
>         return 0;
>  }
>  
> @@ -143,6 +146,9 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm
>         /* Specify GL_OES_EGL_image_external */
>         egl_.pushEnv(shaderEnv, "#extension GL_OES_EGL_image_external: enable");
>  
> +       if (lscEnabled_)
> +               egl_.pushEnv(shaderEnv, "#define APPLY_LSC");
> +
>         /*
>          * Tell shaders how to re-order output taking account of how the pixels
>          * are actually stored by EGL.
> @@ -472,6 +478,13 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara
>         glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm.data().data());
>         LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix;
>  
> +       if (lscEnabled_ && params.lscLutVersion != lscLutVersion_) {
> +               egl_.updateTexture2D(*eglImageLscLookup_, params.lscLut.data());
> +               lscLutVersion_ = params.lscLutVersion;
> +               glUniform1i(textureUniformLsc_, eglImageLscLookup_->texture_unit_uniform_id_);
> +               LOG(Debayer, Debug) << "LSC texture updated to version " << lscLutVersion_.value_or(-1);
> +       }
> +
>         /*
>          * 0 = Red, 1 = Green, 2 = Blue
>          */
> @@ -678,6 +691,18 @@ int DebayerEGL::start()
>         if (initBayerShaders(inputPixelFormat_, outputPixelFormat_))
>                 return -EINVAL;
>  
> +       if (lscEnabled_) {
> +               constexpr unsigned int gridSize = DebayerParams::kLscGridSize;
> +               constexpr unsigned int stride = gridSize * DebayerParams::kLscBytesPerCell;
> +               eglImageLscLookup_.emplace(GL_RGBA,
> +                                          gridSize,
> +                                          gridSize,
> +                                          stride,
> +                                          GL_TEXTURE2,
> +                                          2);
> +               egl_.createTexture2D(*eglImageLscLookup_, nullptr, GL_LINEAR);
> +       }
> +
>         return 0;
>  }
>  
> @@ -686,6 +711,9 @@ void DebayerEGL::stop()
>         eglImageOutCache_.clear();
>         eglImageInCache_.clear();
>  
> +       eglImageLscLookup_.reset();
> +       lscLutVersion_.reset();
> +
>         if (programId_)
>                 glDeleteProgram(programId_);
>  
> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
> index a64dc925a..cbbc6a450 100644
> --- a/src/libcamera/software_isp/debayer_egl.h
> +++ b/src/libcamera/software_isp/debayer_egl.h
> @@ -83,7 +83,11 @@ private:
>         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_;
> @@ -100,6 +104,9 @@ private:
>         /* 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_;
>  
> -- 
> 2.56.0
>

Patch
diff mbox series

diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h
index dc4c6fdb6..d6fb6fe06 100644
--- a/include/libcamera/internal/egl.h
+++ b/include/libcamera/internal/egl.h
@@ -111,8 +111,8 @@  public:
 
 	int createInputDMABufTexture2D(eGLImage &eglImage, int fd);
 	int createOutputDMABufTexture2D(eGLImage &eglImage, int fd);
-	void createTexture2D(eGLImage &eglImage, void *data, GLint filter);
-	void updateTexture2D(eGLImage &eglImage, void *data);
+	void createTexture2D(eGLImage &eglImage, const void *data, GLint filter);
+	void updateTexture2D(eGLImage &eglImage, const void *data);
 	void createOutputTexture2D(eGLImage &eglImage);
 
 	int attachTextureToFBO(eGLImage &eglImage);
diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
index 2ae1614b3..300c3f2d6 100644
--- a/include/libcamera/internal/software_isp/debayer_params.h
+++ b/include/libcamera/internal/software_isp/debayer_params.h
@@ -34,6 +34,8 @@  struct DebayerParams {
 	 **/
 	static constexpr unsigned int kLscGridSize = 17;
 	static constexpr unsigned int kLscValuesPerCell = 4;
+	static constexpr unsigned int kLscBytesPerCell =
+		kLscValuesPerCell * sizeof(uint8_t);
 	using LscLookupTable =
 		std::array<uint8_t, kLscGridSize * kLscGridSize * kLscValuesPerCell>;
 	static constexpr auto identityLscLut = [] {
diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
index 4c89eea05..b3d3b9abb 100644
--- a/src/libcamera/egl.cpp
+++ b/src/libcamera/egl.cpp
@@ -286,7 +286,7 @@  int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)
  * is useful for uploading static data like lookup tables or uniform color
  * matrices to the GPU.
  */
-void eGL::createTexture2D(eGLImage &eglImage, void *data, GLint filter)
+void eGL::createTexture2D(eGLImage &eglImage, const void *data, GLint filter)
 {
 	assertThread();
 
@@ -339,7 +339,7 @@  EGLDisplay eGL::probeDisplay()
  *
  * Updates a 2D texture in VRAM.
  */
-void eGL::updateTexture2D(eGLImage &eglImage, void *data)
+void eGL::updateTexture2D(eGLImage &eglImage, const void *data)
 {
 	assertThread();
 
diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
index 8295b8fc2..4f6659000 100644
--- a/src/libcamera/software_isp/debayer.cpp
+++ b/src/libcamera/software_isp/debayer.cpp
@@ -53,6 +53,11 @@  namespace libcamera {
  * \brief Number of pixel values per each of the lens shading grid areas
  */
 
+/**
+ * \var DebayerParams::kLscBytesPerCell
+ * \brief Number of bytes per each of the lens shading grid areas
+ */
+
 /**
  * \typedef DebayerParams::LscLookupTable
  * \brief Lookup table for lens shading correction
diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index bae63e076..33933b11d 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -115,6 +115,8 @@  int DebayerEGL::getShaderVariableLocations(void)
 	textureUniformBayerFirstRed_ = glGetUniformLocation(programId_, "tex_bayer_first_red");
 	textureUniformProjMatrix_ = glGetUniformLocation(programId_, "proj_matrix");
 
+	textureUniformLsc_ = glGetUniformLocation(programId_, "lsc_tex");
+
 	LOG(Debayer, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
 			    << " tex_y " << textureUniformBayerDataIn_
 			    << " awb " << awbUniformDataIn_
@@ -126,7 +128,8 @@  int DebayerEGL::getShaderVariableLocations(void)
 			    << " tex_size " << textureUniformSize_
 			    << " stride_factor " << textureUniformStrideFactor_
 			    << " tex_bayer_first_red " << textureUniformBayerFirstRed_
-			    << " proj_matrix " << textureUniformProjMatrix_;
+			    << " proj_matrix " << textureUniformProjMatrix_
+			    << " tex_lsc " << textureUniformLsc_;
 	return 0;
 }
 
@@ -143,6 +146,9 @@  int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm
 	/* Specify GL_OES_EGL_image_external */
 	egl_.pushEnv(shaderEnv, "#extension GL_OES_EGL_image_external: enable");
 
+	if (lscEnabled_)
+		egl_.pushEnv(shaderEnv, "#define APPLY_LSC");
+
 	/*
 	 * Tell shaders how to re-order output taking account of how the pixels
 	 * are actually stored by EGL.
@@ -472,6 +478,13 @@  void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara
 	glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm.data().data());
 	LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix;
 
+	if (lscEnabled_ && params.lscLutVersion != lscLutVersion_) {
+		egl_.updateTexture2D(*eglImageLscLookup_, params.lscLut.data());
+		lscLutVersion_ = params.lscLutVersion;
+		glUniform1i(textureUniformLsc_, eglImageLscLookup_->texture_unit_uniform_id_);
+		LOG(Debayer, Debug) << "LSC texture updated to version " << lscLutVersion_.value_or(-1);
+	}
+
 	/*
 	 * 0 = Red, 1 = Green, 2 = Blue
 	 */
@@ -678,6 +691,18 @@  int DebayerEGL::start()
 	if (initBayerShaders(inputPixelFormat_, outputPixelFormat_))
 		return -EINVAL;
 
+	if (lscEnabled_) {
+		constexpr unsigned int gridSize = DebayerParams::kLscGridSize;
+		constexpr unsigned int stride = gridSize * DebayerParams::kLscBytesPerCell;
+		eglImageLscLookup_.emplace(GL_RGBA,
+					   gridSize,
+					   gridSize,
+					   stride,
+					   GL_TEXTURE2,
+					   2);
+		egl_.createTexture2D(*eglImageLscLookup_, nullptr, GL_LINEAR);
+	}
+
 	return 0;
 }
 
@@ -686,6 +711,9 @@  void DebayerEGL::stop()
 	eglImageOutCache_.clear();
 	eglImageInCache_.clear();
 
+	eglImageLscLookup_.reset();
+	lscLutVersion_.reset();
+
 	if (programId_)
 		glDeleteProgram(programId_);
 
diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
index a64dc925a..cbbc6a450 100644
--- a/src/libcamera/software_isp/debayer_egl.h
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -83,7 +83,11 @@  private:
 	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_;
@@ -100,6 +104,9 @@  private:
 	/* 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_;