[Mesa-dev] [PATCH 09/11] Wayland/egl: Add GPU offloading support

Axel Davy axel.davy at ens.fr
Wed Jun 18 20:27:38 PDT 2014


This is easier than GLX DRI3 GPU offloading support,
because applications are not allowed to read the front
buffer.

We just need to send to the server buffers it can read,
and for that we use for every back buffer an intermediate
buffer with no tiling to which we copy before sending
the buffer to the compositor.

Signed-off-by: Axel Davy <axel.davy at ens.fr>
---
 src/egl/drivers/dri2/egl_dri2.h         |   5 +-
 src/egl/drivers/dri2/platform_wayland.c | 171 ++++++++++++++++++++++++++------
 2 files changed, 142 insertions(+), 34 deletions(-)

diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index 0dd9d69..4b70c48 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -195,6 +195,8 @@ struct dri2_egl_display
    int			     authenticated;
    int			     formats;
    uint32_t                  capabilities;
+   int			     is_different_gpu;
+   int			     blit_front;
 #endif
 };
 
@@ -247,7 +249,8 @@ struct dri2_egl_surface
    struct {
 #ifdef HAVE_WAYLAND_PLATFORM
       struct wl_buffer   *wl_buffer;
-      __DRIimage         *dri_image;
+      __DRIimage         *rendering_image;
+      __DRIimage         *shared_image;
 #endif
 #ifdef HAVE_DRM_PLATFORM
       struct gbm_bo       *bo;
diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
index 537d26e..0dd4640 100644
--- a/src/egl/drivers/dri2/platform_wayland.c
+++ b/src/egl/drivers/dri2/platform_wayland.c
@@ -238,8 +238,10 @@ dri2_wl_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
    for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
       if (dri2_surf->color_buffers[i].wl_buffer)
          wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
-      if (dri2_surf->color_buffers[i].dri_image)
-         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
+      if (dri2_surf->color_buffers[i].rendering_image) {
+         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].rendering_image);
+         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].shared_image);
+      }
    }
 
    for (i = 0; i < __DRI_BUFFER_COUNT; i++)
@@ -272,11 +274,14 @@ dri2_wl_release_buffers(struct dri2_egl_surface *dri2_surf)
       if (dri2_surf->color_buffers[i].wl_buffer &&
           !dri2_surf->color_buffers[i].locked)
          wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
-      if (dri2_surf->color_buffers[i].dri_image)
-         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
+      if (dri2_surf->color_buffers[i].rendering_image) {
+         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].rendering_image);
+         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].shared_image);
+      }
 
       dri2_surf->color_buffers[i].wl_buffer = NULL;
-      dri2_surf->color_buffers[i].dri_image = NULL;
+      dri2_surf->color_buffers[i].rendering_image = NULL;
+      dri2_surf->color_buffers[i].shared_image = NULL;
       dri2_surf->color_buffers[i].locked = 0;
    }
 
