libcamera: egl: Fix DRM format for GL_RG dmabuf import
diff mbox series

Message ID 20261021065624.153207-1-faizel.kb@gmail.com
State New
Headers show
Series
  • libcamera: egl: Fix DRM format for GL_RG dmabuf import
Related show

Commit Message

Faizel K B Oct. 21, 2026, 6:56 a.m. UTC
Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
dmabufs always fails with Mesa, and DebayerEGL silently falls back to
mapping each input buffer and uploading it with glTexImage2D() on every
frame:

  INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, falling back to upload

The cause is the DRM format used for GL_RG textures. Unpacked Bayer
pixels are 16-bit little-endian words, loaded into a GL_RG texture with
the low byte in .r and the high byte in .g, which the shaders recombine
into a single sample. R and G here are texture channels, not colours.
GL names formats in memory byte order, while DRM fourccs describe the
components of a little-endian word from MSB to LSB:

  DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, byte 1 = R
  DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, byte 1 = G

The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
byte order. Mesa has no RG88 entry in its DRI image format table, so
eglCreateImageKHR() fails.

Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
output path (GL_RGBA) are not affected.

Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage constructor")
Signed-off-by: Faizel K B <faizel.kb@gmail.com>
---

Notes:
    Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an IMX415
    sensor (SGBRG10 unpacked, 3864x2192).
    
    Panfrost requires the row stride of linear dmabuf imports to be
    64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 bytes, so
    the import also needed a local rkcif change to honour the 256-byte
    aligned stride (7936) that DebayerEGL::preferredInputStride() requests.

 src/libcamera/egl.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Robert Mader Oct. 21, 2026, 9:13 a.m. UTC | #1
Hi, thanks for the patch!

On 21.10.26 08:56, Faizel K B wrote:
> Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
> dmabufs always fails with Mesa, and DebayerEGL silently falls back to
> mapping each input buffer and uploading it with glTexImage2D() on every
> frame:
>
>    INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, falling back to upload
>
> The cause is the DRM format used for GL_RG textures. Unpacked Bayer
> pixels are 16-bit little-endian words, loaded into a GL_RG texture with
> the low byte in .r and the high byte in .g, which the shaders recombine
> into a single sample. R and G here are texture channels, not colours.
> GL names formats in memory byte order, while DRM fourccs describe the
> components of a little-endian word from MSB to LSB:
>
>    DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, byte 1 = R
>    DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, byte 1 = G
>
> The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
> byte order. Mesa has no RG88 entry in its DRI image format table, so
> eglCreateImageKHR() fails.
>
> Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
> supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
> output path (GL_RGBA) are not affected.
>
> Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage constructor")
> Signed-off-by: Faizel K B <faizel.kb@gmail.com>
> ---
>
> Notes:
>      Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an IMX415
>      sensor (SGBRG10 unpacked, 3864x2192).
>      
>      Panfrost requires the row stride of linear dmabuf imports to be
>      64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 bytes, so
>      the import also needed a local rkcif change to honour the 256-byte
>      aligned stride (7936) that DebayerEGL::preferredInputStride() requests.
Note that V4L2 drivers don't need to honor the bytesperline request - 
you could also just align things to 64 bytes (7744) if that's enough for 
Panfrost. But optimally the upstream driver would honor the request and 
not hardcode any values - any chance you're looking into working on 
that? :)
>
>   src/libcamera/egl.cpp | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
> index 3cde9040a..59a7d8cf9 100644
> --- a/src/libcamera/egl.cpp
> +++ b/src/libcamera/egl.cpp
> @@ -184,7 +184,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>   		drm_format = DRM_FORMAT_R8;
>   		break;
>   	case GL_RG:
> -		drm_format = DRM_FORMAT_RG88;
> +		drm_format = DRM_FORMAT_GR88;

Good catch!

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

>   		break;
>   	case GL_RGBA:
>   		drm_format = DRM_FORMAT_ARGB8888;
Barnabás Pőcze Oct. 21, 2026, 10:30 a.m. UTC | #2
Hi

