| Message ID | 20260716222515.251464-7-mzamazal@redhat.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
On 16/07/2026 23:25, Milan Zamazal wrote: > From: Xander Pronk <xander.c.pronk@gmail.com> > > Add support for passing the LSC table from debayerParams to the shaders. > > The LSC table values are floats, we must add `type' parameter to > createTexture2D to support this. Moreover, we use 16-bit internal > format for the values, primarily because this works for me, unlike > 32-bit floats. > > 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. > > 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> > --- > include/libcamera/internal/egl.h | 6 +++- > .../internal/software_isp/debayer_params.h | 2 ++ > src/ipa/simple/algorithms/lsc.cpp | 11 ++++--- > src/libcamera/egl.cpp | 10 +++++-- > src/libcamera/software_isp/debayer.cpp | 5 ++++ > src/libcamera/software_isp/debayer_egl.cpp | 29 +++++++++++++++++-- > src/libcamera/software_isp/debayer_egl.h | 6 ++++ > 7 files changed, 60 insertions(+), 9 deletions(-) > > diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h > index 030c813a5..37da2b18a 100644 > --- a/include/libcamera/internal/egl.h > +++ b/include/libcamera/internal/egl.h > @@ -107,7 +107,11 @@ public: > > int createInputDMABufTexture2D(eGLImage &eglImage, int fd); > int createOutputDMABufTexture2D(eGLImage &eglImage, int fd); > - void createTexture2D(eGLImage &eglImage, void *data, GLint filter); > + void createTexture2D(eGLImage &eglImage, > + GLint internalFormat, > + GLenum type, > + const void *data, > + GLint filter); > > void pushEnv(std::vector<std::string> &shaderEnv, const char *str); > void makeCurrent(); > diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h > index e3e01bc04..76825e6c8 100644 > --- a/include/libcamera/internal/software_isp/debayer_params.h > +++ b/include/libcamera/internal/software_isp/debayer_params.h > @@ -29,6 +29,8 @@ struct DebayerParams { > > static constexpr unsigned int kLscGridSize = 16; > static constexpr unsigned int kLscValuesPerCell = 3; > + static constexpr unsigned int kLscBytesPerCell = > + kLscValuesPerCell * sizeof(float); > using LscLookupTable = > std::array<float, kLscGridSize * kLscGridSize * kLscValuesPerCell>; > LscLookupTable lscLut{}; > diff --git a/src/ipa/simple/algorithms/lsc.cpp b/src/ipa/simple/algorithms/lsc.cpp > index c531937c7..51c5b63d5 100644 > --- a/src/ipa/simple/algorithms/lsc.cpp > +++ b/src/ipa/simple/algorithms/lsc.cpp > @@ -46,13 +46,16 @@ void Lsc::prepare([[maybe_unused]] IPAContext &context, > IPAFrameContext &frameContext, > DebayerParams *params) > { > - params->lscEnabled = frameContext.lsc.enabled; > - > unsigned int ct = frameContext.awb.colourTemperature; > constexpr unsigned int minTemperatureChange = 100; > > - if (!frameContext.lsc.enabled || > - utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) > + if (!frameContext.lsc.enabled) { > + params->lscLut.fill(1.0); > + lastAppliedCt_ = 0; > + return; > + } > + > + if (utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) > return; > > const lsc::Components<uint8_t> &set = lscAlgo_.interpolateComponents(ct); > diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp > index aeac5d313..c2a158ce7 100644 > --- a/src/libcamera/egl.cpp > +++ b/src/libcamera/egl.cpp > @@ -241,6 +241,8 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd) > /** > * \brief Create a 2D texture from a memory buffer > * \param[in,out] eglImage EGL image to associate with the texture > + * \param[in] internalFormat OpenGL internal storage format (e.g., GL_RGB8, GL_RGBA8) > + * \param[in] type OpenGL pixel data type (e.g., GL_UNSIGNED_BYTE, GL_FLOAT) > * \param[in] data Pointer to pixel data, or nullptr for uninitialised texture > * \param[in] filter GL texture filter setting > * > @@ -249,7 +251,11 @@ 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, > + GLint internalFormat, > + GLenum type, > + const void *data, > + GLint filter) > { > ASSERT(tid_ == Thread::currentId()); > > @@ -257,7 +263,7 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data, GLint filter) > glBindTexture(GL_TEXTURE_2D, eglImage.texture_); > > // Generate texture, bind, associate image to texture, configure, unbind > - glTexImage2D(GL_TEXTURE_2D, 0, eglImage.format_, eglImage.width_, eglImage.height_, 0, eglImage.format_, GL_UNSIGNED_BYTE, data); > + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, eglImage.width_, eglImage.height_, 0, eglImage.format_, type, data); > > // Nearest filtering > glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); > diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp > index 5ae9f032f..70c0e3336 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 562e9fc7a..0fdf0e6e8 100644 > --- a/src/libcamera/software_isp/debayer_egl.cpp > +++ b/src/libcamera/software_isp/debayer_egl.cpp > @@ -128,6 +128,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_ > @@ -139,7 +141,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; > } > > @@ -156,6 +159,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. > @@ -348,6 +354,18 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg, > */ > stats_->setWindow(Rectangle(window_.size())); > > + if (lscEnabled_) { > + constexpr unsigned int gridSize = DebayerParams::kLscGridSize; > + const unsigned int stride = gridSize * DebayerParams::kLscBytesPerCell; > + eglImageLscLookup_ = > + std::make_unique<eGLImage>(GL_RGB, > + gridSize, > + gridSize, > + stride, > + GL_TEXTURE2, > + 2); > + } > + > return 0; > } > > @@ -483,6 +501,12 @@ void DebayerEGL::setShaderVariableValues(const DebayerParams ¶ms) > glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_TRUE, params.combinedMatrix.data().data()); > LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; > > + if (lscEnabled_) { > + egl_.createTexture2D(*eglImageLscLookup_, GL_RGB16F, GL_FLOAT, > + params.lscLut.data(), GL_LINEAR); As we are discussing in the multi-pass thread, GL_RGB16F is dependent on the optional GL_EXT_color_buffer_half_float https://registry.khronos.org/OpenGL/extensions/EXT/EXT_color_buffer_half_float.txt I think it should be pretty easy to disjunct on that as a flag in the base class - a flag I plan to add for SSBO stats but also perfectly happy for you to add. Half float linear filtering requires OES_texture_half_float_linear glGetString(GL_EXTENSIONS) | grep OES_texture_half_float_linear | debayer_egl{ bool _has_OES_texture_half_float_linear;} or whatever if (_has_OES_texture_half_float_linear) GL_RGB16F; Could we repreent the LSC data as uint8 natively ? I mean without storing it all as float and then converting to uint8 on each LSC application cycle ? > + glUniform1i(textureUniformLsc_, eglImageLscLookup_->texture_unit_uniform_id_); > + } > + > /* > * 0 = Red, 1 = Green, 2 = Blue > */ > @@ -530,7 +554,8 @@ int DebayerEGL::debayerGPU(FrameBuffer *input, FrameBuffer *output, const Debaye > LOG(Debayer, Error) << "mmap-ing buffer(s) failed"; > return -ENODEV; > } > - egl_.createTexture2D(*eglImageBayerIn_, inMapped->value().planes()[0].data(), GL_NEAREST); > + egl_.createTexture2D(*eglImageBayerIn_, glFormat_, GL_UNSIGNED_BYTE, > + inMapped->value().planes()[0].data(), GL_NEAREST); > } > > /* Generate the output render framebuffer as render to texture */ > diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h > index 82ed1305d..f3913d564 100644 > --- a/src/libcamera/software_isp/debayer_egl.h > +++ b/src/libcamera/software_isp/debayer_egl.h > @@ -78,7 +78,10 @@ private: > std::unique_ptr<eGLImage> eglImageBayerIn_; > std::unique_ptr<eGLImage> eglImageBayerOut_; > > + /* LSC lookup table */ > + std::unique_ptr<eGLImage> eglImageLscLookup_; > bool lscEnabled_; > + > /* Shader parameters */ > float firstRed_x_; > float firstRed_y_; > @@ -95,6 +98,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.55.0 >
On 16/07/2026 23:25, Milan Zamazal wrote: > @@ -483,6 +501,12 @@ void DebayerEGL::setShaderVariableValues(const DebayerParams ¶ms) > glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_TRUE, params.combinedMatrix.data().data()); > LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; > > + if (lscEnabled_) { > + egl_.createTexture2D(*eglImageLscLookup_, GL_RGB16F, GL_FLOAT, > + params.lscLut.data(), GL_LINEAR); > + glUniform1i(textureUniformLsc_, eglImageLscLookup_->texture_unit_uniform_id_); > + } > + Create texture should happen in configure() and we should use egl_.updateTexture2D - if and only if the LSC algorithm has called for new data - else it is sufficient to create and populate the texture and then reuse the texture_id on subsequent passes. I guess ? LSC doesn't need to be updated for each frame - even if it does updateTexture2D should be used - as its role in life is to update a GPU texture we already have instead of nuking and replacing. --- bod
Bryan O'Donoghue <bod.linux@nxsw.ie> writes: > On 16/07/2026 23:25, Milan Zamazal wrote: >> From: Xander Pronk <xander.c.pronk@gmail.com> >> > >> Add support for passing the LSC table from debayerParams to the shaders. >> >> The LSC table values are floats, we must add `type' parameter to >> createTexture2D to support this. Moreover, we use 16-bit internal >> format for the values, primarily because this works for me, unlike >> 32-bit floats. >> >> 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. >> >> 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> >> --- >> include/libcamera/internal/egl.h | 6 +++- >> .../internal/software_isp/debayer_params.h | 2 ++ >> src/ipa/simple/algorithms/lsc.cpp | 11 ++++--- >> src/libcamera/egl.cpp | 10 +++++-- >> src/libcamera/software_isp/debayer.cpp | 5 ++++ >> src/libcamera/software_isp/debayer_egl.cpp | 29 +++++++++++++++++-- >> src/libcamera/software_isp/debayer_egl.h | 6 ++++ >> 7 files changed, 60 insertions(+), 9 deletions(-) >> >> diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h >> index 030c813a5..37da2b18a 100644 >> --- a/include/libcamera/internal/egl.h >> +++ b/include/libcamera/internal/egl.h >> @@ -107,7 +107,11 @@ public: >> >> int createInputDMABufTexture2D(eGLImage &eglImage, int fd); >> int createOutputDMABufTexture2D(eGLImage &eglImage, int fd); >> - void createTexture2D(eGLImage &eglImage, void *data, GLint filter); >> + void createTexture2D(eGLImage &eglImage, >> + GLint internalFormat, >> + GLenum type, >> + const void *data, >> + GLint filter); >> >> void pushEnv(std::vector<std::string> &shaderEnv, const char *str); >> void makeCurrent(); >> diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h >> index e3e01bc04..76825e6c8 100644 >> --- a/include/libcamera/internal/software_isp/debayer_params.h >> +++ b/include/libcamera/internal/software_isp/debayer_params.h >> @@ -29,6 +29,8 @@ struct DebayerParams { >> >> static constexpr unsigned int kLscGridSize = 16; >> static constexpr unsigned int kLscValuesPerCell = 3; >> + static constexpr unsigned int kLscBytesPerCell = >> + kLscValuesPerCell * sizeof(float); >> using LscLookupTable = >> std::array<float, kLscGridSize * kLscGridSize * kLscValuesPerCell>; >> LscLookupTable lscLut{}; >> diff --git a/src/ipa/simple/algorithms/lsc.cpp b/src/ipa/simple/algorithms/lsc.cpp >> index c531937c7..51c5b63d5 100644 >> --- a/src/ipa/simple/algorithms/lsc.cpp >> +++ b/src/ipa/simple/algorithms/lsc.cpp >> @@ -46,13 +46,16 @@ void Lsc::prepare([[maybe_unused]] IPAContext &context, >> IPAFrameContext &frameContext, >> DebayerParams *params) >> { >> - params->lscEnabled = frameContext.lsc.enabled; >> - >> unsigned int ct = frameContext.awb.colourTemperature; >> constexpr unsigned int minTemperatureChange = 100; >> >> - if (!frameContext.lsc.enabled || >> - utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) >> + if (!frameContext.lsc.enabled) { >> + params->lscLut.fill(1.0); >> + lastAppliedCt_ = 0; >> + return; >> + } >> + >> + if (utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) >> return; >> >> const lsc::Components<uint8_t> &set = lscAlgo_.interpolateComponents(ct); >> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp >> index aeac5d313..c2a158ce7 100644 >> --- a/src/libcamera/egl.cpp >> +++ b/src/libcamera/egl.cpp >> @@ -241,6 +241,8 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd) >> /** >> * \brief Create a 2D texture from a memory buffer >> * \param[in,out] eglImage EGL image to associate with the texture >> + * \param[in] internalFormat OpenGL internal storage format (e.g., GL_RGB8, GL_RGBA8) >> + * \param[in] type OpenGL pixel data type (e.g., GL_UNSIGNED_BYTE, GL_FLOAT) >> * \param[in] data Pointer to pixel data, or nullptr for uninitialised texture >> * \param[in] filter GL texture filter setting >> * >> @@ -249,7 +251,11 @@ 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, >> + GLint internalFormat, >> + GLenum type, >> + const void *data, >> + GLint filter) >> { >> ASSERT(tid_ == Thread::currentId()); >> >> @@ -257,7 +263,7 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data, GLint filter) >> glBindTexture(GL_TEXTURE_2D, eglImage.texture_); >> >> // Generate texture, bind, associate image to texture, configure, unbind >> - glTexImage2D(GL_TEXTURE_2D, 0, eglImage.format_, eglImage.width_, eglImage.height_, 0, eglImage.format_, GL_UNSIGNED_BYTE, data); >> + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, eglImage.width_, eglImage.height_, 0, eglImage.format_, type, data); >> >> // Nearest filtering >> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); >> diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp >> index 5ae9f032f..70c0e3336 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 562e9fc7a..0fdf0e6e8 100644 >> --- a/src/libcamera/software_isp/debayer_egl.cpp >> +++ b/src/libcamera/software_isp/debayer_egl.cpp >> @@ -128,6 +128,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_ >> @@ -139,7 +141,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; >> } >> >> @@ -156,6 +159,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. >> @@ -348,6 +354,18 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg, >> */ >> stats_->setWindow(Rectangle(window_.size())); >> >> + if (lscEnabled_) { >> + constexpr unsigned int gridSize = DebayerParams::kLscGridSize; >> + const unsigned int stride = gridSize * DebayerParams::kLscBytesPerCell; >> + eglImageLscLookup_ = >> + std::make_unique<eGLImage>(GL_RGB, >> + gridSize, >> + gridSize, >> + stride, >> + GL_TEXTURE2, >> + 2); >> + } >> + >> return 0; >> } >> >> @@ -483,6 +501,12 @@ void DebayerEGL::setShaderVariableValues(const DebayerParams ¶ms) >> glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_TRUE, params.combinedMatrix.data().data()); >> LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; >> >> + if (lscEnabled_) { >> + egl_.createTexture2D(*eglImageLscLookup_, GL_RGB16F, GL_FLOAT, >> + params.lscLut.data(), GL_LINEAR); > > As we are discussing in the multi-pass thread, GL_RGB16F is dependent on > the optional GL_EXT_color_buffer_half_float > > https://registry.khronos.org/OpenGL/extensions/EXT/EXT_color_buffer_half_float.txt > > I think it should be pretty easy to disjunct on that as a flag in the > base class - a flag I plan to add for SSBO stats but also perfectly > happy for you to add. > > Half float linear filtering requires OES_texture_half_float_linear > > glGetString(GL_EXTENSIONS) | grep OES_texture_half_float_linear | > debayer_egl{ bool _has_OES_texture_half_float_linear;} or whatever > > if (_has_OES_texture_half_float_linear) > GL_RGB16F; > > Could we repreent the LSC data as uint8 natively ? I mean without > storing it all as float and then converting to uint8 on each LSC > application cycle ? We could. `float' would be a nice and clear interface. But I'd prefer not to deal with conditional support, especially if we had to handle the unsupported case some way. Since we opt for UQ<2,6> with libipa in the algorithm part, we can pass that to debayering. It may become a limitation in the future, but for now, why not, it's not that difficult to change once/if needed. >> + glUniform1i(textureUniformLsc_, eglImageLscLookup_->texture_unit_uniform_id_); >> + } >> + >> /* >> * 0 = Red, 1 = Green, 2 = Blue >> */ >> @@ -530,7 +554,8 @@ int DebayerEGL::debayerGPU(FrameBuffer *input, FrameBuffer *output, const Debaye >> LOG(Debayer, Error) << "mmap-ing buffer(s) failed"; >> return -ENODEV; >> } >> - egl_.createTexture2D(*eglImageBayerIn_, inMapped->value().planes()[0].data(), GL_NEAREST); >> + egl_.createTexture2D(*eglImageBayerIn_, glFormat_, GL_UNSIGNED_BYTE, >> + inMapped->value().planes()[0].data(), GL_NEAREST); >> } >> >> /* Generate the output render framebuffer as render to texture */ >> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h >> index 82ed1305d..f3913d564 100644 >> --- a/src/libcamera/software_isp/debayer_egl.h >> +++ b/src/libcamera/software_isp/debayer_egl.h >> @@ -78,7 +78,10 @@ private: >> std::unique_ptr<eGLImage> eglImageBayerIn_; >> std::unique_ptr<eGLImage> eglImageBayerOut_; >> >> + /* LSC lookup table */ >> + std::unique_ptr<eGLImage> eglImageLscLookup_; >> bool lscEnabled_; >> + >> /* Shader parameters */ >> float firstRed_x_; >> float firstRed_y_; >> @@ -95,6 +98,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.55.0 >>
Bryan O'Donoghue <bod.linux@nxsw.ie> writes: > On 16/07/2026 23:25, Milan Zamazal wrote: >> @@ -483,6 +501,12 @@ void DebayerEGL::setShaderVariableValues(const DebayerParams ¶ms) >> glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_TRUE, params.combinedMatrix.data().data()); >> LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; >> >> + if (lscEnabled_) { >> + egl_.createTexture2D(*eglImageLscLookup_, GL_RGB16F, GL_FLOAT, >> + params.lscLut.data(), GL_LINEAR); >> + glUniform1i(textureUniformLsc_, eglImageLscLookup_->texture_unit_uniform_id_); >> + } >> + > > Create texture should happen in configure() and we should use > egl_.updateTexture2D - if and only if the LSC algorithm has called for > new data - else it is sufficient to create and populate the texture and > then reuse the texture_id on subsequent passes. > > I guess ? LSC doesn't need to be updated for each frame - even if it > does updateTexture2D should be used - as its role in life is to update a > GPU texture we already have instead of nuking and replacing. OK, we have it now so let's use it. I'll do something about it in v10.
On 20/07/2026 14:35, Milan Zamazal wrote: >>> @@ -483,6 +501,12 @@ void DebayerEGL::setShaderVariableValues(const DebayerParams ¶ms) >>> glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_TRUE, params.combinedMatrix.data().data()); >>> LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; >>> >>> + if (lscEnabled_) { >>> + egl_.createTexture2D(*eglImageLscLookup_, GL_RGB16F, GL_FLOAT, >>> + params.lscLut.data(), GL_LINEAR); >> As we are discussing in the multi-pass thread, GL_RGB16F is dependent on >> the optional GL_EXT_color_buffer_half_float >> >> https://registry.khronos.org/OpenGL/extensions/EXT/ >> EXT_color_buffer_half_float.txt >> >> I think it should be pretty easy to disjunct on that as a flag in the >> base class - a flag I plan to add for SSBO stats but also perfectly >> happy for you to add. >> >> Half float linear filtering requires OES_texture_half_float_linear >> >> glGetString(GL_EXTENSIONS) | grep OES_texture_half_float_linear | >> debayer_egl{ bool _has_OES_texture_half_float_linear;} or whatever >> >> if (_has_OES_texture_half_float_linear) >> GL_RGB16F; >> >> Could we repreent the LSC data as uint8 natively ? I mean without >> storing it all as float and then converting to uint8 on each LSC >> application cycle ? > We could. `float' would be a nice and clear interface. But I'd prefer > not to deal with conditional support, especially if we had to handle the > unsupported case some way. Since we opt for UQ<2,6> with libipa in the > algorithm part, we can pass that to debayering. It may become a > limitation in the future, but for now, why not, it's not that difficult > to change once/if needed. Right so that would fit into eight bits as a regular GL_RGB/GL_UNSIGNED_BYTE. So long as you do the normalisation in the shader, that should work. --- bod
diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h index 030c813a5..37da2b18a 100644 --- a/include/libcamera/internal/egl.h +++ b/include/libcamera/internal/egl.h @@ -107,7 +107,11 @@ public: int createInputDMABufTexture2D(eGLImage &eglImage, int fd); int createOutputDMABufTexture2D(eGLImage &eglImage, int fd); - void createTexture2D(eGLImage &eglImage, void *data, GLint filter); + void createTexture2D(eGLImage &eglImage, + GLint internalFormat, + GLenum type, + const void *data, + GLint filter); void pushEnv(std::vector<std::string> &shaderEnv, const char *str); void makeCurrent(); diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h index e3e01bc04..76825e6c8 100644 --- a/include/libcamera/internal/software_isp/debayer_params.h +++ b/include/libcamera/internal/software_isp/debayer_params.h @@ -29,6 +29,8 @@ struct DebayerParams { static constexpr unsigned int kLscGridSize = 16; static constexpr unsigned int kLscValuesPerCell = 3; + static constexpr unsigned int kLscBytesPerCell = + kLscValuesPerCell * sizeof(float); using LscLookupTable = std::array<float, kLscGridSize * kLscGridSize * kLscValuesPerCell>; LscLookupTable lscLut{}; diff --git a/src/ipa/simple/algorithms/lsc.cpp b/src/ipa/simple/algorithms/lsc.cpp index c531937c7..51c5b63d5 100644 --- a/src/ipa/simple/algorithms/lsc.cpp +++ b/src/ipa/simple/algorithms/lsc.cpp @@ -46,13 +46,16 @@ void Lsc::prepare([[maybe_unused]] IPAContext &context, IPAFrameContext &frameContext, DebayerParams *params) { - params->lscEnabled = frameContext.lsc.enabled; - unsigned int ct = frameContext.awb.colourTemperature; constexpr unsigned int minTemperatureChange = 100; - if (!frameContext.lsc.enabled || - utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) + if (!frameContext.lsc.enabled) { + params->lscLut.fill(1.0); + lastAppliedCt_ = 0; + return; + } + + if (utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) return; const lsc::Components<uint8_t> &set = lscAlgo_.interpolateComponents(ct); diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp index aeac5d313..c2a158ce7 100644 --- a/src/libcamera/egl.cpp +++ b/src/libcamera/egl.cpp @@ -241,6 +241,8 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd) /** * \brief Create a 2D texture from a memory buffer * \param[in,out] eglImage EGL image to associate with the texture + * \param[in] internalFormat OpenGL internal storage format (e.g., GL_RGB8, GL_RGBA8) + * \param[in] type OpenGL pixel data type (e.g., GL_UNSIGNED_BYTE, GL_FLOAT) * \param[in] data Pointer to pixel data, or nullptr for uninitialised texture * \param[in] filter GL texture filter setting * @@ -249,7 +251,11 @@ 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, + GLint internalFormat, + GLenum type, + const void *data, + GLint filter) { ASSERT(tid_ == Thread::currentId()); @@ -257,7 +263,7 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data, GLint filter) glBindTexture(GL_TEXTURE_2D, eglImage.texture_); // Generate texture, bind, associate image to texture, configure, unbind - glTexImage2D(GL_TEXTURE_2D, 0, eglImage.format_, eglImage.width_, eglImage.height_, 0, eglImage.format_, GL_UNSIGNED_BYTE, data); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, eglImage.width_, eglImage.height_, 0, eglImage.format_, type, data); // Nearest filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp index 5ae9f032f..70c0e3336 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 562e9fc7a..0fdf0e6e8 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -128,6 +128,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_ @@ -139,7 +141,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; } @@ -156,6 +159,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. @@ -348,6 +354,18 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg, */ stats_->setWindow(Rectangle(window_.size())); + if (lscEnabled_) { + constexpr unsigned int gridSize = DebayerParams::kLscGridSize; + const unsigned int stride = gridSize * DebayerParams::kLscBytesPerCell; + eglImageLscLookup_ = + std::make_unique<eGLImage>(GL_RGB, + gridSize, + gridSize, + stride, + GL_TEXTURE2, + 2); + } + return 0; } @@ -483,6 +501,12 @@ void DebayerEGL::setShaderVariableValues(const DebayerParams ¶ms) glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_TRUE, params.combinedMatrix.data().data()); LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; + if (lscEnabled_) { + egl_.createTexture2D(*eglImageLscLookup_, GL_RGB16F, GL_FLOAT, + params.lscLut.data(), GL_LINEAR); + glUniform1i(textureUniformLsc_, eglImageLscLookup_->texture_unit_uniform_id_); + } + /* * 0 = Red, 1 = Green, 2 = Blue */ @@ -530,7 +554,8 @@ int DebayerEGL::debayerGPU(FrameBuffer *input, FrameBuffer *output, const Debaye LOG(Debayer, Error) << "mmap-ing buffer(s) failed"; return -ENODEV; } - egl_.createTexture2D(*eglImageBayerIn_, inMapped->value().planes()[0].data(), GL_NEAREST); + egl_.createTexture2D(*eglImageBayerIn_, glFormat_, GL_UNSIGNED_BYTE, + inMapped->value().planes()[0].data(), GL_NEAREST); } /* Generate the output render framebuffer as render to texture */ diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h index 82ed1305d..f3913d564 100644 --- a/src/libcamera/software_isp/debayer_egl.h +++ b/src/libcamera/software_isp/debayer_egl.h @@ -78,7 +78,10 @@ private: std::unique_ptr<eGLImage> eglImageBayerIn_; std::unique_ptr<eGLImage> eglImageBayerOut_; + /* LSC lookup table */ + std::unique_ptr<eGLImage> eglImageLscLookup_; bool lscEnabled_; + /* Shader parameters */ float firstRed_x_; float firstRed_y_; @@ -95,6 +98,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_;