@@ -292,6 +297,7 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
 {
    struct dri2_egl_display *dri2_dpy =
       dri2_egl_display(dri2_surf->base.Resource.Display);
+   unsigned int use_flags;
    int i;
 
    /* We always want to throttle to some event (either a frame callback or
@@ -311,24 +317,45 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
             continue;
          if (dri2_surf->back == NULL)
             dri2_surf->back = &dri2_surf->color_buffers[i];
-         else if (dri2_surf->back->dri_image == NULL)
+         else if (dri2_surf->back->rendering_image == NULL)
             dri2_surf->back = &dri2_surf->color_buffers[i];
       }
    }
 
    if (dri2_surf->back == NULL)
       return -1;
-   if (dri2_surf->back->dri_image == NULL) {
-      dri2_surf->back->dri_image = 
+
+   if (dri2_surf->back->rendering_image == NULL) {
+      use_flags = __DRI_IMAGE_USE_SHARE;
+
+      if (dri2_dpy->is_different_gpu)
+         use_flags |= __DRI_IMAGE_USE_LINEAR;
+
+      dri2_surf->back->shared_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);
+      if (dri2_surf->back->shared_image == NULL)
+         return -1;
+
+      if (dri2_dpy->blit_front)
+         dri2_surf->back->rendering_image =
+            dri2_dpy->image->createImage(dri2_dpy->dri_screen,
+                                         dri2_surf->base.Width,
+                                         dri2_surf->base.Height,
+                                         __DRI_IMAGE_FORMAT_ARGB8888,
+                                         0,
+                                         NULL);
+      else
+         dri2_surf->back->rendering_image =
+            dri2_dpy->image->dupImage(dri2_surf->back->shared_image, NULL);
+
       dri2_surf->back->age = 0;
    }
-   if (dri2_surf->back->dri_image == NULL)
+   if (dri2_surf->back->rendering_image == NULL)
       return -1;
 
    dri2_surf->back->locked = 1;
@@ -345,7 +372,7 @@ back_bo_to_dri_buffer(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer)
    __DRIimage *image;
    int name, pitch;
 
-   image = dri2_surf->back->dri_image;
+   image = dri2_surf->back->rendering_image;
 
    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_NAME, &name);
    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &pitch);
@@ -411,9 +438,11 @@ update_buffers(struct dri2_egl_surface *dri2_surf)
       if (!dri2_surf->color_buffers[i].locked &&
           dri2_surf->color_buffers[i].wl_buffer) {
          wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
-         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
+         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].rendering_image);
+         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].shared_image);
          dri2_surf->color_buffers[i].wl_buffer = NULL;
-         dri2_surf->color_buffers[i].dri_image = NULL;
+         dri2_surf->color_buffers[i].rendering_image = NULL;
+         dri2_surf->color_buffers[i].shared_image = NULL;
       }
    }
 
@@ -504,7 +533,7 @@ image_get_buffers(__DRIdrawable *driDrawable,
       return 0;
 
    buffers->image_mask = __DRI_IMAGE_BUFFER_BACK;
-   buffers->back = dri2_surf->back->dri_image;
+   buffers->back = dri2_surf->back->rendering_image;
 
    return 1;
 }
@@ -549,9 +578,9 @@ create_wl_buffer(struct dri2_egl_surface *dri2_surf)
       return;
 
    if (dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME) {
-      dri2_dpy->image->queryImage(dri2_surf->current->dri_image,
+      dri2_dpy->image->queryImage(dri2_surf->current->shared_image,
                                   __DRI_IMAGE_ATTRIB_FD, &fd);
-      dri2_dpy->image->queryImage(dri2_surf->current->dri_image,
+      dri2_dpy->image->queryImage(dri2_surf->current->shared_image,
                                   __DRI_IMAGE_ATTRIB_STRIDE, &stride);
 
       dri2_surf->current->wl_buffer =
@@ -565,9 +594,9 @@ create_wl_buffer(struct dri2_egl_surface *dri2_surf)
                                     0, 0);
       close(fd);
    } else {
-      dri2_dpy->image->queryImage(dri2_surf->current->dri_image,
+      dri2_dpy->image->queryImage(dri2_surf->current->shared_image,
                                   __DRI_IMAGE_ATTRIB_NAME, &name);
-      dri2_dpy->image->queryImage(dri2_surf->current->dri_image,
+      dri2_dpy->image->queryImage(dri2_surf->current->shared_image,
                                   __DRI_IMAGE_ATTRIB_STRIDE, &stride);
 
       dri2_surf->current->wl_buffer =
@@ -650,6 +679,11 @@ dri2_wl_swap_buffers_with_damage(_EGLDriver *drv,
       }
    }
 
+   if (dri2_dpy->flush->base.version >= 4 || dri2_dpy->blit_front) {
+      ctx = _eglGetCurrentContext();
+      dri2_ctx = dri2_egl_context(ctx);
+   }
+
    if (dri2_dpy->flush->base.version >= 4) {
       ctx = _eglGetCurrentContext();
       dri2_ctx = dri2_egl_context(ctx);
@@ -661,6 +695,15 @@ dri2_wl_swap_buffers_with_damage(_EGLDriver *drv,
       (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable);
    }
 
+   if (dri2_dpy->blit_front)
+      dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+                                 dri2_surf->current->shared_image,
+                                 dri2_surf->current->rendering_image,
+                                 0, 0, dri2_surf->base.Width,
+                                 dri2_surf->base.Height,
+                                 0, 0, dri2_surf->base.Width,
+                                 dri2_surf->base.Height, 1);
+
    (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable);
 
    wl_surface_commit(dri2_surf->wl_win->surface);
@@ -714,6 +757,14 @@ dri2_wl_create_wayland_buffer_from_image(_EGLDriver *drv,
    int width, height, format, pitch;
    enum wl_drm_format wl_format;
 
+   /* The buffer to import likely has tiling. This tiling mode is likely
+    * incompatible with the server gpu. We can't check for it, so assume
+    * we cannot import.*/
+   if (dri2_dpy->is_different_gpu) {
+      _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) {
@@ -777,6 +828,22 @@ bad_format:
    return NULL;
 }
 
+static char
+is_fd_render_node(int fd)
+{
+   struct stat render;
+
+   if (fstat(fd, &render))
+      return 0;
+
+   if (!S_ISCHR(render.st_mode))
+      return 0;
+
+   if (render.st_rdev & 0x80)
+      return 1;
+   return 0;
+}
+
 static int
 dri2_wl_authenticate(_EGLDisplay *disp, uint32_t id)
 {
@@ -804,28 +871,27 @@ drm_handle_device(void *data, struct wl_drm *drm, const char *device)
    struct dri2_egl_display *dri2_dpy = data;
    drm_magic_t magic;
 
-   dri2_dpy->device_name = strdup(device);
-   if (!dri2_dpy->device_name)
-      return;
-
 #ifdef O_CLOEXEC
-   dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR | O_CLOEXEC);
+   dri2_dpy->fd = open(device, O_RDWR | O_CLOEXEC);
    if (dri2_dpy->fd == -1 && errno == EINVAL)
 #endif
    {
-      dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR);
+      dri2_dpy->fd = open(device, 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));
+	      device, strerror(errno));
       return;
    }
 
