RFC: egl: Implement DMABuf import for input buffers
diff mbox series

Message ID 20260110210930.123920-1-robert.mader@collabora.com
State New
Headers show
Series
  • RFC: egl: Implement DMABuf import for input buffers
Related show

Commit Message

Robert Mader Jan. 10, 2026, 9:09 p.m. UTC
In many cases we can import the GPU-ISP input buffer, a dmabuf from v4l2,
directly into EGL instead of mapping and uploading - i.e. copying - it.
This reduces memory bandwith usage and can even slightly improve
latency.
This main reason this doesn't work in many cases is the stride
alignment, as GPUs often have stricter requirements (often 128 or even
256 bytes) than hardware ISPs.

Thus try to import buffer directly and - if that fails - fall back to
the previous upload path. To do so, adjust some function parameters and
turn down error messages. Failing imports should come at low cost as
drivers know the limitations and can bail out early, without causing
additional IO or context switches.

In the future we might be able to request buffers with a matching stride
from v4l2 drivers in many cases, making direct import the norm instead
of a hit-or-miss. An optional kernel API for that exists, but doesn't
seem to be implemented by any driver tested so far.

Signed-off-by: Robert Mader <robert.mader@collabora.com>

---

This patch should be applied on top of
https://patchwork.libcamera.org/cover/25706/
---
 include/libcamera/internal/egl.h           |  4 +--
 src/libcamera/egl.cpp                      | 37 +++++++++++++++++-----
 src/libcamera/software_isp/debayer_egl.cpp | 21 ++++++------
 src/libcamera/software_isp/debayer_egl.h   |  2 +-
 4 files changed, 44 insertions(+), 20 deletions(-)

Comments

Robert Mader Jan. 14, 2026, 11:12 a.m. UTC | #1
Just a note/reminder from the weekly call:

On 10.01.26 22:09, Robert Mader wrote:
> In many cases we can import the GPU-ISP input buffer, a dmabuf from v4l2,
> directly into EGL instead of mapping and uploading - i.e. copying - it.
> This reduces memory bandwith usage and can even slightly improve
> latency.
> This main reason this doesn't work in many cases is the stride
> alignment, as GPUs often have stricter requirements (often 128 or even
> 256 bytes) than hardware ISPs.
>
> Thus try to import buffer directly and - if that fails - fall back to
> the previous upload path. To do so, adjust some function parameters and
> turn down error messages. Failing imports should come at low cost as
> drivers know the limitations and can bail out early, without causing
> additional IO or context switches.
>
> In the future we might be able to request buffers with a matching stride
> from v4l2 drivers in many cases, making direct import the norm instead
> of a hit-or-miss. An optional kernel API for that exists, but doesn't
> seem to be implemented by any driver tested so far.
>
> Signed-off-by: Robert Mader <robert.mader@collabora.com>
>
> ---
>
> This patch should be applied on top of
> https://patchwork.libcamera.org/cover/25706/
> ---
>   include/libcamera/internal/egl.h           |  4 +--
>   src/libcamera/egl.cpp                      | 37 +++++++++++++++++-----
>   src/libcamera/software_isp/debayer_egl.cpp | 21 ++++++------
>   src/libcamera/software_isp/debayer_egl.h   |  2 +-
>   4 files changed, 44 insertions(+), 20 deletions(-)
>
> diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h
> index f007f448a..b61769542 100644
> --- a/include/libcamera/internal/egl.h
> +++ b/include/libcamera/internal/egl.h
> @@ -98,7 +98,7 @@ public:
>   
>   	int initEGLContext(GBM *gbmContext);
>   
> -	int createInputDMABufTexture2D(eGLImage &eglImage, int fd);
> +	int createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height);
>   	int createOutputDMABufTexture2D(eGLImage &eglImage, int fd);
>   	void destroyDMABufTexture(eGLImage &eglImage);
>   	void createTexture2D(eGLImage &eglImage, GLint format, uint32_t width, uint32_t height, void *data);
> @@ -131,7 +131,7 @@ private:
>   			  unsigned int shaderDataLen,
>   			  Span<const std::string> shaderEnv);
>   
> -	int createDMABufTexture2D(eGLImage &eglImage, int fd, bool output);
> +	int createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output);
>   
>   	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
>   	PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
> index 0ffd008c7..2853be7c0 100644
> --- a/src/libcamera/egl.cpp
> +++ b/src/libcamera/egl.cpp
> @@ -19,6 +19,7 @@
>   
>   #include <libcamera/base/thread.h>
>   
> +#include <GLES3/gl32.h>
>   #include <libdrm/drm_fourcc.h>
>   
>   namespace libcamera {
> @@ -102,6 +103,9 @@ void eGL::syncOutput()
>    * \brief Create a DMA-BUF backed 2D texture
>    * \param[in,out] eglImage EGL image to associate with the DMA-BUF
>    * \param[in] fd DMA-BUF file descriptor
> + * \param[in] drm_format the DRM fourcc
> + * \param[in] width the buffer width
> + * \param[in] height the buffer height
>    * \param[in] output If true, create framebuffer for render target
>    *
>    * Internal implementation for creating DMA-BUF textures. Creates an EGL
> @@ -110,7 +114,7 @@ void eGL::syncOutput()
>    *
>    * \return 0 on success, or -ENODEV on failure
>    */
> -int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
> +int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output)
>   {
>   	int ret = 0;
>   
> @@ -118,9 +122,9 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>   
>   	// clang-format off
>   	EGLint image_attrs[] = {
> -		EGL_WIDTH, (EGLint)eglImage.width_,
> -		EGL_HEIGHT, (EGLint)eglImage.height_,
> -		EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888,
> +		EGL_WIDTH, (EGLint)width,
> +		EGL_HEIGHT, (EGLint)height,
> +		EGL_LINUX_DRM_FOURCC_EXT, (EGLint)drm_format,
>   		EGL_DMA_BUF_PLANE0_FD_EXT, fd,
>   		EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
>   		EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)eglImage.stride_,
> @@ -135,7 +139,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>   					    NULL, image_attrs);
>   
>   	if (eglImage.image_ == EGL_NO_IMAGE_KHR) {
> -		LOG(eGL, Error) << "eglCreateImageKHR fail";
> +		LOG(eGL, Debug) << "eglCreateImageKHR fail";
>   		ret = -ENODEV;
>   		goto done;
>   	}
> @@ -176,6 +180,9 @@ done:
>    * \brief Create an input DMA-BUF backed texture
>    * \param[in,out] eglImage EGL image to associate with the DMA-BUF
>    * \param[in] fd DMA-BUF file descriptor
> + * \param[in] format the GL format
> + * \param[in] width the buffer width
> + * \param[in] height the buffer height
>    *
>    * Creates an EGL image from a DMA-BUF file descriptor and binds it to
>    * a 2D texture for use as an input texture in shaders. The texture is
> @@ -183,11 +190,25 @@ done:
>    *
>    * \return 0 on success, or -ENODEV on failure
>    */
> -int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd)
> +int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height)
>   {
> +	EGLint drm_format;
> +
>   	ASSERT(tid_ == Thread::currentId());
>   
> -	return createDMABufTexture2D(eglImage, fd, false);
> +	switch (format) {
> +	case GL_LUMINANCE:
> +		drm_format = DRM_FORMAT_R8;
> +		break;
> +	case GL_RG:
> +		drm_format = DRM_FORMAT_RG88;
> +		break;
> +	default:
> +		LOG(eGL, Error) << "unhandled GL format";
> +		return -ENODEV;
> +	}
> +
> +	return createDMABufTexture2D(eglImage, fd, drm_format, width, height, false);
>   }
>   
>   /**
> @@ -206,7 +227,7 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)
>   {
>   	ASSERT(tid_ == Thread::currentId());
>   
> -	return createDMABufTexture2D(eglImage, fd, true);
> +	return createDMABufTexture2D(eglImage, fd, DRM_FORMAT_ARGB8888, eglImage.width_, eglImage.height_, true);
>   }
>   
>   /**
> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
> index 9693d7252..e6387c851 100644
> --- a/src/libcamera/software_isp/debayer_egl.cpp
> +++ b/src/libcamera/software_isp/debayer_egl.cpp
> @@ -509,13 +509,22 @@ void DebayerEGL::setShaderVariableValues(DebayerParams &params)
>   	return;
>   }
>   
> -int DebayerEGL::debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params)
> +int DebayerEGL::debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params)
>   {
>   	/* eGL context switch */
>   	egl_.makeCurrent();
>   
>   	/* Create a standard texture input */
> -	egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
> +	if (egl_.createInputDMABufTexture2D(*eglImageBayerIn_, input->planes()[0].fd.get(), glFormat_, inputConfig_.stride / bytesPerPixel_, height_) != 0) {
We're currently trying the import on every frame instead of remembering 
whether it failed on previous calls. I'm relatively sure that drivers 
bail fairly early, before doing any syscalls/context switches, if e.g. 
the stride doesn't match - but then import might still fail later in the 
kernel in certain cases. Remembering that should be pretty straight 
forward, so I should probably just do that.
> +		MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
> +		if (!in.isValid()) {
> +			LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
> +			return -ENODEV;
> +		}
> +
> +		LOG(Debayer, Debug) << "Importing input buffer with DMABuf import failed, falling back to upload";
> +		egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
> +	}
>   
>   	/* Generate the output render framebuffer as render to texture */
>   	egl_.createOutputDMABufTexture2D(*eglImageBayerOut_, out_fd);
> @@ -552,13 +561,7 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output
>   	metadata.sequence = input->metadata().sequence;
>   	metadata.timestamp = input->metadata().timestamp;
>   
> -	MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
> -	if (!in.isValid()) {
> -		LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
> -		goto error;
> -	}
> -
> -	if (debayerGPU(in, output->planes()[0].fd.get(), params)) {
> +	if (debayerGPU(input, output->planes()[0].fd.get(), params)) {
>   		LOG(Debayer, Error) << "debayerGPU failed";
>   		goto error;
>   	}
> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
> index a5033bc63..4b2cf448f 100644
> --- a/src/libcamera/software_isp/debayer_egl.h
> +++ b/src/libcamera/software_isp/debayer_egl.h
> @@ -74,7 +74,7 @@ private:
>   	int getShaderVariableLocations();
>   	void setShaderVariableValues(DebayerParams &params);
>   	void configureTexture(GLuint &texture);
> -	int debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params);
> +	int debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params);
>   
>   	/* Shader program identifiers */
>   	GLuint vertexShaderId_ = 0;
Hans de Goede Jan. 14, 2026, 3:21 p.m. UTC | #2
Hi,

On 10-Jan-26 22:09, Robert Mader wrote:
> In many cases we can import the GPU-ISP input buffer, a dmabuf from v4l2,
> directly into EGL instead of mapping and uploading - i.e. copying - it.
> This reduces memory bandwith usage and can even slightly improve
> latency.
> This main reason this doesn't work in many cases is the stride
> alignment, as GPUs often have stricter requirements (often 128 or even
> 256 bytes) than hardware ISPs.
> 
> Thus try to import buffer directly and - if that fails - fall back to
> the previous upload path. To do so, adjust some function parameters and
> turn down error messages. Failing imports should come at low cost as
> drivers know the limitations and can bail out early, without causing
> additional IO or context switches.
> 
> In the future we might be able to request buffers with a matching stride
> from v4l2 drivers in many cases, making direct import the norm instead
> of a hit-or-miss. An optional kernel API for that exists, but doesn't
> seem to be implemented by any driver tested so far.
> 
> Signed-off-by: Robert Mader <robert.mader@collabora.com>

