This is an automated email from the git hooks/post-receive script.
git pushed a commit to reference refs/pull/147/head
in repository efl.
View the commit online.
commit d323ce815f1ff4b55e94313ebd26d3bbc1c52075
Author: Cedric BAIL <[email protected]>
AuthorDate: Mon Aug 17 09:40:46 2026 -0600
evas/gl: expose the DRM device behind Evas GL
zwp_linux_dmabuf_v1 version 4 replaces the format and modifier events
with feedback objects, and every feedback object has to carry a
main_device: the dev_t of the DRM node the compositor imports buffers
on. A client resolves that number to a real device and allocates there,
so on a multi-GPU machine it is what keeps the buffer on the GPU the
compositor composites with instead of copying across every frame.
A compositor built on Evas cannot answer that today. The EGLDisplay that
knows the device lives inside Evas and is not reachable from outside, so
add the one entry that exposes it, next to evasglQueryDmaBufFormats and
evasglQueryDmaBufModifiers which exist for the same reason.
Every failure path returns EINA_FALSE and leaves the caller's value
alone - no EGL, no EGL_EXT_device_query, software rendering. There is
deliberately no default: /dev/dri/renderD128 is right on the machines
where it does not matter and wrong on the multi-GPU laptop that dmabuf
feedback was invented for, and a wrong device silently sends clients to
the wrong GPU. A caller that cannot get an answer must advertise
version 3 instead, which is what the NULL entry and EINA_FALSE are for.
Two things did not transfer from the neighbouring extension blocks.
eglQueryDisplayAttribEXT and eglQueryDeviceStringEXT come from
EGL_EXT_device_query, which is a client extension: it is reported by
eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS) and never by the display,
so the support scan had to learn to consult the client extension string.
That match is on whole tokens rather than by strstr(), because
GL_KHR_debug is a suffix of the client extension EGL_KHR_debug and would
otherwise light up its block on drivers that do not have it. And the
block is kept separate from EGL_EXT_image_dma_buf_import_modifiers:
folded in, a driver without device_query would discard support for the
whole block and lose the format and modifier queries that work today.
dev_t crosses the API as uint64_t. Its width is not guaranteed by the
ABI and the wire format the callers feed wants a fixed size anyway.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01GyH3ReM1swci5M7yZ3wovr
---
src/lib/evas/Evas_GL.h | 36 ++++++-
.../evas/engines/gl_common/evas_gl_api_ext.c | 114 ++++++++++++++++++++-
.../evas/engines/gl_common/evas_gl_api_ext.h | 17 +++
.../evas/engines/gl_common/evas_gl_api_ext_def.h | 22 ++++
4 files changed, 184 insertions(+), 5 deletions(-)
diff --git a/src/lib/evas/Evas_GL.h b/src/lib/evas/Evas_GL.h
index c740439d31..ca9b697c2a 100644
--- a/src/lib/evas/Evas_GL.h
+++ b/src/lib/evas/Evas_GL.h
@@ -5118,8 +5118,9 @@ typedef unsigned long long EvasGLTime;
* Version 5: [version 4] + GLES3.1
* Version 6: [version 5] + GLES3.2
* Version 7: [version 7] + query formats + modifiers
+ * Version 8: [version 7] + query the DRM device
*/
-#define EVAS_GL_API_VERSION 7
+#define EVAS_GL_API_VERSION 8
/**
* @brief The Evas GL API
@@ -6126,6 +6127,39 @@ EvasGLImage *img = glapi->evasglCreateImageForContext
*/
Eina_Bool (*evasglQueryDmaBufModifiers) (Evas_GL *evas_gl, int format, int max_modifiers, uint64_t *modifiers, Eina_Bool *external_only, int *num_modifiers);
/** @} */
+
+ /**
+ * @name Evas GL DRM device functions
+ *
+ * Evas_GL_API version 8 or higher.
+ *
+ * @since 1.29
+ * @{ */
+ /**
+ * @anchor evasglQueryDrmDevice
+ * @brief Get the DRM device this Evas_GL renders on.
+ *
+ * @param[in] evas_gl The Evas_GL object.
+ * @param[out] device The dev_t of the DRM node, widened to a fixed size.
+ *
+ * @return @c EINA_TRUE and a device on success, @c EINA_FALSE otherwise.
+ *
+ * A Wayland compositor needs this to fill in the main_device of a
+ * zwp_linux_dmabuf_v1 feedback object, so that a client allocates its
+ * buffers on the GPU the compositor will import them on.
+ *
+ * There is no fallback and no default. @c EINA_FALSE is returned whenever
+ * the device cannot be determined - no EGL, a driver without
+ * EGL_EXT_device_query, or software rendering - and @p device is left
+ * untouched. A caller that gets @c EINA_FALSE must not substitute a guess:
+ * naming the wrong device sends a client to the wrong GPU, which is a
+ * worse outcome than naming none.
+ *
+ * Use major()/minor() to resolve the value back to a /dev/dri node. It may
+ * name either the render node or the primary node.
+ */
+ Eina_Bool (*evasglQueryDrmDevice) (Evas_GL *evas_gl, uint64_t *device);
+ /** @} */
};
/**
diff --git a/src/modules/evas/engines/gl_common/evas_gl_api_ext.c b/src/modules/evas/engines/gl_common/evas_gl_api_ext.c
index 41f81e65de..3e9827874c 100644
--- a/src/modules/evas/engines/gl_common/evas_gl_api_ext.c
+++ b/src/modules/evas/engines/gl_common/evas_gl_api_ext.c
@@ -8,6 +8,11 @@
# include <dlfcn.h>
#endif
+#ifdef GL_GLES
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+
#define EVGL_FUNC_BEGIN() if (UNLIKELY(_need_context_restore)) _context_restore()
// list of egl extensions
@@ -435,9 +440,106 @@ _evgl_evasglQueryDmaBufModifiers(Evas_GL *evas_gl,
return EXT_FUNC_EGL(eglQueryDmaBufModifiersEXT)(dpy, format, max_modifiers, modifiers, external_only, num_modifiers);
}
+/* Which DRM node is this Evas_GL rendering on? A Wayland compositor needs the
+ * answer to fill in the main_device of a zwp_linux_dmabuf_v1 feedback object,
+ * and the EGLDisplay that knows it only exists in here.
+ *
+ * Every failure path returns EINA_FALSE and leaves *device alone. There is no
+ * sensible default to fall back on: a guessed node points a client at the
+ * wrong GPU, which is precisely the bug dmabuf feedback exists to fix. A
+ * caller that cannot get an answer must not claim it has one. */
+static Eina_Bool
+_evgl_evasglQueryDrmDevice(Evas_GL *evas_gl, uint64_t *device)
+{
+ EGLDisplay dpy = EGLDISPLAY_GET(evas_gl);
+ const char *exts, *path = NULL;
+ EGLDeviceEXT dev;
+ EGLAttrib attr = 0;
+ struct stat st;
+
+ if (!device) return EINA_FALSE;
+ if (!dpy) return EINA_FALSE;
+ if (!EXT_FUNC_EGL(eglQueryDisplayAttribEXT) ||
+ !EXT_FUNC_EGL(eglQueryDeviceStringEXT))
+ return EINA_FALSE;
+
+ /* Which EGLDevice is behind this display... */
+ if (!EXT_FUNC_EGL(eglQueryDisplayAttribEXT)(dpy, EGL_DEVICE_EXT, &attr))
+ return EINA_FALSE;
+ dev = (EGLDeviceEXT)attr;
+ if (!dev) return EINA_FALSE;
+
+ /* ...and which DRM node is behind that. Ask the device what it can answer
+ * first: querying a string it does not implement latches an EGL error that
+ * unrelated code would then trip over. Software rendering answers neither,
+ * which is the honest EINA_FALSE below. */
+ exts = EXT_FUNC_EGL(eglQueryDeviceStringEXT)(dev, EGL_EXTENSIONS);
+ if (!exts) return EINA_FALSE;
+
+ /* The render node is the one a client wants to allocate on. The primary
+ * node is the fallback for drivers that do not expose one - the dmabuf
+ * feedback spec leaves the node type unspecified. */
+ if (strstr(exts, "EGL_EXT_device_drm_render_node"))
+ path = EXT_FUNC_EGL(eglQueryDeviceStringEXT)(dev, EGL_DRM_RENDER_NODE_FILE_EXT);
+ if (!path && strstr(exts, "EGL_EXT_device_drm"))
+ path = EXT_FUNC_EGL(eglQueryDeviceStringEXT)(dev, EGL_DRM_DEVICE_FILE_EXT);
+ if (!path) return EINA_FALSE;
+
+ if (stat(path, &st) != 0) return EINA_FALSE;
+
+ /* dev_t crosses the API as uint64_t: its width is not part of the ABI, and
+ * the consumers of this (a wl_array on the wire) want a fixed size. */
+ *device = (uint64_t)st.st_rdev;
+ return EINA_TRUE;
+}
+
#else
#endif
+/* EGL client extensions - the ones reported by eglQueryString(EGL_NO_DISPLAY,
+ * EGL_EXTENSIONS) rather than by a display. The engines only ever hand us a
+ * display extension string, so without this an extension block naming a client
+ * extension could never match. Queried once and cached.
+ *
+ * Matching is on whole space separated tokens rather than by strstr(), because
+ * the two namespaces overlap by suffix: the GL_KHR_debug block would otherwise
+ * light up on a driver that only offers the client extension EGL_KHR_debug. */
+static Eina_Bool
+_evgl_egl_client_ext_supported(const char *name)
+{
+#ifdef GL_GLES
+ static const char *exts = NULL;
+ const char *p;
+ size_t len;
+
+ if (!exts)
+ {
+ exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ /* EGL_EXT_client_extensions is itself optional: without it this is an
+ * EGL_BAD_DISPLAY that must not be left latched for the next caller. */
+ if (!exts)
+ {
+ eglGetError();
+ exts = "";
+ }
+ }
+
+ len = strlen(name);
+ p = exts;
+ while ((p = strstr(p, name)))
+ {
+ if (((p == exts) || (p[-1] == ' ')) &&
+ ((p[len] == ' ') || (p[len] == '\0')))
+ return EINA_TRUE;
+ p += len;
+ }
+ return EINA_FALSE;
+#else
+ (void) name;
+ return EINA_FALSE;
+#endif
+}
+
static void
_evgl_glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum* attachments)
{
@@ -650,7 +752,8 @@ evgl_api_egl_ext_init(void *getproc, const char *glueexts)
}
#define _EVASGL_EXT_CHECK_SUPPORT(name) \
- (strstr(glueexts, name) != NULL)
+ ((strstr(glueexts, name) != NULL) || \
+ _evgl_egl_client_ext_supported(name))
#define _EVASGL_EXT_DISCARD_SUPPORT() \
*ext_support = 0;
@@ -830,7 +933,8 @@ _evgl_api_gles2_ext_init(void *getproc, const char *glueexts)
}
#define _EVASGL_EXT_CHECK_SUPPORT(name) \
- (strstr(glexts, name) != NULL || strstr(glueexts, name) != NULL)
+ (strstr(glexts, name) != NULL || strstr(glueexts, name) != NULL || \
+ _evgl_egl_client_ext_supported(name))
#define _EVASGL_EXT_DISCARD_SUPPORT() \
*ext_support = 0;
@@ -1109,7 +1213,8 @@ _evgl_api_gles1_ext_init(void *getproc, const char *glueexts)
}
#define _EVASGL_EXT_CHECK_SUPPORT(name) \
- ((strstr(glexts, name) != NULL) || (strstr(glueexts, name) != NULL))
+ ((strstr(glexts, name) != NULL) || (strstr(glueexts, name) != NULL) || \
+ _evgl_egl_client_ext_supported(name))
#define _EVASGL_EXT_DISCARD_SUPPORT() \
*ext_support = 0;
@@ -1398,7 +1503,8 @@ _evgl_api_gles3_ext_init(void *getproc, const char *glueexts)
}
#define _EVASGL_EXT_CHECK_SUPPORT(name) \
- ((strstr(glexts, name) != NULL) || (strstr(glueexts, name) != NULL))
+ ((strstr(glexts, name) != NULL) || (strstr(glueexts, name) != NULL) || \
+ _evgl_egl_client_ext_supported(name))
#define _EVASGL_EXT_DISCARD_SUPPORT() \
*ext_support = 0;
diff --git a/src/modules/evas/engines/gl_common/evas_gl_api_ext.h b/src/modules/evas/engines/gl_common/evas_gl_api_ext.h
index 118e409cf3..654ff7d316 100644
--- a/src/modules/evas/engines/gl_common/evas_gl_api_ext.h
+++ b/src/modules/evas/engines/gl_common/evas_gl_api_ext.h
@@ -6,6 +6,23 @@
#ifdef GL_GLES
#include <EGL/egl.h>
#include <EGL/eglext.h>
+
+/* EGL headers older than EGL_EXT_device_base know nothing about EGLDevice,
+ * and the DRM node strings are younger still. Declare what we need so that
+ * evasglQueryDrmDevice() builds against any vintage of eglext.h; whether the
+ * driver actually implements it is decided at run time. */
+# ifndef EGL_EXT_device_base
+typedef void *EGLDeviceEXT;
+# endif
+# ifndef EGL_DEVICE_EXT
+# define EGL_DEVICE_EXT 0x322C
+# endif
+# ifndef EGL_DRM_DEVICE_FILE_EXT
+# define EGL_DRM_DEVICE_FILE_EXT 0x3233
+# endif
+# ifndef EGL_DRM_RENDER_NODE_FILE_EXT
+# define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
+# endif
#else
# ifdef BUILD_ENGINE_GL_COCOA
# include <OpenGL/gl.h>
diff --git a/src/modules/evas/engines/gl_common/evas_gl_api_ext_def.h b/src/modules/evas/engines/gl_common/evas_gl_api_ext_def.h
index 3a5afcbdfc..f76df528c7 100644
--- a/src/modules/evas/engines/gl_common/evas_gl_api_ext_def.h
+++ b/src/modules/evas/engines/gl_common/evas_gl_api_ext_def.h
@@ -1694,6 +1694,28 @@ _EVASGL_EXT_BEGIN(EGL_EXT_image_dma_buf_import_modifiers)
_EVASGL_EXT_FUNCTION_END()
_EVASGL_EXT_END()
+/* This one is a client extension: it is reported by
+ * eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS), not by the display, which is
+ * why the support scan has to consult the client extension string too. Keep it
+ * in a block of its own - folded into the dmabuf block above, a driver without
+ * it would discard support for the whole block and lose the format and
+ * modifier queries that work today. */
+_EVASGL_EXT_BEGIN(EGL_EXT_device_query)
+
+ _EVASGL_EXT_DRVNAME(EGL_EXT_device_query)
+
+ _EVASGL_EXT_FUNCTION_PRIVATE_BEGIN(EGLBoolean, eglQueryDisplayAttribEXT, (EGLDisplay dpy, EGLint attribute, EGLAttrib *value), (dpy, attribute, value))
+ _EVASGL_EXT_FUNCTION_DRVFUNC_PROCADDR("eglQueryDisplayAttribEXT")
+ _EVASGL_EXT_FUNCTION_PRIVATE_END()
+ _EVASGL_EXT_FUNCTION_PRIVATE_BEGIN(const char *, eglQueryDeviceStringEXT, (EGLDeviceEXT device, EGLint name), (device, name))
+ _EVASGL_EXT_FUNCTION_DRVFUNC_PROCADDR("eglQueryDeviceStringEXT")
+ _EVASGL_EXT_FUNCTION_PRIVATE_END()
+
+ _EVASGL_EXT_FUNCTION_BEGIN(Eina_Bool, evasglQueryDrmDevice, (Evas_GL *evas_gl, uint64_t *device), (evas_gl, device))
+ _EVASGL_EXT_FUNCTION_DRVFUNC(_evgl_evasglQueryDrmDevice)
+ _EVASGL_EXT_FUNCTION_END()
+_EVASGL_EXT_END()
+
#if 0
_EVASGL_EXT_BEGIN(EGL_SEC_map_image)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.