[Mesa-dev] [PATCH v3] EGL/Wayland: DRI_PRIME support

Axel Davy axel.davy at ens.fr
Fri Jan 24 09:42:56 PST 2014


Implements DRI_PRIME support.

Signed-off-by: Axel Davy <axel.davy at ens.fr>
---
v3: add a check in dri2_create_wayland_buffer_from_image_wl to fail
if we are on a different device than the compositor, and then can't
use tiling. This is in the spec. The patch has been rebased.
v2: bug fix
 src/egl/drivers/dri2/egl_dri2.h         |   1 +
 src/egl/drivers/dri2/platform_wayland.c | 261 ++++++++++++++++++++++++++++----
 2 files changed, 233 insertions(+), 29 deletions(-)

diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index dfc5927..941690f 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -133,6 +133,7 @@ struct dri2_egl_display
    int			     authenticated;
    int			     formats;
    uint32_t                  capabilities;
+   int			     enable_tiling;
 #endif
 
    int (*authenticate) (_EGLDisplay *disp, uint32_t id);
diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
index 50750a9..8e6d8ed 100644
--- a/src/egl/drivers/dri2/platform_wayland.c
+++ b/src/egl/drivers/dri2/platform_wayland.c
@@ -41,6 +41,10 @@
 #include <wayland-client.h>
 #include "wayland-drm-client-protocol.h"
 
+#ifdef HAVE_LIBUDEV
+#include <libudev.h>
+#endif
+
 enum wl_drm_format_flags {
    HAS_ARGB8888 = 1,
    HAS_XRGB8888 = 2,
@@ -297,12 +301,17 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
    if (dri2_surf->back == NULL)
       return -1;
    if (dri2_surf->back->dri_image == NULL) {
+      unsigned int use_flags = __DRI_IMAGE_USE_SHARE;
+
+      if (!dri2_dpy->enable_tiling)
+         use_flags |= __DRI_IMAGE_USE_LINEAR;
+
       dri2_surf->back->dri_image = 
          dri2_dpy->image->createImage(dri2_dpy->dri_screen,
                                       dri2_surf->base.Width,
                                       dri2_surf->base.Height,
                                       __DRI_IMAGE_FORMAT_ARGB8888,
-                                      __DRI_IMAGE_USE_SHARE,
+                                      use_flags,
                                       NULL);
       dri2_surf->back->age = 0;
    }
@@ -691,6 +700,13 @@ dri2_create_wayland_buffer_from_image_wl(_EGLDriver *drv,
    int width, height, format, pitch;
    enum wl_drm_format wl_format;
 
+   /* The buffer to import likely has tiling. We can't check for it,
+    * so assume we cannot import.*/
+   if (!dri2_dpy->enable_tiling) {
+      _eglError(EGL_BAD_MATCH, "dri2_create_wayland_buffer_from_image_wl");
+      return NULL;
+   }
+
    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format);
 
    switch (format) {
@@ -800,34 +816,125 @@ dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp)
    return EGL_TRUE;
 }
 