Thanks, I've given this a quick test run and not found any issues:

Tested-by: Hans de Goede <johannes.goede@oss.qualcomm.com> # Thinkpad X1 Carbon G13 IPU7 ov08x40

Note it did not seem to make anything faster on this device though,
so I guess we're hitting the fallback.

Regards,

Hans



> 
> ---
> 
> This patch should be applied on top of
> https://patchwork.libcamera.org/cover/25706/
> ---
>  include/libcamera/internal/egl.h           |  4 +--
>  src/libcamera/egl.cpp                      | 37 +++++++++++++++++-----
>  src/libcamera/software_isp/debayer_egl.cpp | 21 ++++++------
>  src/libcamera/software_isp/debayer_egl.h   |  2 +-
>  4 files changed, 44 insertions(+), 20 deletions(-)
> 
> diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h
> index f007f448a..b61769542 100644
> --- a/include/libcamera/internal/egl.h
> +++ b/include/libcamera/internal/egl.h
> @@ -98,7 +98,7 @@ public:
>  
>  	int initEGLContext(GBM *gbmContext);
>  
> -	int createInputDMABufTexture2D(eGLImage &eglImage, int fd);
> +	int createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height);
>  	int createOutputDMABufTexture2D(eGLImage &eglImage, int fd);
>  	void destroyDMABufTexture(eGLImage &eglImage);
>  	void createTexture2D(eGLImage &eglImage, GLint format, uint32_t width, uint32_t height, void *data);
> @@ -131,7 +131,7 @@ private:
>  			  unsigned int shaderDataLen,
>  			  Span<const std::string> shaderEnv);
>  
> -	int createDMABufTexture2D(eGLImage &eglImage, int fd, bool output);
> +	int createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output);
>  
>  	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
>  	PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
> index 0ffd008c7..2853be7c0 100644
> --- a/src/libcamera/egl.cpp
> +++ b/src/libcamera/egl.cpp
> @@ -19,6 +19,7 @@
>  
>  #include <libcamera/base/thread.h>
>  
> +#include <GLES3/gl32.h>
>  #include <libdrm/drm_fourcc.h>
>  
>  namespace libcamera {
> @@ -102,6 +103,9 @@ void eGL::syncOutput()
>   * \brief Create a DMA-BUF backed 2D texture
>   * \param[in,out] eglImage EGL image to associate with the DMA-BUF
>   * \param[in] fd DMA-BUF file descriptor
> + * \param[in] drm_format the DRM fourcc
> + * \param[in] width the buffer width
> + * \param[in] height the buffer height
>   * \param[in] output If true, create framebuffer for render target
>   *
>   * Internal implementation for creating DMA-BUF textures. Creates an EGL
> @@ -110,7 +114,7 @@ void eGL::syncOutput()
>   *
>   * \return 0 on success, or -ENODEV on failure
>   */
> -int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
> +int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output)
>  {
>  	int ret = 0;
>  
> @@ -118,9 +122,9 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>  
>  	// clang-format off
>  	EGLint image_attrs[] = {
> -		EGL_WIDTH, (EGLint)eglImage.width_,
> -		EGL_HEIGHT, (EGLint)eglImage.height_,
> -		EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888,
> +		EGL_WIDTH, (EGLint)width,
> +		EGL_HEIGHT, (EGLint)height,
> +		EGL_LINUX_DRM_FOURCC_EXT, (EGLint)drm_format,
>  		EGL_DMA_BUF_PLANE0_FD_EXT, fd,
>  		EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
>  		EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)eglImage.stride_,
> @@ -135,7 +139,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>  					    NULL, image_attrs);
>  
>  	if (eglImage.image_ == EGL_NO_IMAGE_KHR) {
> -		LOG(eGL, Error) << "eglCreateImageKHR fail";
> +		LOG(eGL, Debug) << "eglCreateImageKHR fail";
>  		ret = -ENODEV;
>  		goto done;
>  	}
> @@ -176,6 +180,9 @@ done:
>   * \brief Create an input DMA-BUF backed texture
>   * \param[in,out] eglImage EGL image to associate with the DMA-BUF
>   * \param[in] fd DMA-BUF file descriptor
> + * \param[in] format the GL format
> + * \param[in] width the buffer width
> + * \param[in] height the buffer height
>   *
>   * Creates an EGL image from a DMA-BUF file descriptor and binds it to
>   * a 2D texture for use as an input texture in shaders. The texture is
> @@ -183,11 +190,25 @@ done:
>   *
>   * \return 0 on success, or -ENODEV on failure
>   */
> -int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd)
> +int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height)
>  {
> +	EGLint drm_format;
> +
>  	ASSERT(tid_ == Thread::currentId());
>  
> -	return createDMABufTexture2D(eglImage, fd, false);
> +	switch (format) {
> +	case GL_LUMINANCE:
> +		drm_format = DRM_FORMAT_R8;
> +		break;
> +	case GL_RG:
> +		drm_format = DRM_FORMAT_RG88;
> +		break;
> +	default:
> +		LOG(eGL, Error) << "unhandled GL format";
> +		return -ENODEV;
> +	}
> +
> +	return createDMABufTexture2D(eglImage, fd, drm_format, width, height, false);
>  }
>  
>  /**
> @@ -206,7 +227,7 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)
>  {
>  	ASSERT(tid_ == Thread::currentId());
>  
> -	return createDMABufTexture2D(eglImage, fd, true);
> +	return createDMABufTexture2D(eglImage, fd, DRM_FORMAT_ARGB8888, eglImage.width_, eglImage.height_, true);
>  }
>  
>  /**
> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
> index 9693d7252..e6387c851 100644
> --- a/src/libcamera/software_isp/debayer_egl.cpp
> +++ b/src/libcamera/software_isp/debayer_egl.cpp
> @@ -509,13 +509,22 @@ void DebayerEGL::setShaderVariableValues(DebayerParams &params)
>  	return;
>  }
>  
> -int DebayerEGL::debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params)
> +int DebayerEGL::debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params)
>  {
>  	/* eGL context switch */
>  	egl_.makeCurrent();
>  
>  	/* Create a standard texture input */
> -	egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
> +	if (egl_.createInputDMABufTexture2D(*eglImageBayerIn_, input->planes()[0].fd.get(), glFormat_, inputConfig_.stride / bytesPerPixel_, height_) != 0) {
> +		MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
> +		if (!in.isValid()) {
> +			LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
> +			return -ENODEV;
> +		}
> +
> +		LOG(Debayer, Debug) << "Importing input buffer with DMABuf import failed, falling back to upload";
> +		egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
> +	}
>  
>  	/* Generate the output render framebuffer as render to texture */
>  	egl_.createOutputDMABufTexture2D(*eglImageBayerOut_, out_fd);
> @@ -552,13 +561,7 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output
>  	metadata.sequence = input->metadata().sequence;
>  	metadata.timestamp = input->metadata().timestamp;
>  
> -	MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
> -	if (!in.isValid()) {
> -		LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
> -		goto error;
> -	}
> -
> -	if (debayerGPU(in, output->planes()[0].fd.get(), params)) {
> +	if (debayerGPU(input, output->planes()[0].fd.get(), params)) {
>  		LOG(Debayer, Error) << "debayerGPU failed";
>  		goto error;
>  	}
> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
> index a5033bc63..4b2cf448f 100644
> --- a/src/libcamera/software_isp/debayer_egl.h
> +++ b/src/libcamera/software_isp/debayer_egl.h
> @@ -74,7 +74,7 @@ private:
>  	int getShaderVariableLocations();
>  	void setShaderVariableValues(DebayerParams &params);
>  	void configureTexture(GLuint &texture);
> -	int debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params);
> +	int debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params);
>  
>  	/* Shader program identifiers */
>  	GLuint vertexShaderId_ = 0;
Milan Zamazal Jan. 14, 2026, 4:30 p.m. UTC | #3
johannes.goede@oss.qualcomm.com writes:

