[Mesa-dev] [PATCH 3/3] Implement choosing the device to use with DRI_PRIME

Axel Davy axel.davy at ens.fr
Thu Nov 7 08:13:38 PST 2013


two formats are supported: DRI_PRIME="1" will tell Mesa
to choose an other card than the compositor card if possible,
while setting DRI_PRIME to the id_path_tag of a device (like
"pci-0000_02_00_0") tells Mesa to take the device with this
id_path_tag.

If it isn't able to find the desired card ( no render-nodes,
render-nodes not advertising ID_PATH_TAG, invalid ID_PATH_TAG),
then the compositor card is used.

Signed-off-by: Axel Davy <axel.davy at ens.fr>
---
 src/egl/drivers/dri2/platform_wayland.c | 168 ++++++++++++++++++++++++++++++--
 1 file changed, 160 insertions(+), 8 deletions(-)

diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
index b922ff5..3900771 100644
--- a/src/egl/drivers/dri2/platform_wayland.c
+++ b/src/egl/drivers/dri2/platform_wayland.c
@@ -40,6 +40,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,
@@ -656,6 +660,74 @@ is_fd_render_node(int fd)
   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 void
 drm_handle_device(void *data, struct wl_drm *drm, const char *device)
 {
@@ -737,13 +809,20 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
    struct dri2_egl_display *dri2_dpy;
    const __DRIconfig *config;
    uint32_t types;
-   int i, is_render_node;
+   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);
+
    drv->API.CreateWindowSurface = dri2_create_window_surface;
    drv->API.DestroySurface = dri2_destroy_surface;
    drv->API.SwapBuffers = dri2_swap_buffers;
@@ -782,21 +861,94 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
       goto cleanup_drm;
 
 #ifdef O_CLOEXEC
-   dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR | O_CLOEXEC);
-   if (dri2_dpy->fd == -1 && errno == EINVAL)
+   device_fd = open(dri2_dpy->device_name, O_RDWR | O_CLOEXEC);
+   if (device_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) |
+      device_fd = open(dri2_dpy->device_name, O_RDWR);
+      if (device_fd != -1)
+         fcntl(device_fd, F_SETFD, fcntl(device_fd, F_GETFD) |
             FD_CLOEXEC);
    }
-   if (dri2_dpy->fd == -1) {
+   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)
+   {
+#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;
+#ifdef O_CLOEXEC
+      dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR | O_CLOEXEC);
+      if (dri2_dpy->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));
+         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");
@@ -809,7 +961,7 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
       wl_drm_authenticate(dri2_dpy->wl_drm, magic);
       is_render_node = 0;
    }
-   dri2_dpy->enable_tiling = 1;
+   dri2_dpy->enable_tiling = !is_different_device;
 
    if (roundtrip(dri2_dpy) < 0 || !dri2_dpy->authenticated)
       goto cleanup_fd;
-- 
1.8.1.2



More information about the mesa-dev mailing list