2026. 10. 21. 8:56 keltezéssel, Faizel K B írta:
> Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
> dmabufs always fails with Mesa, and DebayerEGL silently falls back to
> mapping each input buffer and uploading it with glTexImage2D() on every
> frame:
> 
>    INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, falling back to upload
> 
> The cause is the DRM format used for GL_RG textures. Unpacked Bayer
> pixels are 16-bit little-endian words, loaded into a GL_RG texture with
> the low byte in .r and the high byte in .g, which the shaders recombine
> into a single sample. R and G here are texture channels, not colours.
> GL names formats in memory byte order, while DRM fourccs describe the
> components of a little-endian word from MSB to LSB:
> 
>    DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, byte 1 = R
>    DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, byte 1 = G
> 
> The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
> byte order. Mesa has no RG88 entry in its DRI image format table, so
> eglCreateImageKHR() fails.
> 
> Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
> supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
> output path (GL_RGBA) are not affected.

The only thing that's not entirely clear to me is why GL_RGBA is not affected.

Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> # ipu6 + ov2740


> 
> Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage constructor")
> Signed-off-by: Faizel K B <faizel.kb@gmail.com>
> ---
> 
> Notes:
>      Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an IMX415
>      sensor (SGBRG10 unpacked, 3864x2192).
>      
>      Panfrost requires the row stride of linear dmabuf imports to be
>      64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 bytes, so
>      the import also needed a local rkcif change to honour the 256-byte
>      aligned stride (7936) that DebayerEGL::preferredInputStride() requests.
> 
>   src/libcamera/egl.cpp | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
> index 3cde9040a..59a7d8cf9 100644
> --- a/src/libcamera/egl.cpp
> +++ b/src/libcamera/egl.cpp
> @@ -184,7 +184,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>   		drm_format = DRM_FORMAT_R8;
>   		break;
>   	case GL_RG:
> -		drm_format = DRM_FORMAT_RG88;
> +		drm_format = DRM_FORMAT_GR88;
>   		break;
>   	case GL_RGBA:
>   		drm_format = DRM_FORMAT_ARGB8888;
Robert Mader Oct. 21, 2026, 11:29 a.m. UTC | #3
Hi,

On 21.10.26 12:30, Barnabás Pőcze wrote:
> Hi
>
> 2026. 10. 21. 8:56 keltezéssel, Faizel K B írta:
>> Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
>> dmabufs always fails with Mesa, and DebayerEGL silently falls back to
>> mapping each input buffer and uploading it with glTexImage2D() on every
>> frame:
>>
>>    INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, 
>> falling back to upload
>>
>> The cause is the DRM format used for GL_RG textures. Unpacked Bayer
>> pixels are 16-bit little-endian words, loaded into a GL_RG texture with
>> the low byte in .r and the high byte in .g, which the shaders recombine
>> into a single sample. R and G here are texture channels, not colours.
>> GL names formats in memory byte order, while DRM fourccs describe the
>> components of a little-endian word from MSB to LSB:
>>
>>    DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, byte 
>> 1 = R
>>    DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, byte 
>> 1 = G
>>
>> The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
>> byte order. Mesa has no RG88 entry in its DRI image format table, so
>> eglCreateImageKHR() fails.
>>
>> Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
>> supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
>> output path (GL_RGBA) are not affected.
>
> The only thing that's not entirely clear to me is why GL_RGBA is not 
> affected.

The mapping from GL_RGBA <-> DRM_FORMAT_ARGB8888 already uses the 
inverted order, so that makes sense, no?