> Hi,
>
> On 10-Jan-26 22:09, Robert Mader wrote:
>> In many cases we can import the GPU-ISP input buffer, a dmabuf from v4l2,
>> directly into EGL instead of mapping and uploading - i.e. copying - it.
>> This reduces memory bandwith usage and can even slightly improve
>> latency.
>> This main reason this doesn't work in many cases is the stride
>> alignment, as GPUs often have stricter requirements (often 128 or even
>> 256 bytes) than hardware ISPs.
>> 
>> Thus try to import buffer directly and - if that fails - fall back to
>> the previous upload path. To do so, adjust some function parameters and
>> turn down error messages. Failing imports should come at low cost as
>> drivers know the limitations and can bail out early, without causing
>> additional IO or context switches.
>> 
>> In the future we might be able to request buffers with a matching stride
>> from v4l2 drivers in many cases, making direct import the norm instead
>> of a hit-or-miss. An optional kernel API for that exists, but doesn't
>> seem to be implemented by any driver tested so far.
>> 
>> Signed-off-by: Robert Mader <robert.mader@collabora.com>
>
> Thanks, I've given this a quick test run and not found any issues:
>
> Tested-by: Hans de Goede <johannes.goede@oss.qualcomm.com> # Thinkpad X1 Carbon G13 IPU7 ov08x40
>
> Note it did not seem to make anything faster on this device though,
> so I guess we're hitting the fallback.

The same here.

Tested-by: Milan Zamazal <mzamazal@redhat.com> # TI AM69

