| Message ID | 20261016202818.1405645-2-halfline@gmail.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
Quoting Ray Strode (2026-10-16 21:28:08) > From: Ray Strode <rstrode@redhat.com> > > The GPU debayer path scales the image to fill a requested output size and > crops any excess image area. > > Its projection positions the scaled image against one side of the output, > so aspect-ratio changes and Bayer border removal shift the visible scene. > The series aligns rendering and metering with a centered sensor crop. > > This commit centers the projection on the output region in sensor > coordinates, distributing the crop across both sides. > > Signed-off-by: Ray Strode <rstrode@redhat.com> > --- > .../internal/software_isp/debayer_geometry.h | 43 +++++++++++++++++++ > .../internal/software_isp/meson.build | 1 + > src/libcamera/software_isp/debayer_egl.cpp | 21 +++------ > 3 files changed, 49 insertions(+), 16 deletions(-) > create mode 100644 include/libcamera/internal/software_isp/debayer_geometry.h > > diff --git a/include/libcamera/internal/software_isp/debayer_geometry.h b/include/libcamera/internal/software_isp/debayer_geometry.h > new file mode 100644 > index 00000000..70ae6a8b > --- /dev/null > +++ b/include/libcamera/internal/software_isp/debayer_geometry.h Is this really 'debayer' geometry? As far as I can tell, this file is added just to be able to add some unit tests around this perhaps? If this really is to be re-usable, then I could imagine some geometry helpers around additional components like dewarp perhaps, but then it really isn't a "debayer geometry". I'd be tempted to suggest that this patch should just focus on fixing the transX and transY in place in DebayerEGL::setShaderVariableValues. It would be a lot easier for reviewers to see the functional change. > @@ -0,0 +1,43 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2026, Red Hat Inc. > + * > + * Geometry for scaling and cropping debayered images > + */ > + > +#pragma once > + > +#include <algorithm> > +#include <array> > + > +#include <libcamera/geometry.h> > + > +namespace libcamera { > + > +/* > + * Return a column-major projection matrix for an input-sized viewport and an > + * output-sized framebuffer. Scale to fill the output, centering the crop on > + * the input. All sizes must be nonzero, with nativeSize contained in inputSize. > + */ > +inline std::array<float, 16> debayerProjection(const Size &inputSize, > + const Size &nativeSize, > + const Size &outputSize) > +{ > + float scale = std::max(static_cast<float>(outputSize.width) / nativeSize.width, > + static_cast<float>(outputSize.height) / nativeSize.height); > + > + /* The framebuffer center is output/viewport - 1 in normalized coordinates. */ > + float transX = static_cast<float>(outputSize.width) / inputSize.width - 1.0f; > + float transY = static_cast<float>(outputSize.height) / inputSize.height - 1.0f; > + > + /* clang-format off */ > + return { > + scale, 0, 0, 0, > + 0, scale, 0, 0, > + 0, 0, 1, 0, > + transX, transY, 0, 1, > + }; > + /* clang-format on */ > +} > + > +} /* namespace libcamera */ > diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build > index df7c3b97..e18597ed 100644 > --- a/include/libcamera/internal/software_isp/meson.build > +++ b/include/libcamera/internal/software_isp/meson.build > @@ -2,6 +2,7 @@ > > libcamera_internal_headers += files([ > 'benchmark.h', > + 'debayer_geometry.h', > 'debayer_params.h', > 'software_isp.h', > 'swisp_stats.h', > diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp > index 300822f9..1a7ad25c 100644 > --- a/src/libcamera/software_isp/debayer_egl.cpp > +++ b/src/libcamera/software_isp/debayer_egl.cpp > @@ -9,7 +9,7 @@ > > #include "debayer_egl.h" > > -#include <algorithm> > +#include <array> > #include <assert.h> > #include <memory> > #include <span> > @@ -24,6 +24,7 @@ > > #include "libcamera/internal/formats.h" > #include "libcamera/internal/framebuffer.h" > +#include "libcamera/internal/software_isp/debayer_geometry.h" > > #include "../shaders/glsl_shaders.h" > > @@ -388,20 +389,8 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara > GLfloat Step[] = { static_cast<float>(bytesPerPixel_) / (inputConfig_.stride - 1), > 1.0f / (height_ - 1) }; > GLfloat Stride = (GLfloat)width_ / (shaderStridePixels_ / bytesPerPixel_); > - /* > - * Scale the output size from the native size the algorithm produces for > - * the input size. Keep the aspect ratio and prefer cropping over black > - * bars. > - */ > - GLfloat scale = std::max((GLfloat)outputSize_.width / nativeOutputSize_.width, > - (GLfloat)outputSize_.height / nativeOutputSize_.height); > - GLfloat trans = -(1.0f - scale); > - GLfloat projMatrix[] = { > - scale, 0, 0, 0, > - 0, scale, 0, 0, > - 0, 0, 1, 0, > - trans, trans, 0, 1 > - }; > + const std::array<float, 16> projMatrix = > + debayerProjection({ width_, height_ }, nativeOutputSize_, outputSize_); > /* Static const coordinates */ > static const GLfloat vcoordinates[4][2] = { > { -1.0f, -1.0f }, > @@ -445,7 +434,7 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara > glUniform2fv(textureUniformSize_, 1, imgSize); > glUniform2fv(textureUniformStep_, 1, Step); > glUniform1f(textureUniformStrideFactor_, Stride); > - glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix); > + glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix.data()); > > LOG(Debayer, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_ > << " tex_y " << textureUniformBayerDataIn_ > -- > 2.56.0 >
Quoting Kieran Bingham (2026-10-18 21:48:40) > Quoting Ray Strode (2026-10-16 21:28:08) > > From: Ray Strode <rstrode@redhat.com> > > > > The GPU debayer path scales the image to fill a requested output size and > > crops any excess image area. > > > > Its projection positions the scaled image against one side of the output, > > so aspect-ratio changes and Bayer border removal shift the visible scene. > > The series aligns rendering and metering with a centered sensor crop. > > > > This commit centers the projection on the output region in sensor > > coordinates, distributing the crop across both sides. > > > > Signed-off-by: Ray Strode <rstrode@redhat.com> > > --- > > .../internal/software_isp/debayer_geometry.h | 43 +++++++++++++++++++ > > .../internal/software_isp/meson.build | 1 + > > src/libcamera/software_isp/debayer_egl.cpp | 21 +++------ > > 3 files changed, 49 insertions(+), 16 deletions(-) > > create mode 100644 include/libcamera/internal/software_isp/debayer_geometry.h > > > > diff --git a/include/libcamera/internal/software_isp/debayer_geometry.h b/include/libcamera/internal/software_isp/debayer_geometry.h > > new file mode 100644 > > index 00000000..70ae6a8b > > --- /dev/null > > +++ b/include/libcamera/internal/software_isp/debayer_geometry.h > > Is this really 'debayer' geometry? > > As far as I can tell, this file is added just to be able to add some > unit tests around this perhaps? > > If this really is to be re-usable, then I could imagine some geometry > helpers around additional components like dewarp perhaps, but then it > really isn't a "debayer geometry". > > > I'd be tempted to suggest that this patch should just focus on fixing > the transX and transY in place in DebayerEGL::setShaderVariableValues. > It would be a lot easier for reviewers to see the functional change. > In fact, I see this was proposed already in: - https://patchwork.libcamera.org/patch/28178/ > > @@ -0,0 +1,43 @@ > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > +/* > > + * Copyright (C) 2026, Red Hat Inc. > > + * > > + * Geometry for scaling and cropping debayered images > > + */ > > + > > +#pragma once > > + > > +#include <algorithm> > > +#include <array> > > + > > +#include <libcamera/geometry.h> > > + > > +namespace libcamera { > > + > > +/* > > + * Return a column-major projection matrix for an input-sized viewport and an > > + * output-sized framebuffer. Scale to fill the output, centering the crop on > > + * the input. All sizes must be nonzero, with nativeSize contained in inputSize. > > + */ > > +inline std::array<float, 16> debayerProjection(const Size &inputSize, > > + const Size &nativeSize, > > + const Size &outputSize) > > +{ > > + float scale = std::max(static_cast<float>(outputSize.width) / nativeSize.width, > > + static_cast<float>(outputSize.height) / nativeSize.height); > > + > > + /* The framebuffer center is output/viewport - 1 in normalized coordinates. */ > > + float transX = static_cast<float>(outputSize.width) / inputSize.width - 1.0f; > > + float transY = static_cast<float>(outputSize.height) / inputSize.height - 1.0f; > > + > > + /* clang-format off */ > > + return { > > + scale, 0, 0, 0, > > + 0, scale, 0, 0, > > + 0, 0, 1, 0, > > + transX, transY, 0, 1, > > + }; > > + /* clang-format on */ > > +} > > + > > +} /* namespace libcamera */ > > diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build > > index df7c3b97..e18597ed 100644 > > --- a/include/libcamera/internal/software_isp/meson.build > > +++ b/include/libcamera/internal/software_isp/meson.build > > @@ -2,6 +2,7 @@ > > > > libcamera_internal_headers += files([ > > 'benchmark.h', > > + 'debayer_geometry.h', > > 'debayer_params.h', > > 'software_isp.h', > > 'swisp_stats.h', > > diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp > > index 300822f9..1a7ad25c 100644 > > --- a/src/libcamera/software_isp/debayer_egl.cpp > > +++ b/src/libcamera/software_isp/debayer_egl.cpp > > @@ -9,7 +9,7 @@ > > > > #include "debayer_egl.h" > > > > -#include <algorithm> > > +#include <array> > > #include <assert.h> > > #include <memory> > > #include <span> > > @@ -24,6 +24,7 @@ > > > > #include "libcamera/internal/formats.h" > > #include "libcamera/internal/framebuffer.h" > > +#include "libcamera/internal/software_isp/debayer_geometry.h" > > > > #include "../shaders/glsl_shaders.h" > > > > @@ -388,20 +389,8 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara > > GLfloat Step[] = { static_cast<float>(bytesPerPixel_) / (inputConfig_.stride - 1), > > 1.0f / (height_ - 1) }; > > GLfloat Stride = (GLfloat)width_ / (shaderStridePixels_ / bytesPerPixel_); > > - /* > > - * Scale the output size from the native size the algorithm produces for > > - * the input size. Keep the aspect ratio and prefer cropping over black > > - * bars. > > - */ > > - GLfloat scale = std::max((GLfloat)outputSize_.width / nativeOutputSize_.width, > > - (GLfloat)outputSize_.height / nativeOutputSize_.height); > > - GLfloat trans = -(1.0f - scale); > > - GLfloat projMatrix[] = { > > - scale, 0, 0, 0, > > - 0, scale, 0, 0, > > - 0, 0, 1, 0, > > - trans, trans, 0, 1 > > - }; > > + const std::array<float, 16> projMatrix = > > + debayerProjection({ width_, height_ }, nativeOutputSize_, outputSize_); > > /* Static const coordinates */ > > static const GLfloat vcoordinates[4][2] = { > > { -1.0f, -1.0f }, > > @@ -445,7 +434,7 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara > > glUniform2fv(textureUniformSize_, 1, imgSize); > > glUniform2fv(textureUniformStep_, 1, Step); > > glUniform1f(textureUniformStrideFactor_, Stride); > > - glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix); > > + glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix.data()); > > > > LOG(Debayer, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_ > > << " tex_y " << textureUniformBayerDataIn_ > > -- > > 2.56.0 > >
Hi, On Mon, Oct 19, 2026 at 9:47 AM Kieran Bingham <kieran.bingham@ideasonboard.com> wrote: > Quoting Kieran Bingham (2026-10-18 21:48:40) > > > > Is this really 'debayer' geometry? I suppose it was more general, calculating scaling and translation between the input viewport and the output framebuffer. > > As far as I can tell, this file is added just to be able to add some > > unit tests around this perhaps? Indeed, the point of splitting it out was entirely so we could add test coverage to check the matrix calculations. > > If this really is to be re-usable, then I could imagine some geometry > > helpers around additional components like dewarp perhaps, but then it > > really isn't a "debayer geometry". You're right, a better name would probably be image_projection or something like that. > > I'd be tempted to suggest that this patch should just focus on fixing > > the transX and transY in place in DebayerEGL::setShaderVariableValues. > > It would be a lot easier for reviewers to see the functional change. I think I agree with you now, we should keep the changes as targeted, reviewable and as noncontroversial as possible. > In fact, I see this was proposed already in: > > - https://patchwork.libcamera.org/patch/28178/ oh perfect. i'll use that as the start and then put white balance/exposure fixes and scalercrop metadata on top. I don't think the unit test gained by splitting out the projection helper is any huge win, we can just drop that. --Ray
diff --git a/include/libcamera/internal/software_isp/debayer_geometry.h b/include/libcamera/internal/software_isp/debayer_geometry.h new file mode 100644 index 00000000..70ae6a8b --- /dev/null +++ b/include/libcamera/internal/software_isp/debayer_geometry.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Red Hat Inc. + * + * Geometry for scaling and cropping debayered images + */ + +#pragma once + +#include <algorithm> +#include <array> + +#include <libcamera/geometry.h> + +namespace libcamera { + +/* + * Return a column-major projection matrix for an input-sized viewport and an + * output-sized framebuffer. Scale to fill the output, centering the crop on + * the input. All sizes must be nonzero, with nativeSize contained in inputSize. + */ +inline std::array<float, 16> debayerProjection(const Size &inputSize, + const Size &nativeSize, + const Size &outputSize) +{ + float scale = std::max(static_cast<float>(outputSize.width) / nativeSize.width, + static_cast<float>(outputSize.height) / nativeSize.height); + + /* The framebuffer center is output/viewport - 1 in normalized coordinates. */ + float transX = static_cast<float>(outputSize.width) / inputSize.width - 1.0f; + float transY = static_cast<float>(outputSize.height) / inputSize.height - 1.0f; + + /* clang-format off */ + return { + scale, 0, 0, 0, + 0, scale, 0, 0, + 0, 0, 1, 0, + transX, transY, 0, 1, + }; + /* clang-format on */ +} + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build index df7c3b97..e18597ed 100644 --- a/include/libcamera/internal/software_isp/meson.build +++ b/include/libcamera/internal/software_isp/meson.build @@ -2,6 +2,7 @@ libcamera_internal_headers += files([ 'benchmark.h', + 'debayer_geometry.h', 'debayer_params.h', 'software_isp.h', 'swisp_stats.h', diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 300822f9..1a7ad25c 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -9,7 +9,7 @@ #include "debayer_egl.h" -#include <algorithm> +#include <array> #include <assert.h> #include <memory> #include <span> @@ -24,6 +24,7 @@ #include "libcamera/internal/formats.h" #include "libcamera/internal/framebuffer.h" +#include "libcamera/internal/software_isp/debayer_geometry.h" #include "../shaders/glsl_shaders.h" @@ -388,20 +389,8 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara GLfloat Step[] = { static_cast<float>(bytesPerPixel_) / (inputConfig_.stride - 1), 1.0f / (height_ - 1) }; GLfloat Stride = (GLfloat)width_ / (shaderStridePixels_ / bytesPerPixel_); - /* - * Scale the output size from the native size the algorithm produces for - * the input size. Keep the aspect ratio and prefer cropping over black - * bars. - */ - GLfloat scale = std::max((GLfloat)outputSize_.width / nativeOutputSize_.width, - (GLfloat)outputSize_.height / nativeOutputSize_.height); - GLfloat trans = -(1.0f - scale); - GLfloat projMatrix[] = { - scale, 0, 0, 0, - 0, scale, 0, 0, - 0, 0, 1, 0, - trans, trans, 0, 1 - }; + const std::array<float, 16> projMatrix = + debayerProjection({ width_, height_ }, nativeOutputSize_, outputSize_); /* Static const coordinates */ static const GLfloat vcoordinates[4][2] = { { -1.0f, -1.0f }, @@ -445,7 +434,7 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara glUniform2fv(textureUniformSize_, 1, imgSize); glUniform2fv(textureUniformStep_, 1, Step); glUniform1f(textureUniformStrideFactor_, Stride); - glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix); + glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix.data()); LOG(Debayer, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_ << " tex_y " << textureUniformBayerDataIn_