-static void
-drm_handle_device(void *data, struct wl_drm *drm, const char *device)
+static char
+is_fd_render_node(int fd)
 {
-   struct dri2_egl_display *dri2_dpy = data;
-   drm_magic_t magic;
+   struct stat render;
 
-   dri2_dpy->device_name = strdup(device);
-   if (!dri2_dpy->device_name)
-      return;
+   if (fstat(fd, &render))
+      return 0;
+
+   if (!S_ISCHR(render.st_mode))
+      return 0;
+
+   if (render.st_rdev & 0x80)
+      return 1;
+   return 0;
+}
+
+#ifdef HAVE_LIBUDEV
+
+static char *
+get_render_node_from_id_path_tag(struct udev *udev, char* id_path_tag,
+				 char another_tag)
+{
+   struct udev_device *device;
+   struct udev_enumerate *e;
+   struct udev_list_entry *entry;
+   const char *path, *id_path_tag_tmp;
+   char found = 0;
+
+   e = udev_enumerate_new(udev);
+   udev_enumerate_add_match_subsystem(e, "drm");
+   udev_enumerate_add_match_sysname(e, "render*");
+
+   udev_enumerate_scan_devices(e);
+   udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+      path = udev_list_entry_get_name(entry);
+      device = udev_device_new_from_syspath(udev, path);
+      if (!device)
+         continue;
+      id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG");
+      if (id_path_tag_tmp) {
+         if((!another_tag && !strcmp(id_path_tag, id_path_tag_tmp))
+	    || (another_tag && strcmp(id_path_tag, id_path_tag_tmp))) {
+            found = 1;
+            break;
+         }
+      }
+      udev_device_unref(device);
+   }
+
+   if(found) {
+      char* path = strdup(udev_device_get_devnode(device));
+      udev_device_unref(device);
+      return path;
+   }
+   return NULL;
+}
+
+static char *
+get_id_path_tag_from_fd(struct udev *udev, int fd)
+{
+   struct udev_device *device;
+   struct stat buf;
+   const char *id_path_tag_tmp;
+
+   if (fstat(fd, &buf) < 0) {
+      return NULL;
+   }
+
+   device = udev_device_new_from_devnum(udev, 'c', buf.st_rdev);
+
+   if(!device)
+      return NULL;
+   id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG");
+   if(!id_path_tag_tmp)
+      return NULL;
+
+   return strdup(id_path_tag_tmp);
+}
+#endif
+
+static EGLBoolean
+is_render_node_capable(struct dri2_egl_display *dri2_dpy)
+{
+   const __DRIextension **extensions;
+   int i;
 
+   if (!(dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME))
+      return EGL_FALSE;
+
+   extensions = dri2_dpy->driver_extensions;
+   for (i = 0; extensions[i]; i++) {
+      if (strcmp(extensions[i]->name, __DRI_IMAGE_DRIVER) == 0)
+         return EGL_TRUE;
+   }
+   return EGL_FALSE;
+}
+
+static int
+drm_open_device(const char* device_name)
+{
+   int fd;
 #ifdef O_CLOEXEC
-   dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR | O_CLOEXEC);
-   if (dri2_dpy->fd == -1 && errno == EINVAL)
+   fd = open(device_name, O_RDWR | O_CLOEXEC);
+   if (fd == -1 && errno == EINVAL)
 #endif
    {
-      dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR);
-      if (dri2_dpy->fd != -1)
-         fcntl(dri2_dpy->fd, F_SETFD, fcntl(dri2_dpy->fd, F_GETFD) |
-            FD_CLOEXEC);
-   }
-   if (dri2_dpy->fd == -1) {
-      _eglLog(_EGL_WARNING, "wayland-egl: could not open %s (%s)",
-	      dri2_dpy->device_name, strerror(errno));
-      return;
+      fd = open(device_name, O_RDWR);
+      if (fd != -1)
+         fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
    }
+   return fd;
+}
 
-   drmGetMagic(dri2_dpy->fd, &magic);
-   wl_drm_authenticate(dri2_dpy->wl_drm, magic);
+static void
+drm_handle_device(void *data, struct wl_drm *drm, const char *device)
+{
+   struct dri2_egl_display *dri2_dpy = data;
+
+   dri2_dpy->device_name = strdup(device);
 }
 
 static void
@@ -957,12 +1064,19 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
    struct dri2_egl_display *dri2_dpy;
    const __DRIconfig *config;
    uint32_t types;
-   int i;
+   int i, is_render_node, device_fd, is_different_device;
+   drm_magic_t magic;
    static const unsigned int argb_masks[4] =
       { 0xff0000, 0xff00, 0xff, 0xff000000 };
    static const unsigned int rgb_masks[4] = { 0xff0000, 0xff00, 0xff, 0 };
    static const unsigned int rgb565_masks[4] = { 0xf800, 0x07e0, 0x001f, 0 };
 
+   char* prime_device_name = NULL;
+   char* prime = NULL;
+   const char *dri_prime = getenv("DRI_PRIME");
+
+   if (dri_prime)
+      prime = strdup(dri_prime);
    loader_set_logger(_eglLog);
 
    drv->API.CreateWindowSurface = dri2_create_window_surface;
@@ -1003,9 +1117,92 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
    if (roundtrip(dri2_dpy) < 0 || dri2_dpy->wl_drm == NULL)
       goto cleanup_dpy;
 
-   if (roundtrip(dri2_dpy) < 0 || dri2_dpy->fd == -1)
+   if (roundtrip(dri2_dpy) < 0 || dri2_dpy->device_name == NULL)
       goto cleanup_drm;
 