-   drmGetMagic(dri2_dpy->fd, &magic);
-   wl_drm_authenticate(dri2_dpy->wl_drm, magic);
+   if (!is_fd_render_node(dri2_dpy->fd)) {
+      drmGetMagic(dri2_dpy->fd, &magic);
+      wl_drm_authenticate(dri2_dpy->wl_drm, magic);
+   } else
+      dri2_dpy->authenticated = 1;
 }
 
 static void
@@ -967,6 +1033,23 @@ static struct dri2_egl_display_vtbl dri2_wl_display_vtbl = {
    .get_sync_values = dri2_fallback_get_sync_values,
 };
 
+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;
+}
+
 EGLBoolean
 dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
 {
@@ -1014,10 +1097,19 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
    if (roundtrip(dri2_dpy) < 0 || !dri2_dpy->authenticated)
       goto cleanup_fd;
 
+   dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
+   dri2_dpy->device_name = loader_get_device_name_for_fd(dri2_dpy->fd);
+
+   if (dri2_dpy->device_name == NULL) {
+      _eglError(EGL_BAD_ALLOC, "DRI2: failed to get device name");
+      goto cleanup_fd;
+   }
+
    dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd, 0);
+
    if (dri2_dpy->driver_name == NULL) {
       _eglError(EGL_BAD_ALLOC, "DRI2: failed to get driver name");
-      goto cleanup_fd;
+      goto cleanup_device_name;
    }
 
    if (!dri2_load_driver(disp))
@@ -1043,15 +1135,25 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
 
    dri2_wl_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;
 
+   if (is_fd_render_node(dri2_dpy->fd) && !is_render_node_capable(dri2_dpy)) {
+      _eglLog(_EGL_WARNING, "wayland-egl: display is not render-node capable");
+      goto cleanup_screen;
+   }
+
+   if (dri2_dpy->is_different_gpu && dri2_dpy->image->base.version >= 9) {
+      dri2_dpy->blit_front = 1;
+      _eglLog(_EGL_DEBUG, "wayland-egl: blit mode activated");
+   }
+
    types = EGL_WINDOW_BIT;
    for (i = 0; dri2_dpy->driver_configs[i]; i++) {
       config = dri2_dpy->driver_configs[i];
@@ -1080,14 +1182,17 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
 
    return EGL_TRUE;
 
+ cleanup_screen:
+   dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
  cleanup_driver:
    dlclose(dri2_dpy->driver);
  cleanup_driver_name:
    free(dri2_dpy->driver_name);
+ cleanup_device_name:
+   free(dri2_dpy->device_name);
  cleanup_fd:
    close(dri2_dpy->fd);
  cleanup_drm:
-   free(dri2_dpy->device_name);
    wl_drm_destroy(dri2_dpy->wl_drm);
  cleanup_dpy:
    free(dri2_dpy);
-- 
1.9.1



More information about the mesa-dev mailing list