>
> Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> # ipu6 + 
> ov2740
>
>
>>
>> Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage 
>> constructor")
>> Signed-off-by: Faizel K B <faizel.kb@gmail.com>
>> ---
>>
>> Notes:
>>      Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an IMX415
>>      sensor (SGBRG10 unpacked, 3864x2192).
>>           Panfrost requires the row stride of linear dmabuf imports 
>> to be
>>      64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 
>> bytes, so
>>      the import also needed a local rkcif change to honour the 256-byte
>>      aligned stride (7936) that DebayerEGL::preferredInputStride() 
>> requests.
>>
>>   src/libcamera/egl.cpp | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
>> index 3cde9040a..59a7d8cf9 100644
>> --- a/src/libcamera/egl.cpp
>> +++ b/src/libcamera/egl.cpp
>> @@ -184,7 +184,7 @@ int eGL::createDMABufTexture2D(eGLImage 
>> &eglImage, int fd, bool output)
>>           drm_format = DRM_FORMAT_R8;
>>           break;
>>       case GL_RG:
>> -        drm_format = DRM_FORMAT_RG88;
>> +        drm_format = DRM_FORMAT_GR88;
>>           break;
>>       case GL_RGBA:
>>           drm_format = DRM_FORMAT_ARGB8888;
Barnabás Pőcze Oct. 21, 2026, 11:35 a.m. UTC | #4
2026. 10. 21. 13:29 keltezéssel, Robert Mader írta:
> Hi,
> 
> On 21.10.26 12:30, Barnabás Pőcze wrote:
>> Hi
>>
>> 2026. 10. 21. 8:56 keltezéssel, Faizel K B írta:
>>> Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
>>> dmabufs always fails with Mesa, and DebayerEGL silently falls back to
>>> mapping each input buffer and uploading it with glTexImage2D() on every
>>> frame:
>>>
>>>    INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, falling back to upload
>>>
>>> The cause is the DRM format used for GL_RG textures. Unpacked Bayer
>>> pixels are 16-bit little-endian words, loaded into a GL_RG texture with
>>> the low byte in .r and the high byte in .g, which the shaders recombine
>>> into a single sample. R and G here are texture channels, not colours.
>>> GL names formats in memory byte order, while DRM fourccs describe the
>>> components of a little-endian word from MSB to LSB:
>>>
>>>    DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, byte 1 = R
>>>    DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, byte 1 = G
>>>
>>> The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
>>> byte order. Mesa has no RG88 entry in its DRI image format table, so
>>> eglCreateImageKHR() fails.
>>>
>>> Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
>>> supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
>>> output path (GL_RGBA) are not affected.
>>
>> The only thing that's not entirely clear to me is why GL_RGBA is not affected.
> 
> The mapping from GL_RGBA <-> DRM_FORMAT_ARGB8888 already uses the inverted order, so that makes sense, no?

I'd have expected DRM_FORMAT_ABGR8888, since

   /* [31:0] A:B:G:R 8:8:8:8 little endian */

so the memory order is RGBA. But I must be missing something.


> 
> 
>>
>> Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> # ipu6 + ov2740
>>
>>
>>>
>>> Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage constructor")
>>> Signed-off-by: Faizel K B <faizel.kb@gmail.com>
>>> ---
>>>
>>> Notes:
>>>      Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an IMX415
>>>      sensor (SGBRG10 unpacked, 3864x2192).
>>>           Panfrost requires the row stride of linear dmabuf imports to be
>>>      64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 bytes, so
>>>      the import also needed a local rkcif change to honour the 256-byte
>>>      aligned stride (7936) that DebayerEGL::preferredInputStride() requests.
>>>
>>>   src/libcamera/egl.cpp | 2 +-
>>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
>>> index 3cde9040a..59a7d8cf9 100644
>>> --- a/src/libcamera/egl.cpp
>>> +++ b/src/libcamera/egl.cpp
>>> @@ -184,7 +184,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>>>           drm_format = DRM_FORMAT_R8;
>>>           break;
>>>       case GL_RG:
>>> -        drm_format = DRM_FORMAT_RG88;
>>> +        drm_format = DRM_FORMAT_GR88;
>>>           break;
>>>       case GL_RGBA:
>>>           drm_format = DRM_FORMAT_ARGB8888;
Robert Mader Oct. 21, 2026, 11:44 a.m. UTC | #5
On 21.10.26 13:35, Barnabás Pőcze wrote:
> 2026. 10. 21. 13:29 keltezéssel, Robert Mader írta:
>> Hi,
>>
>> On 21.10.26 12:30, Barnabás Pőcze wrote:
>>> Hi
>>>
>>> 2026. 10. 21. 8:56 keltezéssel, Faizel K B írta:
>>>> Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
>>>> dmabufs always fails with Mesa, and DebayerEGL silently falls back to
>>>> mapping each input buffer and uploading it with glTexImage2D() on 
>>>> every
>>>> frame:
>>>>
>>>>    INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, 
>>>> falling back to upload
>>>>
>>>> The cause is the DRM format used for GL_RG textures. Unpacked Bayer
>>>> pixels are 16-bit little-endian words, loaded into a GL_RG texture 
>>>> with
>>>> the low byte in .r and the high byte in .g, which the shaders 
>>>> recombine
>>>> into a single sample. R and G here are texture channels, not colours.
>>>> GL names formats in memory byte order, while DRM fourccs describe the
>>>> components of a little-endian word from MSB to LSB:
>>>>
>>>>    DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, 
>>>> byte 1 = R
>>>>    DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, 
>>>> byte 1 = G
>>>>
>>>> The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
>>>> byte order. Mesa has no RG88 entry in its DRI image format table, so
>>>> eglCreateImageKHR() fails.
>>>>
>>>> Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
>>>> supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
>>>> output path (GL_RGBA) are not affected.
>>>
>>> The only thing that's not entirely clear to me is why GL_RGBA is not 
>>> affected.
>>
>> The mapping from GL_RGBA <-> DRM_FORMAT_ARGB8888 already uses the 
>> inverted order, so that makes sense, no?
>
> I'd have expected DRM_FORMAT_ABGR8888, since
>
>   /* [31:0] A:B:G:R 8:8:8:8 little endian */
>
> so the memory order is RGBA. But I must be missing something.