+   if (prime && !(dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME)) {
+   /* render-nodes are not supported */
+      free(prime);
+      prime = NULL;
+   }
+
+   device_fd = drm_open_device(dri2_dpy->device_name);
+   if (device_fd == -1) {
+      _eglLog(_EGL_WARNING, "wayland-egl: could not open %s (%s)",
+	      dri2_dpy->device_name, strerror(errno));
+      free(prime);
+      goto cleanup_drm;
+   }
+
+   if(prime != NULL) {
+#ifdef HAVE_LIBUDEV
+      struct udev* udev = udev_new();
+      char* device_id_path_tag;
+      char another_tag = 0;
+
+      if (!udev)
+         goto prime_clean;
+      device_id_path_tag = get_id_path_tag_from_fd(udev, device_fd);
+      if (!device_id_path_tag)
+         goto udev_clean;
+
+      is_different_device = 1;
+      /* two format are supported:
+       * "1": choose any other card than the card used by the compositor.
+       * id_path_tag: (for example "pci-0000_02_00_0") choose the card
+       * with this id_path_tag. */
+      if (!strcmp(prime,"1")) {
+         free(prime);
+         prime = strdup(device_id_path_tag);
+         /* request a card with a different card than the compositor card */
+         another_tag = 1;
+      } else if (!strcmp(device_id_path_tag, prime))
+         /* we want to get the render-node of the compositor card */
+         is_different_device = 0;
+
+      prime_device_name = get_render_node_from_id_path_tag(udev,
+                                                           prime,
+                                                           another_tag);
+      if (prime_device_name)
+         _eglLog(_EGL_DEBUG,"requested device found: %s",
+                  prime_device_name);
+      else
+         _eglLog(_EGL_WARNING,"requested device not found.");
+      free(device_id_path_tag);
+    udev_clean:
+      udev_unref(udev);
+    prime_clean:
+#endif
+      free(prime);
+   }
+
+   if (prime_device_name != NULL) {
+      close(device_fd);
+      free(dri2_dpy->device_name);
+      dri2_dpy->device_name = prime_device_name;
+      dri2_dpy->fd = drm_open_device(dri2_dpy->device_name);
+
+      if (dri2_dpy->fd == -1) {
+         _eglLog(_EGL_WARNING, "wayland-egl: could not open %s (%s)",
+                 dri2_dpy->device_name, strerror(errno));
+         goto cleanup_drm;
+      }
+   } else {
+      is_different_device = 0;
+      dri2_dpy->fd = device_fd;
+   }
+
+   if (is_fd_render_node(dri2_dpy->fd)) {
+      _eglLog(_EGL_DEBUG, "wayland-egl: card is render-node");
+      dri2_dpy->authenticated = 1;
+      is_render_node = 1;
+   } else {
+      drmGetMagic(dri2_dpy->fd, &magic);
+      wl_drm_authenticate(dri2_dpy->wl_drm, magic);
+      is_render_node = 0;
+   }
+   dri2_dpy->enable_tiling = !is_different_device;
+
    if (roundtrip(dri2_dpy) < 0 || !dri2_dpy->authenticated)
       goto cleanup_fd;
 
@@ -1038,14 +1235,19 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
 
    dri2_setup_swap_interval(dri2_dpy);
 
-   /* The server shouldn't advertise WL_DRM_CAPABILITY_PRIME if the driver
-    * doesn't have createImageFromFds, since we're using the same driver on
-    * both sides.  We don't want crash if that happens anyway, so fall back to
-    * gem names if we don't have prime support. */
+   /* To use Prime, we must have _DRI_IMAGE v7 at least.
+    * createImageFromFds support indicates that Prime export/import
+    * is supported by the driver. Fall back to
+    * gem names if we don't have Prime support. */
 
    if (dri2_dpy->image->base.version < 7 ||
        dri2_dpy->image->createImageFromFds == NULL)
-      dri2_dpy->capabilities &= WL_DRM_CAPABILITY_PRIME;
+      dri2_dpy->capabilities &= ~WL_DRM_CAPABILITY_PRIME;
+
+   if (is_render_node && !is_render_node_capable(dri2_dpy)) {
+      _eglLog(_EGL_WARNING, "wayland-egl: display is not render-node capable");
+      goto cleanup_screen;
+   }
 
    types = EGL_WINDOW_BIT;
    for (i = 0; dri2_dpy->driver_configs[i]; i++) {
@@ -1070,7 +1272,8 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
    disp->VersionMinor = 4;
 
    return EGL_TRUE;
-
+ cleanup_screen:
+   dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
  cleanup_driver:
    dlclose(dri2_dpy->driver);
  cleanup_driver_name:
-- 
1.8.3.2



More information about the mesa-dev mailing list