> Regards,
>
> Hans
>
>
>
>> 
>> ---
>> 
>> This patch should be applied on top of
>> https://patchwork.libcamera.org/cover/25706/
>> ---
>>  include/libcamera/internal/egl.h           |  4 +--
>>  src/libcamera/egl.cpp                      | 37 +++++++++++++++++-----
>>  src/libcamera/software_isp/debayer_egl.cpp | 21 ++++++------
>>  src/libcamera/software_isp/debayer_egl.h   |  2 +-
>>  4 files changed, 44 insertions(+), 20 deletions(-)
>> 
>> diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h
>> index f007f448a..b61769542 100644
>> --- a/include/libcamera/internal/egl.h
>> +++ b/include/libcamera/internal/egl.h
>> @@ -98,7 +98,7 @@ public:
>>  
>>  	int initEGLContext(GBM *gbmContext);
>>  
>> -	int createInputDMABufTexture2D(eGLImage &eglImage, int fd);
>> +	int createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height);
>>  	int createOutputDMABufTexture2D(eGLImage &eglImage, int fd);
>>  	void destroyDMABufTexture(eGLImage &eglImage);
>>  	void createTexture2D(eGLImage &eglImage, GLint format, uint32_t width, uint32_t height, void *data);
>> @@ -131,7 +131,7 @@ private:
>>  			  unsigned int shaderDataLen,
>>  			  Span<const std::string> shaderEnv);
>>  
>> -	int createDMABufTexture2D(eGLImage &eglImage, int fd, bool output);
>> +	int createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output);
>>  
>>  	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
>>  	PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
>> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
>> index 0ffd008c7..2853be7c0 100644
>> --- a/src/libcamera/egl.cpp
>> +++ b/src/libcamera/egl.cpp
>> @@ -19,6 +19,7 @@
>>  
>>  #include <libcamera/base/thread.h>
>>  
>> +#include <GLES3/gl32.h>
>>  #include <libdrm/drm_fourcc.h>
>>  
>>  namespace libcamera {
>> @@ -102,6 +103,9 @@ void eGL::syncOutput()
>>   * \brief Create a DMA-BUF backed 2D texture
>>   * \param[in,out] eglImage EGL image to associate with the DMA-BUF
>>   * \param[in] fd DMA-BUF file descriptor
>> + * \param[in] drm_format the DRM fourcc
>> + * \param[in] width the buffer width
>> + * \param[in] height the buffer height
>>   * \param[in] output If true, create framebuffer for render target
>>   *
>>   * Internal implementation for creating DMA-BUF textures. Creates an EGL
>> @@ -110,7 +114,7 @@ void eGL::syncOutput()
>>   *
>>   * \return 0 on success, or -ENODEV on failure
>>   */
>> -int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>> +int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output)
>>  {
>>  	int ret = 0;
>>  
>> @@ -118,9 +122,9 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>>  
>>  	// clang-format off
>>  	EGLint image_attrs[] = {
>> -		EGL_WIDTH, (EGLint)eglImage.width_,
>> -		EGL_HEIGHT, (EGLint)eglImage.height_,
>> -		EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888,
>> +		EGL_WIDTH, (EGLint)width,
>> +		EGL_HEIGHT, (EGLint)height,
>> +		EGL_LINUX_DRM_FOURCC_EXT, (EGLint)drm_format,
>>  		EGL_DMA_BUF_PLANE0_FD_EXT, fd,
>>  		EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
>>  		EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)eglImage.stride_,
>> @@ -135,7 +139,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>>  					    NULL, image_attrs);
>>  
>>  	if (eglImage.image_ == EGL_NO_IMAGE_KHR) {
>> -		LOG(eGL, Error) << "eglCreateImageKHR fail";
>> +		LOG(eGL, Debug) << "eglCreateImageKHR fail";
>>  		ret = -ENODEV;
>>  		goto done;
>>  	}
>> @@ -176,6 +180,9 @@ done:
>>   * \brief Create an input DMA-BUF backed texture
>>   * \param[in,out] eglImage EGL image to associate with the DMA-BUF
>>   * \param[in] fd DMA-BUF file descriptor
>> + * \param[in] format the GL format
>> + * \param[in] width the buffer width
>> + * \param[in] height the buffer height
>>   *
>>   * Creates an EGL image from a DMA-BUF file descriptor and binds it to
>>   * a 2D texture for use as an input texture in shaders. The texture is
>> @@ -183,11 +190,25 @@ done:
>>   *
>>   * \return 0 on success, or -ENODEV on failure
>>   */
>> -int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd)
>> +int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height)
>>  {
>> +	EGLint drm_format;
>> +
>>  	ASSERT(tid_ == Thread::currentId());
>>  
>> -	return createDMABufTexture2D(eglImage, fd, false);
>> +	switch (format) {
>> +	case GL_LUMINANCE:
>> +		drm_format = DRM_FORMAT_R8;
>> +		break;
>> +	case GL_RG:
>> +		drm_format = DRM_FORMAT_RG88;
>> +		break;
>> +	default:
>> +		LOG(eGL, Error) << "unhandled GL format";
>> +		return -ENODEV;
>> +	}
>> +
>> +	return createDMABufTexture2D(eglImage, fd, drm_format, width, height, false);
>>  }
>>  
>>  /**
>> @@ -206,7 +227,7 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)
>>  {
>>  	ASSERT(tid_ == Thread::currentId());
>>  
>> -	return createDMABufTexture2D(eglImage, fd, true);
>> +	return createDMABufTexture2D(eglImage, fd, DRM_FORMAT_ARGB8888, eglImage.width_, eglImage.height_, true);
>>  }
>>  
>>  /**
>> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
>> index 9693d7252..e6387c851 100644
>> --- a/src/libcamera/software_isp/debayer_egl.cpp
>> +++ b/src/libcamera/software_isp/debayer_egl.cpp
>> @@ -509,13 +509,22 @@ void DebayerEGL::setShaderVariableValues(DebayerParams &params)
>>  	return;
>>  }
>>  
>> -int DebayerEGL::debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params)
>> +int DebayerEGL::debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params)
>>  {
>>  	/* eGL context switch */
>>  	egl_.makeCurrent();
>>  
>>  	/* Create a standard texture input */
>> -	egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
>> +	if (egl_.createInputDMABufTexture2D(*eglImageBayerIn_, input->planes()[0].fd.get(), glFormat_, inputConfig_.stride / bytesPerPixel_, height_) != 0) {
>> +		MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
>> +		if (!in.isValid()) {
>> +			LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
>> +			return -ENODEV;
>> +		}
>> +
>> +		LOG(Debayer, Debug) << "Importing input buffer with DMABuf import failed, falling back to upload";
>> +		egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
>> +	}
>>  
>>  	/* Generate the output render framebuffer as render to texture */
>>  	egl_.createOutputDMABufTexture2D(*eglImageBayerOut_, out_fd);
>> @@ -552,13 +561,7 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output
>>  	metadata.sequence = input->metadata().sequence;
>>  	metadata.timestamp = input->metadata().timestamp;
>>  
>> -	MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
>> -	if (!in.isValid()) {
>> -		LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
>> -		goto error;
>> -	}
>> -
>> -	if (debayerGPU(in, output->planes()[0].fd.get(), params)) {
>> +	if (debayerGPU(input, output->planes()[0].fd.get(), params)) {
>>  		LOG(Debayer, Error) << "debayerGPU failed";
>>  		goto error;
>>  	}
>> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
>> index a5033bc63..4b2cf448f 100644
>> --- a/src/libcamera/software_isp/debayer_egl.h
>> +++ b/src/libcamera/software_isp/debayer_egl.h
>> @@ -74,7 +74,7 @@ private:
>>  	int getShaderVariableLocations();
>>  	void setShaderVariableValues(DebayerParams &params);
>>  	void configureTexture(GLuint &texture);
>> -	int debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params);
>> +	int debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params);
>>  
>>  	/* Shader program identifiers */
>>  	GLuint vertexShaderId_ = 0;
Barnabás Pőcze Jan. 15, 2026, 10:19 a.m. UTC | #4
2026. 01. 10. 22:09 keltezéssel, Robert Mader írta:
> In many cases we can import the GPU-ISP input buffer, a dmabuf from v4l2,
> directly into EGL instead of mapping and uploading - i.e. copying - it.
> This reduces memory bandwith usage and can even slightly improve
> latency.
> This main reason this doesn't work in many cases is the stride
> alignment, as GPUs often have stricter requirements (often 128 or even
> 256 bytes) than hardware ISPs.
> 
> Thus try to import buffer directly and - if that fails - fall back to
> the previous upload path. To do so, adjust some function parameters and
> turn down error messages. Failing imports should come at low cost as
> drivers know the limitations and can bail out early, without causing
> additional IO or context switches.
> 
> In the future we might be able to request buffers with a matching stride
> from v4l2 drivers in many cases, making direct import the norm instead
> of a hit-or-miss. An optional kernel API for that exists, but doesn't
> seem to be implemented by any driver tested so far.
> 
> Signed-off-by: Robert Mader <robert.mader@collabora.com>
> 
> ---
> 
> This patch should be applied on top of
> https://patchwork.libcamera.org/cover/25706/
> ---
>   include/libcamera/internal/egl.h           |  4 +--
>   src/libcamera/egl.cpp                      | 37 +++++++++++++++++-----
>   src/libcamera/software_isp/debayer_egl.cpp | 21 ++++++------
>   src/libcamera/software_isp/debayer_egl.h   |  2 +-
>   4 files changed, 44 insertions(+), 20 deletions(-)
> 
> diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h
> index f007f448a..b61769542 100644
> --- a/include/libcamera/internal/egl.h
> +++ b/include/libcamera/internal/egl.h
> @@ -98,7 +98,7 @@ public:
>   
>   	int initEGLContext(GBM *gbmContext);
>   
> -	int createInputDMABufTexture2D(eGLImage &eglImage, int fd);
> +	int createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height);
>   	int createOutputDMABufTexture2D(eGLImage &eglImage, int fd);
>   	void destroyDMABufTexture(eGLImage &eglImage);
>   	void createTexture2D(eGLImage &eglImage, GLint format, uint32_t width, uint32_t height, void *data);
> @@ -131,7 +131,7 @@ private:
>   			  unsigned int shaderDataLen,
>   			  Span<const std::string> shaderEnv);
>   
> -	int createDMABufTexture2D(eGLImage &eglImage, int fd, bool output);
> +	int createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output);
>   
>   	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
>   	PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
> index 0ffd008c7..2853be7c0 100644
> --- a/src/libcamera/egl.cpp
> +++ b/src/libcamera/egl.cpp
> @@ -19,6 +19,7 @@
>   
>   #include <libcamera/base/thread.h>
>   
> +#include <GLES3/gl32.h>
>   #include <libdrm/drm_fourcc.h>
>   
>   namespace libcamera {
> @@ -102,6 +103,9 @@ void eGL::syncOutput()
>    * \brief Create a DMA-BUF backed 2D texture
>    * \param[in,out] eglImage EGL image to associate with the DMA-BUF
>    * \param[in] fd DMA-BUF file descriptor
> + * \param[in] drm_format the DRM fourcc
> + * \param[in] width the buffer width
> + * \param[in] height the buffer height
>    * \param[in] output If true, create framebuffer for render target
>    *
>    * Internal implementation for creating DMA-BUF textures. Creates an EGL
> @@ -110,7 +114,7 @@ void eGL::syncOutput()
>    *
>    * \return 0 on success, or -ENODEV on failure
>    */
> -int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
> +int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output)
>   {
>   	int ret = 0;
>   
> @@ -118,9 +122,9 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>   
>   	// clang-format off
>   	EGLint image_attrs[] = {
> -		EGL_WIDTH, (EGLint)eglImage.width_,
> -		EGL_HEIGHT, (EGLint)eglImage.height_,
> -		EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888,
> +		EGL_WIDTH, (EGLint)width,
> +		EGL_HEIGHT, (EGLint)height,
> +		EGL_LINUX_DRM_FOURCC_EXT, (EGLint)drm_format,
>   		EGL_DMA_BUF_PLANE0_FD_EXT, fd,
>   		EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
>   		EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)eglImage.stride_,
> @@ -135,7 +139,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>   					    NULL, image_attrs);
>   
>   	if (eglImage.image_ == EGL_NO_IMAGE_KHR) {
> -		LOG(eGL, Error) << "eglCreateImageKHR fail";
> +		LOG(eGL, Debug) << "eglCreateImageKHR fail";
>   		ret = -ENODEV;
>   		goto done;
>   	}
> @@ -176,6 +180,9 @@ done:
>    * \brief Create an input DMA-BUF backed texture
>    * \param[in,out] eglImage EGL image to associate with the DMA-BUF
>    * \param[in] fd DMA-BUF file descriptor
> + * \param[in] format the GL format
> + * \param[in] width the buffer width
> + * \param[in] height the buffer height
>    *
>    * Creates an EGL image from a DMA-BUF file descriptor and binds it to
>    * a 2D texture for use as an input texture in shaders. The texture is
> @@ -183,11 +190,25 @@ done:
>    *
>    * \return 0 on success, or -ENODEV on failure
>    */
> -int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd)
> +int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height)
>   {
> +	EGLint drm_format;
> +
>   	ASSERT(tid_ == Thread::currentId());
>   
> -	return createDMABufTexture2D(eglImage, fd, false);
> +	switch (format) {
> +	case GL_LUMINANCE:
> +		drm_format = DRM_FORMAT_R8;
> +		break;
> +	case GL_RG:
> +		drm_format = DRM_FORMAT_RG88;
> +		break;
> +	default:
> +		LOG(eGL, Error) << "unhandled GL format";
> +		return -ENODEV;
> +	}
> +
> +	return createDMABufTexture2D(eglImage, fd, drm_format, width, height, false);
>   }
>   
>   /**
> @@ -206,7 +227,7 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)
>   {
>   	ASSERT(tid_ == Thread::currentId());
>   
> -	return createDMABufTexture2D(eglImage, fd, true);
> +	return createDMABufTexture2D(eglImage, fd, DRM_FORMAT_ARGB8888, eglImage.width_, eglImage.height_, true);
>   }
>   
>   /**
> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
> index 9693d7252..e6387c851 100644
> --- a/src/libcamera/software_isp/debayer_egl.cpp
> +++ b/src/libcamera/software_isp/debayer_egl.cpp
> @@ -509,13 +509,22 @@ void DebayerEGL::setShaderVariableValues(DebayerParams &params)
>   	return;
>   }
>   
> -int DebayerEGL::debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params)
> +int DebayerEGL::debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params)
>   {
>   	/* eGL context switch */
>   	egl_.makeCurrent();
>   
>   	/* Create a standard texture input */
> -	egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
> +	if (egl_.createInputDMABufTexture2D(*eglImageBayerIn_, input->planes()[0].fd.get(), glFormat_, inputConfig_.stride / bytesPerPixel_, height_) != 0) {
> +		MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
> +		if (!in.isValid()) {
> +			LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
> +			return -ENODEV;
> +		}
> +
> +		LOG(Debayer, Debug) << "Importing input buffer with DMABuf import failed, falling back to upload";
> +		egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
> +	}
>   
>   	/* Generate the output render framebuffer as render to texture */
>   	egl_.createOutputDMABufTexture2D(*eglImageBayerOut_, out_fd);