Urgh. I think you're right. This format is only used for the output 
buffer, not the input ones - and we require the dmabuf import to succeed 
in that case, so there's no fallback path.

If we have a R-B swap here - then we most likely also have one in the 
shaders, compensating for it.

>
>
>>
>>
>>>
>>> Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> # ipu6 + 
>>> ov2740
>>>
>>>
>>>>
>>>> Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage 
>>>> constructor")
>>>> Signed-off-by: Faizel K B <faizel.kb@gmail.com>
>>>> ---
>>>>
>>>> Notes:
>>>>      Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an 
>>>> IMX415
>>>>      sensor (SGBRG10 unpacked, 3864x2192).
>>>>           Panfrost requires the row stride of linear dmabuf imports 
>>>> to be
>>>>      64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 
>>>> bytes, so
>>>>      the import also needed a local rkcif change to honour the 
>>>> 256-byte
>>>>      aligned stride (7936) that DebayerEGL::preferredInputStride() 
>>>> requests.
>>>>
>>>>   src/libcamera/egl.cpp | 2 +-
>>>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>>>
>>>> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
>>>> index 3cde9040a..59a7d8cf9 100644
>>>> --- a/src/libcamera/egl.cpp
>>>> +++ b/src/libcamera/egl.cpp
>>>> @@ -184,7 +184,7 @@ int eGL::createDMABufTexture2D(eGLImage 
>>>> &eglImage, int fd, bool output)
>>>>           drm_format = DRM_FORMAT_R8;
>>>>           break;
>>>>       case GL_RG:
>>>> -        drm_format = DRM_FORMAT_RG88;
>>>> +        drm_format = DRM_FORMAT_GR88;
>>>>           break;
>>>>       case GL_RGBA:
>>>>           drm_format = DRM_FORMAT_ARGB8888;
>
Laurent Pinchart Oct. 21, 2026, 12:11 p.m. UTC | #6
On Tue, Oct 20, 2026 at 11:56:24PM -0700, Faizel K B wrote:
> Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
> dmabufs always fails with Mesa, and DebayerEGL silently falls back to
> mapping each input buffer and uploading it with glTexImage2D() on every
> frame:
> 
>   INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, falling back to upload

That's not very silent, is it ?

> The cause is the DRM format used for GL_RG textures. Unpacked Bayer
> pixels are 16-bit little-endian words, loaded into a GL_RG texture with
> the low byte in .r and the high byte in .g, which the shaders recombine
> into a single sample. R and G here are texture channels, not colours.
> GL names formats in memory byte order, while DRM fourccs describe the
> components of a little-endian word from MSB to LSB:
> 
>   DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, byte 1 = R
>   DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, byte 1 = G
> 
> The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
> byte order. Mesa has no RG88 entry in its DRI image format table, so
> eglCreateImageKHR() fails.

I've checked mesa and can confirm that.

> Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
> supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
> output path (GL_RGBA) are not affected.
> 
> Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage constructor")
> Signed-off-by: Faizel K B <faizel.kb@gmail.com>
> ---
> 
> Notes:
>     Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an IMX415
>     sensor (SGBRG10 unpacked, 3864x2192).
>     
>     Panfrost requires the row stride of linear dmabuf imports to be
>     64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 bytes, so
>     the import also needed a local rkcif change to honour the 256-byte
>     aligned stride (7936) that DebayerEGL::preferredInputStride() requests.

