OSDN Git Service

Add pointer to struct wl_interface for driver to use
[android-x86/hardware-intel-common-libva.git] / va / wayland / va_wayland_drm.c
index 958ea85..4cd3f6c 100644 (file)
 #include "va_wayland_private.h"
 #include "wayland-drm-client-protocol.h"
 
-/* XXX: Wayland/DRM support currently lives in Mesa libEGL.so.* library */
-#define LIBWAYLAND_DRM_NAME "libEGL.so.1"
-
 typedef struct va_wayland_drm_context {
     struct va_wayland_context   base;
-    void                       *handle;
+    struct wl_event_queue      *queue;
     struct wl_drm              *drm;
+    uint32_t                    drm_name;
     struct wl_registry         *registry;
-    void                       *drm_interface;
     unsigned int                is_authenticated        : 1;
 } VADisplayContextWaylandDRM;
 
@@ -58,25 +55,29 @@ drm_handle_device(void *data, struct wl_drm *drm, const char *device)
     struct drm_state * const drm_state = ctx->drm_state;
     drm_magic_t magic;
     struct stat st;
+    int fd = -1;
 
-    if (stat(device, &st) < 0) {
-        va_wayland_error("failed to identify %s: %s (errno %d)",
+    fd = open(device, O_RDWR);
+    if (fd < 0) {
+        va_wayland_error("failed to open %s: %s (errno %d)",
                          device, strerror(errno), errno);
         return;
     }
 
-    if (!S_ISCHR(st.st_mode)) {
-        va_wayland_error("%s is not a device", device);
+    if (fstat(fd, &st) < 0) {
+        va_wayland_error("failed to identify %s: %s (errno %d)",
+                         device, strerror(errno), errno);
+        close(fd);
         return;
     }
 
-    drm_state->fd = open(device, O_RDWR);
-    if (drm_state->fd < 0) {
-        va_wayland_error("failed to open %s: %s (errno %d)",
-                         device, strerror(errno), errno);
+    if (!S_ISCHR(st.st_mode)) {
+        va_wayland_error("%s is not a device", device);
+        close(fd);
         return;
     }
 
+    drm_state->fd = fd;
     drmGetMagic(drm_state->fd, &magic);
     wl_drm_authenticate(wl_drm_ctx->drm, magic);
 }
@@ -135,18 +136,10 @@ va_wayland_drm_destroy(VADisplayContextP pDisplayContext)
     struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
 
     vtable->has_prime_sharing = 0;
+    vtable->wl_interface = NULL;
 
-    if (wl_drm_ctx->drm) {
-        wl_drm_destroy(wl_drm_ctx->drm);
-        wl_drm_ctx->drm = NULL;
-    }
     wl_drm_ctx->is_authenticated = 0;
 
-    if (wl_drm_ctx->handle) {
-        dlclose(wl_drm_ctx->handle);
-        wl_drm_ctx->handle = NULL;
-    }
-
     if (drm_state) {
         if (drm_state->fd >= 0) {
             close(drm_state->fd);
@@ -161,78 +154,180 @@ static void
 registry_handle_global(
     void               *data,
     struct wl_registry *registry,
-    uint32_t            id,
+    uint32_t            name,
     const char         *interface,
     uint32_t            version
 )
 {
-    struct va_wayland_drm_context *wl_drm_ctx = data;
+    VADisplayContextP const pDisplayContext = data;
+    struct va_wayland_drm_context * const wl_drm_ctx = pDisplayContext->opaque;
 
     if (strcmp(interface, "wl_drm") == 0) {
+        /* bind to at most version 2, but also support version 1 if
+         * compositor does not have v2
+         */
+        wl_drm_ctx->drm_name = name;
         wl_drm_ctx->drm =
-            wl_registry_bind(wl_drm_ctx->registry, id, wl_drm_ctx->drm_interface, 2);
+            wl_registry_bind(wl_drm_ctx->registry, name, &wl_drm_interface,
+                             (version < 2) ? version : 2);
+
+        if (wl_drm_ctx->drm
+            && wl_drm_add_listener(wl_drm_ctx->drm, &drm_listener, pDisplayContext) != 0) {
+            va_wayland_error("could not add listener to wl_drm");
+            wl_drm_destroy(wl_drm_ctx->drm);
+            wl_drm_ctx->drm = NULL;
+        }
+    }
+}
+
+static void
+registry_handle_global_remove(
+    void               *data,
+    struct wl_registry *registry,
+    uint32_t            name
+)
+{
+    struct va_wayland_drm_context *wl_drm_ctx = data;
+
+    if (wl_drm_ctx->drm && name == wl_drm_ctx->drm_name) {
+        wl_drm_destroy(wl_drm_ctx->drm);
+        wl_drm_ctx->drm = NULL;
     }
 }
 
 static const struct wl_registry_listener registry_listener = {
     registry_handle_global,
-    NULL,
+    registry_handle_global_remove
 };
 
+static bool
+wayland_roundtrip_queue(struct wl_display *display,
+                         struct wl_event_queue *queue)
+{
+    if (wl_display_roundtrip_queue(display, queue) < 0) {
+        int err = wl_display_get_error(display);
+        va_wayland_error("Wayland roundtrip error: %s (errno %d)", strerror(err), err);
+        return false;
+    } else {
+        return true;
+    }
+}
+
 bool
 va_wayland_drm_create(VADisplayContextP pDisplayContext)
 {
+    bool result = false;
     VADriverContextP const ctx = pDisplayContext->pDriverContext;
     struct va_wayland_drm_context *wl_drm_ctx;
     struct drm_state *drm_state;
     struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
+    struct wl_display *wrapped_display = NULL;
+
+    vtable->wl_interface = NULL;
 
     wl_drm_ctx = malloc(sizeof(*wl_drm_ctx));
-    if (!wl_drm_ctx)
-        return false;
+    if (!wl_drm_ctx) {
+        va_wayland_error("could not allocate wl_drm_ctx");
+        goto end;
+    }
     wl_drm_ctx->base.destroy            = va_wayland_drm_destroy;
-    wl_drm_ctx->handle                  = NULL;
+    wl_drm_ctx->queue                   = NULL;
     wl_drm_ctx->drm                     = NULL;
-    wl_drm_ctx->drm_interface           = NULL;
+    wl_drm_ctx->registry                = NULL;
     wl_drm_ctx->is_authenticated        = 0;
     pDisplayContext->opaque             = wl_drm_ctx;
     pDisplayContext->vaGetDriverName    = va_DisplayContextGetDriverName;
 
     drm_state = calloc(1, sizeof(struct drm_state));
-    if (!drm_state)
-        return false;
+    if (!drm_state) {
+        va_wayland_error("could not allocate drm_state");
+        goto end;
+    }
     drm_state->fd        = -1;
     drm_state->auth_type = 0;
     ctx->drm_state       = drm_state;
     vtable->has_prime_sharing = 0;
 
-    wl_drm_ctx->handle = dlopen(LIBWAYLAND_DRM_NAME, RTLD_LAZY|RTLD_LOCAL);
-    if (!wl_drm_ctx->handle)
-        return false;
+    /* Use wrapped wl_display with private event queue to prevent
+     * thread safety issues with applications that e.g. run an event pump
+     * parallel to libva initialization.
+     * Using the default queue, events might get lost and crashes occur
+     * because wl_display_roundtrip is not thread-safe with respect to the
+     * same queue.
+     */
+    wl_drm_ctx->queue = wl_display_create_queue(ctx->native_dpy);
+    if (!wl_drm_ctx->queue) {
+        va_wayland_error("could not create Wayland event queue");
+        goto end;
+    }
 
-    wl_drm_ctx->drm_interface =
-        dlsym(wl_drm_ctx->handle, "wl_drm_interface");
-    if (!wl_drm_ctx->drm_interface)
-        return false;
+    wrapped_display = wl_proxy_create_wrapper(ctx->native_dpy);
+    if (!wrapped_display) {
+        va_wayland_error("could not create Wayland proxy wrapper");
+        goto end;
+    }
 
-    wl_drm_ctx->registry = wl_display_get_registry(ctx->native_dpy);
-    wl_registry_add_listener(wl_drm_ctx->registry, &registry_listener, wl_drm_ctx);
-    wl_display_roundtrip(ctx->native_dpy);
+    /* All created objects will inherit this queue */
+    wl_proxy_set_queue((struct wl_proxy *) wrapped_display, wl_drm_ctx->queue);
+    wl_drm_ctx->registry = wl_display_get_registry(wrapped_display);
+    if (!wl_drm_ctx->registry) {
+        va_wayland_error("could not create wl_registry");
+        goto end;
+    }
+    if (wl_registry_add_listener(wl_drm_ctx->registry, &registry_listener, pDisplayContext) != 0) {
+        va_wayland_error("could not add listener to wl_registry");
+        goto end;
+    }
+    if (!wayland_roundtrip_queue(ctx->native_dpy, wl_drm_ctx->queue))
+        goto end;
 
     /* registry_handle_global should have been called by the
-     * wl_display_roundtrip above
+     * wl_display_roundtrip_queue above
      */
 
+    /* Do not print an error, the compositor might just not support wl_drm */
     if (!wl_drm_ctx->drm)
-        return false;
+        goto end;
+    if (!wayland_roundtrip_queue(ctx->native_dpy, wl_drm_ctx->queue))
+        goto end;
+    if (drm_state->fd < 0) {
+        va_wayland_error("did not get DRM device");
+        goto end;
+    }
 
-    wl_drm_add_listener(wl_drm_ctx->drm, &drm_listener, pDisplayContext);
-    wl_display_roundtrip(ctx->native_dpy);
-    if (drm_state->fd < 0)
-        return false;
+    if (!wayland_roundtrip_queue(ctx->native_dpy, wl_drm_ctx->queue))
+        goto end;
 
-    wl_display_roundtrip(ctx->native_dpy);
-    if (!wl_drm_ctx->is_authenticated)
-        return false;
-    return true;
+    if (!wl_drm_ctx->is_authenticated) {
+        va_wayland_error("Wayland compositor did not respond to DRM authentication");
+        goto end;
+    }
+
+    vtable->wl_interface = &wl_drm_interface;
+    result = true;
+
+end:
+    if (wrapped_display) {
+        wl_proxy_wrapper_destroy(wrapped_display);
+        wrapped_display = NULL;
+    }
+
+    if (wl_drm_ctx) {
+        if (wl_drm_ctx->drm) {
+            wl_drm_destroy(wl_drm_ctx->drm);
+            wl_drm_ctx->drm = NULL;
+        }
+
+        if (wl_drm_ctx->registry) {
+            wl_registry_destroy(wl_drm_ctx->registry);
+            wl_drm_ctx->registry = NULL;
+        }
+
+        if (wl_drm_ctx->queue) {
+            wl_event_queue_destroy(wl_drm_ctx->queue);
+            wl_drm_ctx->queue = NULL;
+        }
+    }
+
+    return result;
 }