I think I'm a bit confused about this part (existing code as well).
`createOutputDMABufTexture2D()` unconditionally overwrites the `EGLImageKHR`
handle in the `eGLImage::image_`. I traced it with gdb it would appear that
it is continuously leaking handles. Am I missing something?

And of course the same applies to `eglImageBayerIn_` in the introduced, code, no?
(Although I can't test that part because import fails.)

I have tested:

diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 3543a678f..9df57d863 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -528,6 +528,9 @@ int DebayerEGL::debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params
  
         /* Generate the output render framebuffer as render to texture */
         egl_.createOutputDMABufTexture2D(*eglImageBayerOut_, out_fd);
+       utils::scope_exit outImageGuard([&] {
+               egl_.destroyDMABufTexture(*eglImageBayerOut_);
+       });
  
         setShaderVariableValues(params);
         glViewport(0, 0, width_, height_);

and things seem to work, but I'm not sure.

This also brings us to the question: why aren't `eglImageBayer{In,Out}_` simple local variables?


Regards,
Barnabás Pőcze

> @@ -552,13 +561,7 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output
>   	metadata.sequence = input->metadata().sequence;
>   	metadata.timestamp = input->metadata().timestamp;
>   
> -	MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
> -	if (!in.isValid()) {
> -		LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
> -		goto error;
> -	}
> -
> -	if (debayerGPU(in, output->planes()[0].fd.get(), params)) {
> +	if (debayerGPU(input, output->planes()[0].fd.get(), params)) {
>   		LOG(Debayer, Error) << "debayerGPU failed";
>   		goto error;
>   	}
> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
> index a5033bc63..4b2cf448f 100644
> --- a/src/libcamera/software_isp/debayer_egl.h
> +++ b/src/libcamera/software_isp/debayer_egl.h
> @@ -74,7 +74,7 @@ private:
>   	int getShaderVariableLocations();
>   	void setShaderVariableValues(DebayerParams &params);
>   	void configureTexture(GLuint &texture);
> -	int debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params);
> +	int debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params);
>   
>   	/* Shader program identifiers */
>   	GLuint vertexShaderId_ = 0;
Bryan O'Donoghue Jan. 16, 2026, 10:27 p.m. UTC | #5
On 15/01/2026 10:19, Barnabás Pőcze wrote:
> This also brings us to the question: why aren't `eglImageBayer{In,Out}_` simple local variables?