Do you plan to upstream that ?

With s/silently // in the commit message (which we can handle when
applying, no need for a v2),

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

>  src/libcamera/egl.cpp | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
> index 3cde9040a..59a7d8cf9 100644
> --- a/src/libcamera/egl.cpp
> +++ b/src/libcamera/egl.cpp
> @@ -184,7 +184,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>  		drm_format = DRM_FORMAT_R8;
>  		break;
>  	case GL_RG:
> -		drm_format = DRM_FORMAT_RG88;
> +		drm_format = DRM_FORMAT_GR88;
>  		break;
>  	case GL_RGBA:
>  		drm_format = DRM_FORMAT_ARGB8888;
Barnabás Pőcze Oct. 21, 2026, 12:48 p.m. UTC | #7
2026. 10. 21. 13:44 keltezéssel, Robert Mader írta:
> On 21.10.26 13:35, Barnabás Pőcze wrote:
>> 2026. 10. 21. 13:29 keltezéssel, Robert Mader írta:
>>> Hi,
>>>
>>> On 21.10.26 12:30, Barnabás Pőcze wrote:
>>>> Hi
>>>>
>>>> 2026. 10. 21. 8:56 keltezéssel, Faizel K B írta:
>>>>> Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
>>>>> dmabufs always fails with Mesa, and DebayerEGL silently falls back to
>>>>> mapping each input buffer and uploading it with glTexImage2D() on every
>>>>> frame:
>>>>>
>>>>>    INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, falling back to upload
>>>>>
>>>>> The cause is the DRM format used for GL_RG textures. Unpacked Bayer
>>>>> pixels are 16-bit little-endian words, loaded into a GL_RG texture with
>>>>> the low byte in .r and the high byte in .g, which the shaders recombine
>>>>> into a single sample. R and G here are texture channels, not colours.
>>>>> GL names formats in memory byte order, while DRM fourccs describe the
>>>>> components of a little-endian word from MSB to LSB:
>>>>>
>>>>>    DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, byte 1 = R
>>>>>    DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, byte 1 = G
>>>>>
>>>>> The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
>>>>> byte order. Mesa has no RG88 entry in its DRI image format table, so
>>>>> eglCreateImageKHR() fails.
>>>>>
>>>>> Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
>>>>> supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
>>>>> output path (GL_RGBA) are not affected.
>>>>
>>>> The only thing that's not entirely clear to me is why GL_RGBA is not affected.
>>>
>>> The mapping from GL_RGBA <-> DRM_FORMAT_ARGB8888 already uses the inverted order, so that makes sense, no?
>>
>> I'd have expected DRM_FORMAT_ABGR8888, since
>>
>>   /* [31:0] A:B:G:R 8:8:8:8 little endian */
>>
>> so the memory order is RGBA. But I must be missing something.
> 
> Urgh. I think you're right. This format is only used for the output buffer, not the input ones - and we require the dmabuf import to succeed in that case, so there's no fallback path.
> 
> If we have a R-B swap here - then we most likely also have one in the shaders, compensating for it.
> 

After some testing, I think "... output path (GL_RGBA) are not affected" is not entirely correct.
It is affected, it's just not visible.


```
diff --git a/src/libcamera/shaders/bayer_1x_packed.frag b/src/libcamera/shaders/bayer_1x_packed.frag
index 0b641a5f3..e5a4f4b10 100644
--- a/src/libcamera/shaders/bayer_1x_packed.frag
+++ b/src/libcamera/shaders/bayer_1x_packed.frag
@@ -255,4 +255,6 @@ void main(void)
  #else
         gl_FragColor = vec4(rgb, 1.0);
  #endif
+
+       gl_FragColor = vec4(1, 0.5, 0.25, 1);
  }
diff --git a/src/libcamera/shaders/bayer_unpacked.frag b/src/libcamera/shaders/bayer_unpacked.frag
index 10c5e941b..e52164703 100644
--- a/src/libcamera/shaders/bayer_unpacked.frag
+++ b/src/libcamera/shaders/bayer_unpacked.frag
@@ -157,4 +157,6 @@ void main(void) {
  #else
      gl_FragColor = vec4(rgb, 1.0);
  #endif
+
+       gl_FragColor = vec4(1, 0.5, 0.25, 1);
  }
```

