wayland: Add prime fd passing as a buffer sharing mechanism
authorKristian Høgsberg <krh@bitplanet.net>
Sat, 2 Feb 2013 17:26:12 +0000 (12:26 -0500)
committerKristian Høgsberg <krh@bitplanet.net>
Tue, 19 Mar 2013 01:15:41 +0000 (21:15 -0400)
Reviewed-by: Ander Conselvan de Oliveira <conselvan2@gmail.com>
src/egl/drivers/dri2/egl_dri2.c
src/egl/drivers/dri2/egl_dri2.h
src/egl/drivers/dri2/platform_wayland.c
src/egl/wayland/wayland-drm/wayland-drm.c
src/egl/wayland/wayland-drm/wayland-drm.h
src/egl/wayland/wayland-drm/wayland-drm.xml

index b7749196c5d20e1fd19b89f118ec88026f97e7b2..a3aabf5ab516db05ba667e2eebd3aefde15d223a 100644 (file)
@@ -320,7 +320,7 @@ static struct dri2_extension_match dri2_driver_extensions[] = {
 static struct dri2_extension_match dri2_core_extensions[] = {
    { __DRI2_FLUSH, 1, offsetof(struct dri2_egl_display, flush) },
    { __DRI_TEX_BUFFER, 2, offsetof(struct dri2_egl_display, tex_buffer) },
-   { __DRI_IMAGE, 1, offsetof(struct dri2_egl_display, image) },
+   { __DRI_IMAGE, 7, offsetof(struct dri2_egl_display, image) },
    { NULL, 0, 0 }
 };
 
@@ -1498,7 +1498,7 @@ dri2_export_drm_image_mesa(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img,
 #ifdef HAVE_WAYLAND_PLATFORM
 
 static void
-dri2_wl_reference_buffer(void *user_data, uint32_t name,
+dri2_wl_reference_buffer(void *user_data, uint32_t name, int fd,
                          struct wl_drm_buffer *buffer)
 {
    _EGLDisplay *disp = user_data;
@@ -1506,13 +1506,24 @@ dri2_wl_reference_buffer(void *user_data, uint32_t name,
    __DRIimage *img;
    int i, dri_components = 0;
 
-   img = dri2_dpy->image->createImageFromNames(dri2_dpy->dri_screen,
-                                               buffer->buffer.width,
-                                               buffer->buffer.height,
-                                               buffer->format, (int*)&name, 1,
-                                               buffer->stride,
-                                               buffer->offset,
-                                               NULL);
+   if (fd == -1)
+      img = dri2_dpy->image->createImageFromNames(dri2_dpy->dri_screen,
+                                                  buffer->buffer.width,
+                                                  buffer->buffer.height,
+                                                  buffer->format,
+                                                  (int*)&name, 1,
+                                                  buffer->stride,
+                                                  buffer->offset,
+                                                  NULL);
+   else
+      img = dri2_dpy->image->createImageFromFds(dri2_dpy->dri_screen,
+                                                buffer->buffer.width,
+                                                buffer->buffer.height,
+                                                buffer->format,
+                                                &fd, 1,
+                                                buffer->stride,
+                                                buffer->offset,
+                                                NULL);
 
    if (img == NULL)
       return;
@@ -1550,6 +1561,8 @@ dri2_bind_wayland_display_wl(_EGLDriver *drv, _EGLDisplay *disp,
                             struct wl_display *wl_dpy)
 {
    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   int ret, flags = 0;
+   uint64_t cap;
 
    (void) drv;
 
@@ -1559,9 +1572,13 @@ dri2_bind_wayland_display_wl(_EGLDriver *drv, _EGLDisplay *disp,
    wl_drm_callbacks.authenticate =
       (int(*)(void *, uint32_t)) dri2_dpy->authenticate;
 
+   ret = drmGetCap(dri2_dpy->fd, DRM_CAP_PRIME, &cap);
+   if (ret == 0 && cap == (DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT))
+      flags |= WAYLAND_DRM_PRIME;
+
    dri2_dpy->wl_server_drm =
           wayland_drm_init(wl_dpy, dri2_dpy->device_name,
-                            &wl_drm_callbacks, disp);
+                            &wl_drm_callbacks, disp, flags);
 
    if (!dri2_dpy->wl_server_drm)
           return EGL_FALSE;
index 7f3ed4e37455dd5c35231a01b3b45b99cbfd9454..6dfdf946fc808edd4d7eda38f74f6cf454026983 100644 (file)
@@ -132,6 +132,7 @@ struct dri2_egl_display
    struct wl_event_queue    *wl_queue;
    int                      authenticated;
    int                      formats;
+   uint32_t                  capabilities;
 #endif
 
    int (*authenticate) (_EGLDisplay *disp, uint32_t id);
index b5cd04a5ec3549bf2c95579c3eff91eb10f52545..740fc7d4e0ff21e2068e131260af7d7553905797 100644 (file)
@@ -451,6 +451,46 @@ static const struct wl_callback_listener frame_listener = {
        wayland_frame_callback
 };
 
+static void
+create_wl_buffer(struct dri2_egl_surface *dri2_surf)
+{
+   struct dri2_egl_display *dri2_dpy =
+      dri2_egl_display(dri2_surf->base.Resource.Display);
+   int fd;
+
+   if (dri2_surf->current->wl_buffer != NULL)
+      return;
+
+   if (dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME) {
+      dri2_dpy->image->queryImage(dri2_surf->current->dri_image,
+                                  __DRI_IMAGE_ATTRIB_FD, &fd);
+
+      dri2_surf->current->wl_buffer =
+         wl_drm_create_prime_buffer(dri2_dpy->wl_drm,
+                                    fd,
+                                    dri2_surf->base.Width,
+                                    dri2_surf->base.Height,
+                                    dri2_surf->format,
+                                    0, dri2_surf->current->pitch,
+                                    0, 0,
+                                    0, 0);
+      close(fd);
+   } else {
+      dri2_surf->current->wl_buffer =
+         wl_drm_create_buffer(dri2_dpy->wl_drm,
+                              dri2_surf->current->name,
+                              dri2_surf->base.Width,
+                              dri2_surf->base.Height,
+                              dri2_surf->current->pitch,
+                              dri2_surf->format);
+   }
+
+   wl_proxy_set_queue((struct wl_proxy *) dri2_surf->current->wl_buffer,
+                      dri2_dpy->wl_queue);
+   wl_buffer_add_listener(dri2_surf->current->wl_buffer,
+                          &wl_buffer_listener, dri2_surf);
+}
+
 /**
  * Called via eglSwapBuffers(), drv->API.SwapBuffers().
  */
@@ -488,19 +528,7 @@ dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
    dri2_surf->current = dri2_surf->back;
    dri2_surf->back = NULL;
 
-   if (dri2_surf->current->wl_buffer == NULL) {
-      dri2_surf->current->wl_buffer =
-         wl_drm_create_buffer(dri2_dpy->wl_drm,
-                              dri2_surf->current->name,
-                              dri2_surf->base.Width,
-                              dri2_surf->base.Height,
-                              dri2_surf->current->pitch,
-                              dri2_surf->format);
-      wl_proxy_set_queue((struct wl_proxy *) dri2_surf->current->wl_buffer,
-                         dri2_dpy->wl_queue);
-      wl_buffer_add_listener(dri2_surf->current->wl_buffer,
-                             &wl_buffer_listener, dri2_surf);
-   }
+   create_wl_buffer(dri2_surf);
 
    wl_surface_attach(dri2_surf->wl_win->surface,
                      dri2_surf->current->wl_buffer,
@@ -629,6 +657,14 @@ drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
    }
 }
 
+static void
+drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t value)
+{
+   struct dri2_egl_display *dri2_dpy = data;
+
+   dri2_dpy->capabilities = value;
+}
+
 static void
 drm_handle_authenticated(void *data, struct wl_drm *drm)
 {
@@ -640,7 +676,8 @@ drm_handle_authenticated(void *data, struct wl_drm *drm)
 static const struct wl_drm_listener drm_listener = {
        drm_handle_device,
        drm_handle_format,
-       drm_handle_authenticated
+       drm_handle_authenticated,
+       drm_handle_capabilities
 };
 
 static void
@@ -649,9 +686,11 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
 {
    struct dri2_egl_display *dri2_dpy = data;
 
+   if (version > 1)
+      version = 2;
    if (strcmp(interface, "wl_drm") == 0) {
       dri2_dpy->wl_drm =
-         wl_registry_bind(registry, name, &wl_drm_interface, 1);
+         wl_registry_bind(registry, name, &wl_drm_interface, version);
       wl_drm_add_listener(dri2_dpy->wl_drm, &drm_listener, dri2_dpy);
    }
 }
index d02aab66324b7ff2a0f2b5fd217c28b3f949eeb6..7e2073a73f399a97cdc74c5cfc7a2b2a2481896d 100644 (file)
@@ -31,6 +31,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stddef.h>
+#include <unistd.h>
 
 #include <wayland-server.h>
 #include "wayland-drm.h"
@@ -41,6 +42,7 @@ struct wl_drm {
 
        void *user_data;
        char *device_name;
+        uint32_t flags;
 
        struct wayland_drm_callbacks *callbacks;
 };
@@ -67,7 +69,8 @@ const static struct wl_buffer_interface drm_buffer_interface = {
 
 static void
 create_buffer(struct wl_client *client, struct wl_resource *resource,
-              uint32_t id, uint32_t name, int32_t width, int32_t height,
+              uint32_t id, uint32_t name, int fd,
+              int32_t width, int32_t height,
               uint32_t format,
               int32_t offset0, int32_t stride0,
               int32_t offset1, int32_t stride1,
@@ -93,7 +96,7 @@ create_buffer(struct wl_client *client, struct wl_resource *resource,
        buffer->offset[2] = offset2;
        buffer->stride[2] = stride2;
 
-        drm->callbacks->reference_buffer(drm->user_data, name, buffer);
+        drm->callbacks->reference_buffer(drm->user_data, name, fd, buffer);
        if (buffer->driver_buffer == NULL) {
                wl_resource_post_error(resource,
                                       WL_DRM_ERROR_INVALID_NAME,
@@ -131,7 +134,7 @@ drm_create_buffer(struct wl_client *client, struct wl_resource *resource,
         }
 
         create_buffer(client, resource, id,
-                      name, width, height, format, 0, stride, 0, 0, 0, 0);
+                      name, -1, width, height, format, 0, stride, 0, 0, 0, 0);
 }
 
 static void
@@ -159,10 +162,24 @@ drm_create_planar_buffer(struct wl_client *client,
            return;
         }
 
-        create_buffer(client, resource, id, name, width, height, format,
+        create_buffer(client, resource, id, name, -1, width, height, format,
                       offset0, stride0, offset1, stride1, offset2, stride2);
 }
 
+static void
+drm_create_prime_buffer(struct wl_client *client,
+                        struct wl_resource *resource,
+                        uint32_t id, int fd,
+                        int32_t width, int32_t height, uint32_t format,
+                        int32_t offset0, int32_t stride0,
+                        int32_t offset1, int32_t stride1,
+                        int32_t offset2, int32_t stride2)
+{
+        create_buffer(client, resource, id, 0, fd, width, height, format,
+                      offset0, stride0, offset1, stride1, offset2, stride2);
+        close(fd);
+}
+
 static void
 drm_authenticate(struct wl_client *client,
                 struct wl_resource *resource, uint32_t id)
@@ -180,7 +197,8 @@ drm_authenticate(struct wl_client *client,
 const static struct wl_drm_interface drm_interface = {
        drm_authenticate,
        drm_create_buffer,
-        drm_create_planar_buffer
+        drm_create_planar_buffer,
+        drm_create_prime_buffer
 };
 
 static void
@@ -188,6 +206,7 @@ bind_drm(struct wl_client *client, void *data, uint32_t version, uint32_t id)
 {
        struct wl_drm *drm = data;
        struct wl_resource *resource;
+        uint32_t capabilities;
 
        resource = wl_client_add_object(client, &wl_drm_interface,
                                        &drm_interface, id, data);
@@ -204,11 +223,19 @@ bind_drm(struct wl_client *client, void *data, uint32_t version, uint32_t id)
         wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_NV12);
         wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_NV16);
         wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_YUYV);
+
+        capabilities = 0;
+        if (drm->flags & WAYLAND_DRM_PRIME)
+           capabilities |= WL_DRM_CAPABILITY_PRIME;
+
+        if (version >= 2)
+           wl_resource_post_event(resource, WL_DRM_CAPABILITIES, capabilities);
 }
 
 struct wl_drm *
 wayland_drm_init(struct wl_display *display, char *device_name,
-                 struct wayland_drm_callbacks *callbacks, void *user_data)
+                 struct wayland_drm_callbacks *callbacks, void *user_data,
+                 uint32_t flags)
 {
        struct wl_drm *drm;
 
@@ -218,6 +245,7 @@ wayland_drm_init(struct wl_display *display, char *device_name,
        drm->device_name = strdup(device_name);
        drm->callbacks = callbacks;
        drm->user_data = user_data;
+        drm->flags = flags;
 
        wl_display_add_global(display, &wl_drm_interface, drm, bind_drm);
 
index 3e8f95173ac30f135134afdcf7662113483419ab..335073a879c8034fe0021c7df7d45f0accddc213 100644 (file)
@@ -82,15 +82,18 @@ struct wl_drm_buffer {
 struct wayland_drm_callbacks {
        int (*authenticate)(void *user_data, uint32_t id);
 
-       void (*reference_buffer)(void *user_data, uint32_t name,
+        void (*reference_buffer)(void *user_data, uint32_t name, int fd,
                                  struct wl_drm_buffer *buffer);
 
        void (*release_buffer)(void *user_data, struct wl_drm_buffer *buffer);
 };
 
+enum { WAYLAND_DRM_PRIME = 0x01 };
+
 struct wl_drm *
 wayland_drm_init(struct wl_display *display, char *device_name,
-                struct wayland_drm_callbacks *callbacks, void *user_data);
+                struct wayland_drm_callbacks *callbacks, void *user_data,
+                 uint32_t flags);
 
 void
 wayland_drm_uninit(struct wl_drm *drm);
index 265d4f892af241ad07ed322cb8299dd90cdbc960..8a3ad69b21d663417335cd7d7593e232cf6e44cf 100644 (file)
@@ -29,7 +29,7 @@
 
   <!-- drm support. This object is created by the server and published
        using the display's global event. -->
-  <interface name="wl_drm" version="1">
+  <interface name="wl_drm" version="2">
     <enum name="error">
       <entry name="authenticate_fail" value="0"/>
       <entry name="invalid_format" value="1"/>
       <arg name="stride2" type="int"/>
     </request>
 
+    <!-- Create a wayland buffer for the prime fd.  Use for regular and planar
+         buffers.  Pass 0 for offset and stride for unused planes. -->
+    <request name="create_prime_buffer" since="2">
+      <arg name="id" type="new_id" interface="wl_buffer"/>
+      <arg name="name" type="fd"/>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+      <arg name="format" type="uint"/>
+      <arg name="offset0" type="int"/>
+      <arg name="stride0" type="int"/>
+      <arg name="offset1" type="int"/>
+      <arg name="stride1" type="int"/>
+      <arg name="offset2" type="int"/>
+      <arg name="stride2" type="int"/>
+    </request>
+
     <!-- Notification of the path of the drm device which is used by
          the server.  The client should use this device for creating
          local buffers.  Only buffers created from this device should
 
     <!-- Raised if the authenticate request succeeded -->
     <event name="authenticated"/>
+
+    <enum name="capability" since="2">
+      <description summary="wl_drm capability bitmask">
+        Bitmask of capabilities.
+      </description>
+      <entry name="prime" value="1" summary="wl_drm prime available"/>
+    </enum>
+
+    <event name="capabilities">
+      <arg name="value" type="uint"/>
+    </event>
   </interface>
 
 </protocol>