Apart from stuffing an object onto the stack, and consuming memory we 
don't need to for each frame - you also don't want to do glGenTextures() 
and glGenFrameBuffers for each frame for no reason.

Do it once at an init phase and save cycles/memory. IMO members > local 
variables for this use case.

---
bod
Bryan O'Donoghue Jan. 16, 2026, 10:30 p.m. UTC | #6
On 10/01/2026 21:09, Robert Mader wrote:
>   	/* Create a standard texture input */
> -	egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
> +	if (egl_.createInputDMABufTexture2D(*eglImageBayerIn_, input->planes()[0].fd.get(), glFormat_, inputConfig_.stride / bytesPerPixel_, height_) != 0) {
> +		MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
> +		if (!in.isValid()) {
> +			LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
> +			return -ENODEV;
> +		}
> +
> +		LOG(Debayer, Debug) << "Importing input buffer with DMABuf import failed, falling back to upload";
> +		egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
> +	}

I think we should have an overloaded configure() function that passes 
input and tries this once, setting a flag or hooking a function pointer 
so that we don't run this test again and again for each frame.

---
bod

Patch
diff mbox series

diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h
index f007f448a..b61769542 100644
--- a/include/libcamera/internal/egl.h
+++ b/include/libcamera/internal/egl.h
@@ -98,7 +98,7 @@  public:
 
 	int initEGLContext(GBM *gbmContext);
 
-	int createInputDMABufTexture2D(eGLImage &eglImage, int fd);
+	int createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height);
 	int createOutputDMABufTexture2D(eGLImage &eglImage, int fd);
 	void destroyDMABufTexture(eGLImage &eglImage);
 	void createTexture2D(eGLImage &eglImage, GLint format, uint32_t width, uint32_t height, void *data);
@@ -131,7 +131,7 @@  private:
 			  unsigned int shaderDataLen,
 			  Span<const std::string> shaderEnv);
 
-	int createDMABufTexture2D(eGLImage &eglImage, int fd, bool output);
+	int createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output);
 
 	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
 	PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