$ cam -c1 -C1 --file=/tmp/asd.bin
$ hexdump -n4 -C /tmp/asd.bin
00000000  40 80 ff ff                                       |@...|
00000004

which corresponds to the BGRA memory order. The reason why it apparently works
in the first place, well, that eludes me for now, but I think we might want to
extend this patch with the following:

```
diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
index 3cde9040a..aeedc5de3 100644
--- a/src/libcamera/egl.cpp
+++ b/src/libcamera/egl.cpp
@@ -187,7 +187,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
                 drm_format = DRM_FORMAT_RG88;
                 break;
         case GL_RGBA:
-               drm_format = DRM_FORMAT_ARGB8888;
+               drm_format = DRM_FORMAT_ABGR8888;
                 break;
         default:
                 LOG(eGL, Error) << "unhandled GL format";
diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 300822f9d..cb9908e1d 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -150,10 +150,10 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm
         switch (outputFormat) {
         case formats::ARGB8888:
         case formats::XRGB8888:
+               egl_.pushEnv(shaderEnv, "#define SWAP_BLUE");
                 break;
         case formats::ABGR8888:
         case formats::XBGR8888:
-               egl_.pushEnv(shaderEnv, "#define SWAP_BLUE");
                 break;
         default:
                 LOG(Debayer, Error) << "Unsupported output format";
```

because that will remove the inconsistency.


>>
>>
>>>
>>>
>>>>
>>>> Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> # ipu6 + ov2740
>>>>
>>>>
>>>>>
>>>>> Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage constructor")
>>>>> Signed-off-by: Faizel K B <faizel.kb@gmail.com>
>>>>> ---
>>>>>
>>>>> Notes:
>>>>>      Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an IMX415
>>>>>      sensor (SGBRG10 unpacked, 3864x2192).
>>>>>           Panfrost requires the row stride of linear dmabuf imports to be
>>>>>      64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 bytes, so
>>>>>      the import also needed a local rkcif change to honour the 256-byte
>>>>>      aligned stride (7936) that DebayerEGL::preferredInputStride() requests.
>>>>>
>>>>>   src/libcamera/egl.cpp | 2 +-
>>>>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
>>>>> index 3cde9040a..59a7d8cf9 100644
>>>>> --- a/src/libcamera/egl.cpp
>>>>> +++ b/src/libcamera/egl.cpp
>>>>> @@ -184,7 +184,7 @@ int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
>>>>>           drm_format = DRM_FORMAT_R8;
>>>>>           break;
>>>>>       case GL_RG:
>>>>> -        drm_format = DRM_FORMAT_RG88;
>>>>> +        drm_format = DRM_FORMAT_GR88;
>>>>>           break;
>>>>>       case GL_RGBA:
>>>>>           drm_format = DRM_FORMAT_ARGB8888;
>>
Laurent Pinchart Oct. 21, 2026, 12:48 p.m. UTC | #8
On Wed, Oct 21, 2026 at 01:44:03PM +0200, Robert Mader wrote:
> On 21.10.26 13:35, Barnabás Pőcze wrote:
> > 2026. 10. 21. 13:29 keltezéssel, Robert Mader írta:
> >> Hi,
> >>
> >> On 21.10.26 12:30, Barnabás Pőcze wrote:
> >>> Hi
> >>>
> >>> 2026. 10. 21. 8:56 keltezéssel, Faizel K B írta:
> >>>> Importing unpacked 10/12-bit Bayer input buffers (e.g. SGBRG10) as
> >>>> dmabufs always fails with Mesa, and DebayerEGL silently falls back to
> >>>> mapping each input buffer and uploading it with glTexImage2D() on 
> >>>> every
> >>>> frame:
> >>>>
> >>>>    INFO Debayer debayer_egl.cpp:523 Importing input DMABuf failed, 
> >>>> falling back to upload
> >>>>
> >>>> The cause is the DRM format used for GL_RG textures. Unpacked Bayer
> >>>> pixels are 16-bit little-endian words, loaded into a GL_RG texture 
> >>>> with
> >>>> the low byte in .r and the high byte in .g, which the shaders 
> >>>> recombine
> >>>> into a single sample. R and G here are texture channels, not colours.
> >>>> GL names formats in memory byte order, while DRM fourccs describe the
> >>>> components of a little-endian word from MSB to LSB:
> >>>>
> >>>>    DRM_FORMAT_RG88  [15:0] R:G 8:8 little endian  -> byte 0 = G, 
> >>>> byte 1 = R
> >>>>    DRM_FORMAT_GR88  [15:0] G:R 8:8 little endian  -> byte 0 = R, 
> >>>> byte 1 = G
> >>>>
> >>>> The import path maps GL_RG to DRM_FORMAT_RG88, which has the opposite
> >>>> byte order. Mesa has no RG88 entry in its DRI image format table, so
> >>>> eglCreateImageKHR() fails.
> >>>>
> >>>> Map GL_RG to DRM_FORMAT_GR88, which matches the upload path and is
> >>>> supported by Mesa. 8-bit and CSI-2 packed input (GL_LUMINANCE) and the
> >>>> output path (GL_RGBA) are not affected.
> >>>
> >>> The only thing that's not entirely clear to me is why GL_RGBA is not 
> >>> affected.
> >>
> >> The mapping from GL_RGBA <-> DRM_FORMAT_ARGB8888 already uses the 
> >> inverted order, so that makes sense, no?
> >
> > I'd have expected DRM_FORMAT_ABGR8888, since
> >
> >   /* [31:0] A:B:G:R 8:8:8:8 little endian */
> >
> > so the memory order is RGBA. But I must be missing something.

