| Message ID | 20260612025641.3665225-1-qi.hou@nxp.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
Qi Hou <qi.hou@nxp.com> writes: > When HAVE_DEBAYER_EGL is enabled, the SoftwareIsp constructor > unconditionally creates a DebayerEGL instance. If the platform lacks > EGL surfaceless support, DebayerEGL::start() fails with -ENODEV after > SwStatsCpu ownership has already been moved, making fallback to > DebayerCpu impossible. > > Add a static eGL::isAvailable() method that probes whether EGL > surfaceless rendering can be initialised. Extract the common display > obtaining logic (eglBindAPI, eglGetPlatformDisplay, eglInitialize) > into a private eGL::probeDisplay() helper that is shared by both > isAvailable() and initEGLContext(), avoiding code duplication. > > The SoftwareIsp constructor now calls eGL::isAvailable() before > creating DebayerEGL. If EGL is not available, the existing fallback > path creates a DebayerCpu instance instead. > > Signed-off-by: Qi Hou <qi.hou@nxp.com> Reviewed-by: Milan Zamazal <mzamazal@redhat.com> > --- > include/libcamera/internal/egl.h | 2 + > src/libcamera/egl.cpp | 60 ++++++++++++++++----- > src/libcamera/software_isp/software_isp.cpp | 10 +++- > 3 files changed, 56 insertions(+), 16 deletions(-) > > diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h > index 57f90d93..f7bfb28d 100644 > --- a/include/libcamera/internal/egl.h > +++ b/include/libcamera/internal/egl.h > @@ -103,6 +103,7 @@ public: > ~eGL(); > > int initEGLContext(); > + static bool isAvailable(); > > int createInputDMABufTexture2D(eGLImage &eglImage, int fd); > int createOutputDMABufTexture2D(eGLImage &eglImage, int fd); > @@ -133,6 +134,7 @@ private: > EGLContext context_ = EGL_NO_CONTEXT; > EGLSurface surface_ = EGL_NO_SURFACE; > > + static EGLDisplay probeDisplay(); > int compileShader(int shaderType, GLuint &shaderId, const unsigned char *shaderData, > unsigned int shaderDataLen, > Span<const std::string> shaderEnv); > diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp > index c185bb7a..860f18dd 100644 > --- a/src/libcamera/egl.cpp > +++ b/src/libcamera/egl.cpp > @@ -267,6 +267,50 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data) > glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); > } > > +EGLDisplay eGL::probeDisplay() > +{ > + EGLDisplay display; > + > + if (!eglBindAPI(EGL_OPENGL_ES_API)) { > + LOG(eGL, Info) << "API bind fail"; > + return EGL_NO_DISPLAY; > + } > + > + display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, > + EGL_DEFAULT_DISPLAY, > + nullptr); > + > + if (display == EGL_NO_DISPLAY) { > + LOG(eGL, Info) << "Unable to get EGL display"; > + return EGL_NO_DISPLAY; > + } > + > + if (eglInitialize(display, nullptr, nullptr) != EGL_TRUE) { > + LOG(eGL, Error) << "eglInitialize fail"; > + return EGL_NO_DISPLAY; > + } > + > + return display; > +} > + > +/** > + * \brief Probe whether EGL surfaceless rendering is available > + * > + * Checks if an EGL surfaceless display can be obtained and initialised. > + * The display is immediately terminated so that no resources are leaked. > + * > + * \return true if EGL surfaceless rendering is available, false otherwise > + */ > +bool eGL::isAvailable() > +{ > + EGLDisplay display = probeDisplay(); > + if (display == EGL_NO_DISPLAY) > + return false; > + > + eglTerminate(display); > + return true; > +} > + > /** > * \brief Initialise the EGL context > * > @@ -297,21 +341,9 @@ int eGL::initEGLContext() > EGLint numConfigs; > EGLConfig config; > > - if (!eglBindAPI(EGL_OPENGL_ES_API)) { > - LOG(eGL, Error) << "API bind fail"; > - goto fail; > - } > - > - display_ = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, > - EGL_DEFAULT_DISPLAY, > - nullptr); > + display_ = probeDisplay(); > if (display_ == EGL_NO_DISPLAY) { > - LOG(eGL, Error) << "Unable to get EGL display"; > - goto fail; > - } > - > - if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE) { > - LOG(eGL, Error) << "eglInitialize fail"; > + LOG(eGL, Error) << "Unable to probe display"; > goto fail; > } > > diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp > index 781cf02f..ff8c3465 100644 > --- a/src/libcamera/software_isp/software_isp.cpp > +++ b/src/libcamera/software_isp/software_isp.cpp > @@ -119,8 +119,14 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, > } > } > > - if (!softISPMode || softISPMode == "gpu") > - debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm); > + if (!softISPMode || softISPMode == "gpu") { > + if (eGL::isAvailable()) { > + debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm); > + } else { > + LOG(SoftwareIsp, Info) > + << "EGL not available, falling back to CPU debayer"; > + } > + } > > #endif > if (!debayer_)
On 12/06/2026 03:56, Qi Hou wrote: > When HAVE_DEBAYER_EGL is enabled, the SoftwareIsp constructor > unconditionally creates a DebayerEGL instance. If the platform lacks > EGL surfaceless support, DebayerEGL::start() fails with -ENODEV after > SwStatsCpu ownership has already been moved, making fallback to > DebayerCpu impossible. > > Add a static eGL::isAvailable() method that probes whether EGL > surfaceless rendering can be initialised. Extract the common display > obtaining logic (eglBindAPI, eglGetPlatformDisplay, eglInitialize) > into a private eGL::probeDisplay() helper that is shared by both > isAvailable() and initEGLContext(), avoiding code duplication. > > The SoftwareIsp constructor now calls eGL::isAvailable() before > creating DebayerEGL. If EGL is not available, the existing fallback > path creates a DebayerCpu instance instead. > > Signed-off-by: Qi Hou <qi.hou@nxp.com> > --- > include/libcamera/internal/egl.h | 2 + > src/libcamera/egl.cpp | 60 ++++++++++++++++----- > src/libcamera/software_isp/software_isp.cpp | 10 +++- > 3 files changed, 56 insertions(+), 16 deletions(-) > > diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h > index 57f90d93..f7bfb28d 100644 > --- a/include/libcamera/internal/egl.h > +++ b/include/libcamera/internal/egl.h > @@ -103,6 +103,7 @@ public: > ~eGL(); > > int initEGLContext(); > + static bool isAvailable(); > > int createInputDMABufTexture2D(eGLImage &eglImage, int fd); > int createOutputDMABufTexture2D(eGLImage &eglImage, int fd); > @@ -133,6 +134,7 @@ private: > EGLContext context_ = EGL_NO_CONTEXT; > EGLSurface surface_ = EGL_NO_SURFACE; > > + static EGLDisplay probeDisplay(); > int compileShader(int shaderType, GLuint &shaderId, const unsigned char *shaderData, > unsigned int shaderDataLen, > Span<const std::string> shaderEnv); > diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp > index c185bb7a..860f18dd 100644 > --- a/src/libcamera/egl.cpp > +++ b/src/libcamera/egl.cpp > @@ -267,6 +267,50 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data) > glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); > } > > +EGLDisplay eGL::probeDisplay() > +{ > + EGLDisplay display; > + > + if (!eglBindAPI(EGL_OPENGL_ES_API)) { > + LOG(eGL, Info) << "API bind fail"; > + return EGL_NO_DISPLAY; > + } > + > + display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, > + EGL_DEFAULT_DISPLAY, > + nullptr); > + > + if (display == EGL_NO_DISPLAY) { > + LOG(eGL, Info) << "Unable to get EGL display"; > + return EGL_NO_DISPLAY; > + } > + > + if (eglInitialize(display, nullptr, nullptr) != EGL_TRUE) { > + LOG(eGL, Error) << "eglInitialize fail"; > + return EGL_NO_DISPLAY; > + } > + > + return display; > +} > + > +/** > + * \brief Probe whether EGL surfaceless rendering is available > + * > + * Checks if an EGL surfaceless display can be obtained and initialised. > + * The display is immediately terminated so that no resources are leaked. > + * > + * \return true if EGL surfaceless rendering is available, false otherwise > + */ > +bool eGL::isAvailable() > +{ > + EGLDisplay display = probeDisplay(); > + if (display == EGL_NO_DISPLAY) > + return false; > + > + eglTerminate(display); > + return true; > +} > + > /** > * \brief Initialise the EGL context > * > @@ -297,21 +341,9 @@ int eGL::initEGLContext() > EGLint numConfigs; > EGLConfig config; > > - if (!eglBindAPI(EGL_OPENGL_ES_API)) { > - LOG(eGL, Error) << "API bind fail"; > - goto fail; > - } > - > - display_ = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, > - EGL_DEFAULT_DISPLAY, > - nullptr); > + display_ = probeDisplay(); > if (display_ == EGL_NO_DISPLAY) { > - LOG(eGL, Error) << "Unable to get EGL display"; > - goto fail; > - } > - > - if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE) { > - LOG(eGL, Error) << "eglInitialize fail"; > + LOG(eGL, Error) << "Unable to probe display"; > goto fail; > } > > diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp > index 781cf02f..ff8c3465 100644 > --- a/src/libcamera/software_isp/software_isp.cpp > +++ b/src/libcamera/software_isp/software_isp.cpp > @@ -119,8 +119,14 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, > } > } > > - if (!softISPMode || softISPMode == "gpu") > - debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm); > + if (!softISPMode || softISPMode == "gpu") { > + if (eGL::isAvailable()) { > + debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm); > + } else { > + LOG(SoftwareIsp, Info) > + << "EGL not available, falling back to CPU debayer"; > + } > + } > > #endif > if (!debayer_) > -- > 2.34.1 > Perhaps a further optimisation of this would be to bring back GBM surface support ? i.e. our first version of GPUISP used a GBM surface which then transitioned to MESA surfaceless. I wonder if you have tried GBM ? It should be easy enough to revert the change and test ? --- bod
Hi Bryan O'Donoghue,
Thanks for your comment. I gave a try to revert below two patches that moved from GBM surface to surfaceless and tested on my i.MX8MM board.
The good news is that DebayerEGL::start() no longer fails. However, the output display is completely black.
I’m not sure if this is a GBM surface configuration issue on i.MX8MM or something else. Since I’m not very familiar with the GBM surface path.
I think it might be better handled by someone like you with professional expertise.
My patch (the isAvailable() probe + CPU fallback) at least provides a working path on platforms without surfaceless support. Would it make sense
to merge this as a basic fix, and then add GBM surface fallback as a follow-up ?
1. commit 0c2ed9ebf99e3328efe7e480d194d333a8dc5568
Author: Robert Mader <robert.mader@collabora.com<mailto:robert.mader@collabora.com>>
Date: Wed Jan 21 10:08:52 2026 +0100
egl: Use the Mesa surfaceless platform instead of GBM
Mesa surfaceless platform appears to be a better fit for the use-case at hand:
1. Like GBM it is Mesa specific, so no change in supported setups is
expected. If ever required, a fallback to the generic device platform
could be added on top.
2. It leaves the complexity of selecting a renderer device to the
driver, reducing code and dependencies.
3. It allows to use llvmpipe / software drivers without dri device,
which can be useful on CI or debugging (with LIBGL_ALWAYS_SOFTWARE=1).
Signed-off-by: Robert Mader <robert.mader@collabora.com<mailto:robert.mader@collabora.com>>
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org<mailto:bryan.odonoghue@linaro.org>>
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org<mailto:bryan.odonoghue@linaro.org>> # sm8250/rb5, x1e/Dell Insprion14p
Reviewed-by: Milan Zamazal <mzamazal@redhat.com<mailto:mzamazal@redhat.com>>
Tested-by: Milan Zamazal <mzamazal@redhat.com<mailto:mzamazal@redhat.com>> # TI AM69
Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com<mailto:barnabas.pocze@ideasonboard.com>> # ThinkPad X1 Yoga Gen 7 + ov2740
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com<mailto:laurent.pinchart@ideasonboard.com>>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com<mailto:kieran.bingham@ideasonboard.com>>
1. commit 7350d6cc5dd1b5bc724749d21744c07879deb803
Author: Robert Mader <robert.mader@collabora.com<mailto:robert.mader@collabora.com>>
Date: Wed Jan 21 10:08:54 2026 +0100
Revert "libcamera: software_isp: gbm: Add a GBM helper class for GPU surface access"
GBM is not used any more - remove the helper class.
This reverts commit c60b1ce8193841c2742b655097bb39ccbcb417c2.
Signed-off-by: Robert Mader <robert.mader@collabora.com<mailto:robert.mader@collabora.com>>
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org<mailto:bryan.odonoghue@linaro.org>>
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org<mailto:bryan.odonoghue@linaro.org>> # sm8250/rb5, x1e/Dell Insprion14p
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com<mailto:barnabas.pocze@ideasonboard.com>>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com<mailto:laurent.pinchart@ideasonboard.com>>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com<mailto:kieran.bingham@ideasonboard.com>>
Regards,
Qi Hou
-----Original Message-----
From: Bryan O'Donoghue <bod.linux@nxsw.ie>
Sent: Saturday, June 13, 2026 8:43 PM
To: Qi Hou <qi.hou@nxp.com>; libcamera-devel@lists.libcamera.org
Cc: Jared Hu <jared.hu@nxp.com>; Julien Vuillaumier <julien.vuillaumier@nxp.com>
Subject: [EXT] Re: [PATCH v3] libcamera: software_isp: Probe EGL availability before creating DebayerEGL
[Some people who received this message don't often get email from bod.linux@nxsw.ie<mailto:bod.linux@nxsw.ie>. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
Caution: This is an external email. Please take care when clicking links or opening attachments. When in doubt, report the message using the 'Report this email' button
On 12/06/2026 03:56, Qi Hou wrote:
> When HAVE_DEBAYER_EGL is enabled, the SoftwareIsp constructor
> unconditionally creates a DebayerEGL instance. If the platform lacks
> EGL surfaceless support, DebayerEGL::start() fails with -ENODEV after
> SwStatsCpu ownership has already been moved, making fallback to
> DebayerCpu impossible.
>
> Add a static eGL::isAvailable() method that probes whether EGL
> surfaceless rendering can be initialised. Extract the common display
> obtaining logic (eglBindAPI, eglGetPlatformDisplay, eglInitialize)
> into a private eGL::probeDisplay() helper that is shared by both
> isAvailable() and initEGLContext(), avoiding code duplication.
>
> The SoftwareIsp constructor now calls eGL::isAvailable() before
> creating DebayerEGL. If EGL is not available, the existing fallback
> path creates a DebayerCpu instance instead.
>
> Signed-off-by: Qi Hou <qi.hou@nxp.com<mailto:qi.hou@nxp.com>>
> ---
> include/libcamera/internal/egl.h | 2 +
> src/libcamera/egl.cpp | 60 ++++++++++++++++-----
> src/libcamera/software_isp/software_isp.cpp | 10 +++-
> 3 files changed, 56 insertions(+), 16 deletions(-)
>
> diff --git a/include/libcamera/internal/egl.h
> b/include/libcamera/internal/egl.h
> index 57f90d93..f7bfb28d 100644
> --- a/include/libcamera/internal/egl.h
> +++ b/include/libcamera/internal/egl.h
> @@ -103,6 +103,7 @@ public:
> ~eGL();
>
> int initEGLContext();
> + static bool isAvailable();
>
> int createInputDMABufTexture2D(eGLImage &eglImage, int fd);
> int createOutputDMABufTexture2D(eGLImage &eglImage, int fd); @@
> -133,6 +134,7 @@ private:
> EGLContext context_ = EGL_NO_CONTEXT;
> EGLSurface surface_ = EGL_NO_SURFACE;
>
> + static EGLDisplay probeDisplay();
> int compileShader(int shaderType, GLuint &shaderId, const unsigned char *shaderData,
> unsigned int shaderDataLen,
> Span<const std::string> shaderEnv); diff --git
> a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp index
> c185bb7a..860f18dd 100644
> --- a/src/libcamera/egl.cpp
> +++ b/src/libcamera/egl.cpp
> @@ -267,6 +267,50 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data)
> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
> }
>
> +EGLDisplay eGL::probeDisplay()
> +{
> + EGLDisplay display;
> +
> + if (!eglBindAPI(EGL_OPENGL_ES_API)) {
> + LOG(eGL, Info) << "API bind fail";
> + return EGL_NO_DISPLAY;
> + }
> +
> + display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA,
> + EGL_DEFAULT_DISPLAY,
> + nullptr);
> +
> + if (display == EGL_NO_DISPLAY) {
> + LOG(eGL, Info) << "Unable to get EGL display";
> + return EGL_NO_DISPLAY;
> + }
> +
> + if (eglInitialize(display, nullptr, nullptr) != EGL_TRUE) {
> + LOG(eGL, Error) << "eglInitialize fail";
> + return EGL_NO_DISPLAY;
> + }
> +
> + return display;
> +}
> +
> +/**
> + * \brief Probe whether EGL surfaceless rendering is available
> + *
> + * Checks if an EGL surfaceless display can be obtained and initialised.
> + * The display is immediately terminated so that no resources are leaked.
> + *
> + * \return true if EGL surfaceless rendering is available, false
> +otherwise */ bool eGL::isAvailable() {
> + EGLDisplay display = probeDisplay();
> + if (display == EGL_NO_DISPLAY)
> + return false;
> +
> + eglTerminate(display);
> + return true;
> +}
> +
> /**
> * \brief Initialise the EGL context
> *
> @@ -297,21 +341,9 @@ int eGL::initEGLContext()
> EGLint numConfigs;
> EGLConfig config;
>
> - if (!eglBindAPI(EGL_OPENGL_ES_API)) {
> - LOG(eGL, Error) << "API bind fail";
> - goto fail;
> - }
> -
> - display_ = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA,
> - EGL_DEFAULT_DISPLAY,
> - nullptr);
> + display_ = probeDisplay();
> if (display_ == EGL_NO_DISPLAY) {
> - LOG(eGL, Error) << "Unable to get EGL display";
> - goto fail;
> - }
> -
> - if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE) {
> - LOG(eGL, Error) << "eglInitialize fail";
> + LOG(eGL, Error) << "Unable to probe display";
> goto fail;
> }
>
> diff --git a/src/libcamera/software_isp/software_isp.cpp
> b/src/libcamera/software_isp/software_isp.cpp
> index 781cf02f..ff8c3465 100644
> --- a/src/libcamera/software_isp/software_isp.cpp
> +++ b/src/libcamera/software_isp/software_isp.cpp
> @@ -119,8 +119,14 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
> }
> }
>
> - if (!softISPMode || softISPMode == "gpu")
> - debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm);
> + if (!softISPMode || softISPMode == "gpu") {
> + if (eGL::isAvailable()) {
> + debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm);
> + } else {
> + LOG(SoftwareIsp, Info)
> + << "EGL not available, falling back to CPU debayer";
> + }
> + }
>
> #endif
> if (!debayer_)
> --
> 2.34.1
>
Perhaps a further optimisation of this would be to bring back GBM surface support ?
i.e. our first version of GPUISP used a GBM surface which then transitioned to MESA surfaceless.
I wonder if you have tried GBM ? It should be easy enough to revert the change and test ?
---
bod
On 15/06/2026 09:23, Qi Hou wrote: > Hi Bryan O'Donoghue, > > Thanks for your comment. I gave a try to revert below two patches that > moved from GBM surface to surfaceless and tested on my i.MX8MM board. > > The good news is that DebayerEGL::start() no longer fails. However, the > output display is completely black. > > I’m not sure if this is a GBM surface configuration issue on i.MX8MM or > something else. Since I’m not very familiar with the GBM surface path. > > I think it might be better handled by someone like you with professional > expertise. > > My patch (the isAvailable() probe + CPU fallback) at least provides a > working path on platforms without surfaceless support. Would it make sense > > to merge this as a basic fix, and then add GBM surface fallback as a > follow-up ? Sure. Could you add something to the gpuisp todo log about adding GBM back in as a progressive fallback ? I'm sure I can get a GBM surface going again, unless you do it first. --- bod
Hi Bryan O'Donoghue, I don’t know why I did not receive the mail about your latest comment in https://patchwork.libcamera.org/patch/26874/ that stated: “Sure. Could you add something to the gpuisp todo log about adding GBM back in as a progressive fallback ? I'm sure I can get a GBM surface going again, unless you do it first. “ Just now I sent out v4 to add this entry to the file src/libcamera/software_isp/TODO.md. I’m not sure if I placed it to the right place. Please help review. Regards, Qi Hou From: Qi Hou Sent: Monday, June 15, 2026 4:24 PM To: Bryan O'Donoghue <bod.linux@nxsw.ie>; libcamera-devel@lists.libcamera.org Cc: Jared Hu <jared.hu@nxp.com>; Julien Vuillaumier <julien.vuillaumier@nxp.com> Subject: RE: [EXT] Re: [PATCH v3] libcamera: software_isp: Probe EGL availability before creating DebayerEGL Hi Bryan O'Donoghue, Thanks for your comment. I gave a try to revert below two patches that moved from GBM surface to surfaceless and tested on my i.MX8MM board. The good news is that DebayerEGL::start() no longer fails. However, the output display is completely black. I’m not sure if this is a GBM surface configuration issue on i.MX8MM or something else. Since I’m not very familiar with the GBM surface path. I think it might be better handled by someone like you with professional expertise. My patch (the isAvailable() probe + CPU fallback) at least provides a working path on platforms without surfaceless support. Would it make sense to merge this as a basic fix, and then add GBM surface fallback as a follow-up ? 1. commit 0c2ed9ebf99e3328efe7e480d194d333a8dc5568 Author: Robert Mader <robert.mader@collabora.com<mailto:robert.mader@collabora.com>> Date: Wed Jan 21 10:08:52 2026 +0100 egl: Use the Mesa surfaceless platform instead of GBM Mesa surfaceless platform appears to be a better fit for the use-case at hand: 1. Like GBM it is Mesa specific, so no change in supported setups is expected. If ever required, a fallback to the generic device platform could be added on top. 2. It leaves the complexity of selecting a renderer device to the driver, reducing code and dependencies. 3. It allows to use llvmpipe / software drivers without dri device, which can be useful on CI or debugging (with LIBGL_ALWAYS_SOFTWARE=1). Signed-off-by: Robert Mader <robert.mader@collabora.com<mailto:robert.mader@collabora.com>> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org<mailto:bryan.odonoghue@linaro.org>> Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org<mailto:bryan.odonoghue@linaro.org>> # sm8250/rb5, x1e/Dell Insprion14p Reviewed-by: Milan Zamazal <mzamazal@redhat.com<mailto:mzamazal@redhat.com>> Tested-by: Milan Zamazal <mzamazal@redhat.com<mailto:mzamazal@redhat.com>> # TI AM69 Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com<mailto:barnabas.pocze@ideasonboard.com>> # ThinkPad X1 Yoga Gen 7 + ov2740 Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com<mailto:laurent.pinchart@ideasonboard.com>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com<mailto:kieran.bingham@ideasonboard.com>> 1. commit 7350d6cc5dd1b5bc724749d21744c07879deb803 Author: Robert Mader <robert.mader@collabora.com<mailto:robert.mader@collabora.com>> Date: Wed Jan 21 10:08:54 2026 +0100 Revert "libcamera: software_isp: gbm: Add a GBM helper class for GPU surface access" GBM is not used any more - remove the helper class. This reverts commit c60b1ce8193841c2742b655097bb39ccbcb417c2. Signed-off-by: Robert Mader <robert.mader@collabora.com<mailto:robert.mader@collabora.com>> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org<mailto:bryan.odonoghue@linaro.org>> Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org<mailto:bryan.odonoghue@linaro.org>> # sm8250/rb5, x1e/Dell Insprion14p Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com<mailto:barnabas.pocze@ideasonboard.com>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com<mailto:laurent.pinchart@ideasonboard.com>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com<mailto:kieran.bingham@ideasonboard.com>> Regards, Qi Hou -----Original Message----- From: Bryan O'Donoghue <bod.linux@nxsw.ie<mailto:bod.linux@nxsw.ie>> Sent: Saturday, June 13, 2026 8:43 PM To: Qi Hou <qi.hou@nxp.com<mailto:qi.hou@nxp.com>>; libcamera-devel@lists.libcamera.org<mailto:libcamera-devel@lists.libcamera.org> Cc: Jared Hu <jared.hu@nxp.com<mailto:jared.hu@nxp.com>>; Julien Vuillaumier <julien.vuillaumier@nxp.com<mailto:julien.vuillaumier@nxp.com>> Subject: [EXT] Re: [PATCH v3] libcamera: software_isp: Probe EGL availability before creating DebayerEGL [Some people who received this message don't often get email from bod.linux@nxsw.ie<mailto:bod.linux@nxsw.ie>. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] Caution: This is an external email. Please take care when clicking links or opening attachments. When in doubt, report the message using the 'Report this email' button On 12/06/2026 03:56, Qi Hou wrote: > When HAVE_DEBAYER_EGL is enabled, the SoftwareIsp constructor > unconditionally creates a DebayerEGL instance. If the platform lacks > EGL surfaceless support, DebayerEGL::start() fails with -ENODEV after > SwStatsCpu ownership has already been moved, making fallback to > DebayerCpu impossible. > > Add a static eGL::isAvailable() method that probes whether EGL > surfaceless rendering can be initialised. Extract the common display > obtaining logic (eglBindAPI, eglGetPlatformDisplay, eglInitialize) > into a private eGL::probeDisplay() helper that is shared by both > isAvailable() and initEGLContext(), avoiding code duplication. > > The SoftwareIsp constructor now calls eGL::isAvailable() before > creating DebayerEGL. If EGL is not available, the existing fallback > path creates a DebayerCpu instance instead. > > Signed-off-by: Qi Hou <qi.hou@nxp.com<mailto:qi.hou@nxp.com>> > --- > include/libcamera/internal/egl.h | 2 + > src/libcamera/egl.cpp | 60 ++++++++++++++++----- > src/libcamera/software_isp/software_isp.cpp | 10 +++- > 3 files changed, 56 insertions(+), 16 deletions(-) > > diff --git a/include/libcamera/internal/egl.h > b/include/libcamera/internal/egl.h > index 57f90d93..f7bfb28d 100644 > --- a/include/libcamera/internal/egl.h > +++ b/include/libcamera/internal/egl.h > @@ -103,6 +103,7 @@ public: > ~eGL(); > > int initEGLContext(); > + static bool isAvailable(); > > int createInputDMABufTexture2D(eGLImage &eglImage, int fd); > int createOutputDMABufTexture2D(eGLImage &eglImage, int fd); @@ > -133,6 +134,7 @@ private: > EGLContext context_ = EGL_NO_CONTEXT; > EGLSurface surface_ = EGL_NO_SURFACE; > > + static EGLDisplay probeDisplay(); > int compileShader(int shaderType, GLuint &shaderId, const unsigned char *shaderData, > unsigned int shaderDataLen, > Span<const std::string> shaderEnv); diff --git > a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp index > c185bb7a..860f18dd 100644 > --- a/src/libcamera/egl.cpp > +++ b/src/libcamera/egl.cpp > @@ -267,6 +267,50 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data) > glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); > } > > +EGLDisplay eGL::probeDisplay() > +{ > + EGLDisplay display; > + > + if (!eglBindAPI(EGL_OPENGL_ES_API)) { > + LOG(eGL, Info) << "API bind fail"; > + return EGL_NO_DISPLAY; > + } > + > + display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, > + EGL_DEFAULT_DISPLAY, > + nullptr); > + > + if (display == EGL_NO_DISPLAY) { > + LOG(eGL, Info) << "Unable to get EGL display"; > + return EGL_NO_DISPLAY; > + } > + > + if (eglInitialize(display, nullptr, nullptr) != EGL_TRUE) { > + LOG(eGL, Error) << "eglInitialize fail"; > + return EGL_NO_DISPLAY; > + } > + > + return display; > +} > + > +/** > + * \brief Probe whether EGL surfaceless rendering is available > + * > + * Checks if an EGL surfaceless display can be obtained and initialised. > + * The display is immediately terminated so that no resources are leaked. > + * > + * \return true if EGL surfaceless rendering is available, false > +otherwise */ bool eGL::isAvailable() { > + EGLDisplay display = probeDisplay(); > + if (display == EGL_NO_DISPLAY) > + return false; > + > + eglTerminate(display); > + return true; > +} > + > /** > * \brief Initialise the EGL context > * > @@ -297,21 +341,9 @@ int eGL::initEGLContext() > EGLint numConfigs; > EGLConfig config; > > - if (!eglBindAPI(EGL_OPENGL_ES_API)) { > - LOG(eGL, Error) << "API bind fail"; > - goto fail; > - } > - > - display_ = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, > - EGL_DEFAULT_DISPLAY, > - nullptr); > + display_ = probeDisplay(); > if (display_ == EGL_NO_DISPLAY) { > - LOG(eGL, Error) << "Unable to get EGL display"; > - goto fail; > - } > - > - if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE) { > - LOG(eGL, Error) << "eglInitialize fail"; > + LOG(eGL, Error) << "Unable to probe display"; > goto fail; > } > > diff --git a/src/libcamera/software_isp/software_isp.cpp > b/src/libcamera/software_isp/software_isp.cpp > index 781cf02f..ff8c3465 100644 > --- a/src/libcamera/software_isp/software_isp.cpp > +++ b/src/libcamera/software_isp/software_isp.cpp > @@ -119,8 +119,14 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, > } > } > > - if (!softISPMode || softISPMode == "gpu") > - debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm); > + if (!softISPMode || softISPMode == "gpu") { > + if (eGL::isAvailable()) { > + debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm); > + } else { > + LOG(SoftwareIsp, Info) > + << "EGL not available, falling back to CPU debayer"; > + } > + } > > #endif > if (!debayer_) > -- > 2.34.1 > Perhaps a further optimisation of this would be to bring back GBM surface support ? i.e. our first version of GPUISP used a GBM surface which then transitioned to MESA surfaceless. I wonder if you have tried GBM ? It should be easy enough to revert the change and test ? --- bod
diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h index 57f90d93..f7bfb28d 100644 --- a/include/libcamera/internal/egl.h +++ b/include/libcamera/internal/egl.h @@ -103,6 +103,7 @@ public: ~eGL(); int initEGLContext(); + static bool isAvailable(); int createInputDMABufTexture2D(eGLImage &eglImage, int fd); int createOutputDMABufTexture2D(eGLImage &eglImage, int fd); @@ -133,6 +134,7 @@ private: EGLContext context_ = EGL_NO_CONTEXT; EGLSurface surface_ = EGL_NO_SURFACE; + static EGLDisplay probeDisplay(); int compileShader(int shaderType, GLuint &shaderId, const unsigned char *shaderData, unsigned int shaderDataLen, Span<const std::string> shaderEnv); diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp index c185bb7a..860f18dd 100644 --- a/src/libcamera/egl.cpp +++ b/src/libcamera/egl.cpp @@ -267,6 +267,50 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } +EGLDisplay eGL::probeDisplay() +{ + EGLDisplay display; + + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + LOG(eGL, Info) << "API bind fail"; + return EGL_NO_DISPLAY; + } + + display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, + EGL_DEFAULT_DISPLAY, + nullptr); + + if (display == EGL_NO_DISPLAY) { + LOG(eGL, Info) << "Unable to get EGL display"; + return EGL_NO_DISPLAY; + } + + if (eglInitialize(display, nullptr, nullptr) != EGL_TRUE) { + LOG(eGL, Error) << "eglInitialize fail"; + return EGL_NO_DISPLAY; + } + + return display; +} + +/** + * \brief Probe whether EGL surfaceless rendering is available + * + * Checks if an EGL surfaceless display can be obtained and initialised. + * The display is immediately terminated so that no resources are leaked. + * + * \return true if EGL surfaceless rendering is available, false otherwise + */ +bool eGL::isAvailable() +{ + EGLDisplay display = probeDisplay(); + if (display == EGL_NO_DISPLAY) + return false; + + eglTerminate(display); + return true; +} + /** * \brief Initialise the EGL context * @@ -297,21 +341,9 @@ int eGL::initEGLContext() EGLint numConfigs; EGLConfig config; - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - LOG(eGL, Error) << "API bind fail"; - goto fail; - } - - display_ = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, - EGL_DEFAULT_DISPLAY, - nullptr); + display_ = probeDisplay(); if (display_ == EGL_NO_DISPLAY) { - LOG(eGL, Error) << "Unable to get EGL display"; - goto fail; - } - - if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE) { - LOG(eGL, Error) << "eglInitialize fail"; + LOG(eGL, Error) << "Unable to probe display"; goto fail; } diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index 781cf02f..ff8c3465 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -119,8 +119,14 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, } } - if (!softISPMode || softISPMode == "gpu") - debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm); + if (!softISPMode || softISPMode == "gpu") { + if (eGL::isAvailable()) { + debayer_ = std::make_unique<DebayerEGL>(std::move(stats), cm); + } else { + LOG(SoftwareIsp, Info) + << "EGL not available, falling back to CPU debayer"; + } + } #endif if (!debayer_)
When HAVE_DEBAYER_EGL is enabled, the SoftwareIsp constructor unconditionally creates a DebayerEGL instance. If the platform lacks EGL surfaceless support, DebayerEGL::start() fails with -ENODEV after SwStatsCpu ownership has already been moved, making fallback to DebayerCpu impossible. Add a static eGL::isAvailable() method that probes whether EGL surfaceless rendering can be initialised. Extract the common display obtaining logic (eglBindAPI, eglGetPlatformDisplay, eglInitialize) into a private eGL::probeDisplay() helper that is shared by both isAvailable() and initEGLContext(), avoiding code duplication. The SoftwareIsp constructor now calls eGL::isAvailable() before creating DebayerEGL. If EGL is not available, the existing fallback path creates a DebayerCpu instance instead. Signed-off-by: Qi Hou <qi.hou@nxp.com> --- include/libcamera/internal/egl.h | 2 + src/libcamera/egl.cpp | 60 ++++++++++++++++----- src/libcamera/software_isp/software_isp.cpp | 10 +++- 3 files changed, 56 insertions(+), 16 deletions(-)