index 0ffd008c7..2853be7c0 100644
--- a/src/libcamera/egl.cpp
+++ b/src/libcamera/egl.cpp
@@ -19,6 +19,7 @@ 
 
 #include <libcamera/base/thread.h>
 
+#include <GLES3/gl32.h>
 #include <libdrm/drm_fourcc.h>
 
 namespace libcamera {
@@ -102,6 +103,9 @@  void eGL::syncOutput()
  * \brief Create a DMA-BUF backed 2D texture
  * \param[in,out] eglImage EGL image to associate with the DMA-BUF
  * \param[in] fd DMA-BUF file descriptor
+ * \param[in] drm_format the DRM fourcc
+ * \param[in] width the buffer width
+ * \param[in] height the buffer height
  * \param[in] output If true, create framebuffer for render target
  *
  * Internal implementation for creating DMA-BUF textures. Creates an EGL
@@ -110,7 +114,7 @@  void eGL::syncOutput()
  *
  * \return 0 on success, or -ENODEV on failure
  */
-int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
+int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, uint32_t drm_format, uint32_t width, uint32_t height, bool output)
 {
 	int ret = 0;
 
@@ -118,9 +122,9 @@  int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
 
 	// clang-format off
 	EGLint image_attrs[] = {
-		EGL_WIDTH, (EGLint)eglImage.width_,
-		EGL_HEIGHT, (EGLint)eglImage.height_,
-		EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888,
+		EGL_WIDTH, (EGLint)width,
+		EGL_HEIGHT, (EGLint)height,
+		EGL_LINUX_DRM_FOURCC_EXT, (EGLint)drm_format,
 		EGL_DMA_BUF_PLANE0_FD_EXT, fd,
 		EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
 		EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)eglImage.stride_,
@@ -135,7 +139,7 @@  int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
 					    NULL, image_attrs);
 
 	if (eglImage.image_ == EGL_NO_IMAGE_KHR) {
-		LOG(eGL, Error) << "eglCreateImageKHR fail";
+		LOG(eGL, Debug) << "eglCreateImageKHR fail";
 		ret = -ENODEV;
 		goto done;
 	}
@@ -176,6 +180,9 @@  done:
  * \brief Create an input DMA-BUF backed texture
  * \param[in,out] eglImage EGL image to associate with the DMA-BUF
  * \param[in] fd DMA-BUF file descriptor
+ * \param[in] format the GL format
+ * \param[in] width the buffer width
+ * \param[in] height the buffer height
  *
  * Creates an EGL image from a DMA-BUF file descriptor and binds it to
  * a 2D texture for use as an input texture in shaders. The texture is
@@ -183,11 +190,25 @@  done:
  *
  * \return 0 on success, or -ENODEV on failure
  */
-int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd)
+int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd, GLint format, uint32_t width, uint32_t height)
 {
+	EGLint drm_format;
+
 	ASSERT(tid_ == Thread::currentId());
 
-	return createDMABufTexture2D(eglImage, fd, false);
+	switch (format) {
+	case GL_LUMINANCE:
+		drm_format = DRM_FORMAT_R8;
+		break;
+	case GL_RG:
+		drm_format = DRM_FORMAT_RG88;
+		break;
+	default:
+		LOG(eGL, Error) << "unhandled GL format";
+		return -ENODEV;
+	}
+
+	return createDMABufTexture2D(eglImage, fd, drm_format, width, height, false);
 }
 
 /**
@@ -206,7 +227,7 @@  int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)
 {
 	ASSERT(tid_ == Thread::currentId());
 
-	return createDMABufTexture2D(eglImage, fd, true);
+	return createDMABufTexture2D(eglImage, fd, DRM_FORMAT_ARGB8888, eglImage.width_, eglImage.height_, true);
 }
 
 /**
diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 9693d7252..e6387c851 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -509,13 +509,22 @@  void DebayerEGL::setShaderVariableValues(DebayerParams &params)
 	return;
 }
 
-int DebayerEGL::debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params)
+int DebayerEGL::debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params)
 {
 	/* eGL context switch */
 	egl_.makeCurrent();
 
 	/* Create a standard texture input */
-	egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
+	if (egl_.createInputDMABufTexture2D(*eglImageBayerIn_, input->planes()[0].fd.get(), glFormat_, inputConfig_.stride / bytesPerPixel_, height_) != 0) {
+		MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
+		if (!in.isValid()) {
+			LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
+			return -ENODEV;
+		}
+
+		LOG(Debayer, Debug) << "Importing input buffer with DMABuf import failed, falling back to upload";
+		egl_.createTexture2D(*eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());
+	}
 
 	/* Generate the output render framebuffer as render to texture */
 	egl_.createOutputDMABufTexture2D(*eglImageBayerOut_, out_fd);
@@ -552,13 +561,7 @@  void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output
 	metadata.sequence = input->metadata().sequence;
 	metadata.timestamp = input->metadata().timestamp;
 
-	MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
-	if (!in.isValid()) {
-		LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
-		goto error;
-	}
-
-	if (debayerGPU(in, output->planes()[0].fd.get(), params)) {
+	if (debayerGPU(input, output->planes()[0].fd.get(), params)) {
 		LOG(Debayer, Error) << "debayerGPU failed";
 		goto error;
 	}
diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
index a5033bc63..4b2cf448f 100644
--- a/src/libcamera/software_isp/debayer_egl.h
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -74,7 +74,7 @@  private:
 	int getShaderVariableLocations();
 	void setShaderVariableValues(DebayerParams &params);
 	void configureTexture(GLuint &texture);
-	int debayerGPU(MappedFrameBuffer &in, int out_fd, DebayerParams &params);
+	int debayerGPU(FrameBuffer *input, int out_fd, DebayerParams &params);
 
 	/* Shader program identifiers */
 	GLuint vertexShaderId_ = 0;