Here are the three relevant entries in dri2_format_table[]
(src/gallium/frontends/dri/dri_helpers.c) in Mesa:

      { DRM_FORMAT_ARGB8888,      __DRI_IMAGE_FORMAT_ARGB8888,
        __DRI_IMAGE_COMPONENTS_RGBA,      PIPE_FORMAT_BGRA8888_UNORM, 1,
        { { 0, 0, 0, __DRI_IMAGE_FORMAT_ARGB8888 } } },
      { DRM_FORMAT_ABGR8888,      __DRI_IMAGE_FORMAT_ABGR8888,
        __DRI_IMAGE_COMPONENTS_RGBA,      PIPE_FORMAT_RGBA8888_UNORM, 1,
        { { 0, 0, 0, __DRI_IMAGE_FORMAT_ABGR8888 } } },
      { DRM_FORMAT_GR88,          __DRI_IMAGE_FORMAT_GR88,
        __DRI_IMAGE_COMPONENTS_RG,        PIPE_FORMAT_RG88_UNORM, 1,
        { { 0, 0, 0, __DRI_IMAGE_FORMAT_GR88 } } },

Note that the table doesn't contain any entry for the DRM_FORMAT_RG88,
DRM_FORMAT_BGRA8888 or DRM_FORMAT_RGBA8888 formats (even though
PIPE_FORMAT_GR88_UNORM, PIPE_FORMAT_ABGR8888_UNORM and
PIPE_FORMAT_ARGB8888_UNORM exist in Mesa).

It's also worth noting that the format_mapping[] table in
src/gallium/frontends/dri/dri_util.c contains the following entries:

   {
      .image_format    = __DRI_IMAGE_FORMAT_ARGB8888,
      .mesa_format     =        MESA_FORMAT_B8G8R8A8_UNORM,
      .internal_format =        GL_RGBA8,
   },
   {
      .image_format    = __DRI_IMAGE_FORMAT_ABGR8888,
      .mesa_format     =        MESA_FORMAT_R8G8B8A8_UNORM,
      .internal_format =        GL_RGBA8,
   },
   {
      .image_format    = __DRI_IMAGE_FORMAT_GR88,
      .mesa_format     =        MESA_FORMAT_RG_UNORM8,
      .internal_format =        GL_RG8,
   },
   {
      .image_format    = __DRI_IMAGE_FORMAT_GR88,
      .mesa_format     =        MESA_FORMAT_LA_UNORM8,
      .internal_format =        GL_RG8,
   },

> Urgh. I think you're right. This format is only used for the output 
> buffer, not the input ones - and we require the dmabuf import to succeed 
> in that case, so there's no fallback path.
> 
> If we have a R-B swap here - then we most likely also have one in the 
> shaders, compensating for it.

We have

	switch (outputFormat) {
	case formats::ARGB8888:
	case formats::XRGB8888:
		break;  
	case formats::ABGR8888:
	case formats::XBGR8888:
		egl_.pushEnv(shaderEnv, "#define SWAP_BLUE");
		break;							
	default:
		LOG(Debayer, Error) << "Unsupported output format";
		return -EINVAL;
	}

in DebayerEGL::initBayerShaders(), and

#if defined (SWAP_BLUE)
    gl_FragColor = vec4(rgb.bgr, 1.0);
#else
    gl_FragColor = vec4(rgb, 1.0);
#endif

in the shaders.

Unless I'm mistaken, while GL_RG and GL_RGBA may map by default to R, G,
B and A stored in bytes 0, 1, 2 and 3 respectively, the actual byte
order in memory can be overridden with EGL_LINUX_DRM_FOURCC_EXT when
calling eglCreateImageKHR(). This is documented in
https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import.txt:

    The ordering of samples within a plane is taken from the drm_fourcc
    pixel_format specified for EGL_LINUX_DRM_FOURCC_EXT.

It therefore doesn't matter much if we map GL_RGBA to
DRM_FORMAT_ARGB8888 or DRM_FORMAT_ABGR8888, as long as both are
supported by the GPU. Similarly, mapping GL_RG to DRM_FORMAT_RG88 would
be absolutely fine if it wasn't for the fact that Mesa doesn't support
that mapping (likely because no GPU supports it).

If GL_RGBA would indeed map natively to DRM_FORMAT_ABGR8888 unless
specified otherwise, it's probably safe to consider DRM_FORMAT_ABGR8888
as more widely supported by GPUs than DRM_FORMAT_ARGB8888. It would then
be better to use DRM_FORMAT_ABGR8888 to ensure compatibility with a
wider range of GPUs.

Could someone test mapping GL_RGBA to GL_ABGR, and setting SWAP_BLUE for
formats::[AX]RGB8888 instead of formats::[AX]BGR8888 ?

The commit message of this patch should probably be updated to reflect
the above (if my explanation is correct).

> >>> Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> # ipu6 + 
> >>> ov2740
> >>>
> >>>> Fixes: 6e23162ddcae ("egl: Add GL format parameter to eGLImage 
> >>>> constructor")
> >>>> Signed-off-by: Faizel K B <faizel.kb@gmail.com>
> >>>> ---
> >>>>
> >>>> Notes:
> >>>>      Tested on RK3588 (Mali-G610, Panfrost, Mesa 24.3.4) with an 
> >>>> IMX415
> >>>>      sensor (SGBRG10 unpacked, 3864x2192).
> >>>>           Panfrost requires the row stride of linear dmabuf imports 
> >>>> to be
> >>>>      64-byte aligned. The SGBRG10 stride at 3864 pixels is 7728 
> >>>> bytes, so
> >>>>      the import also needed a local rkcif change to honour the 
> >>>> 256-byte
> >>>>      aligned stride (7936) that DebayerEGL::preferredInputStride() 
> >>>> requests.
> >>>>
> >>>>   src/libcamera/egl.cpp | 2 +-
> >>>>   1 file changed, 1 insertion(+), 1 deletion(-)
> >>>>
> >>>> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
> >>>> index 3cde9040a..59a7d8cf9 100644
> >>>> --- a/src/libcamera/egl.cpp
> >>>> +++ b/src/libcamera/egl.cpp
> >>>> @@ -184,7 +184,7 @@ int eGL::createDMABufTexture2D(eGLImage 
> >>>> &eglImage, int fd, bool output)
> >>>>           drm_format = DRM_FORMAT_R8;
> >>>>           break;
> >>>>       case GL_RG:
> >>>> -        drm_format = DRM_FORMAT_RG88;
> >>>> +        drm_format = DRM_FORMAT_GR88;
> >>>>           break;
> >>>>       case GL_RGBA:
> >>>>           drm_format = DRM_FORMAT_ARGB8888;

Patch
diff mbox series

diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp
index 3cde9040a..59a7d8cf9 100644
--- a/src/libcamera/egl.cpp
+++ b/src/libcamera/egl.cpp
@@ -184,7 +184,7 @@  int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
 		drm_format = DRM_FORMAT_R8;
 		break;
 	case GL_RG:
-		drm_format = DRM_FORMAT_RG88;
+		drm_format = DRM_FORMAT_GR88;
 		break;
 	case GL_RGBA:
 		drm_format = DRM_FORMAT_ARGB8888;