[vlc-devel] [PATCH 04/48] vdpau: library mechanism to share VDPAU instance

Rémi Denis-Courmont remi at remlab.net
Tue Jul 2 19:51:30 CEST 2013


VDPAU surfaces cannot be shared across multiple device instances.
Thus the same instance must be used within a pipeline.

Sharing instances also saves resources (X11 server connections).
---
 modules/hw/vdpau/Makefile.am |   2 +-
 modules/hw/vdpau/instance.c  | 235 +++++++++++++++++++++++++++++++++++++++++++
 modules/hw/vdpau/vlc_vdpau.h |  35 +++++++
 3 files changed, 271 insertions(+), 1 deletion(-)
 create mode 100644 modules/hw/vdpau/instance.c

diff --git a/modules/hw/vdpau/Makefile.am b/modules/hw/vdpau/Makefile.am
index f210018..f45f1b1 100644
--- a/modules/hw/vdpau/Makefile.am
+++ b/modules/hw/vdpau/Makefile.am
@@ -3,7 +3,7 @@ include $(top_srcdir)/modules/common.am
 
 AM_CFLAGS += $(VDPAU_CFLAGS)
 
-libvlc_vdpau_la_SOURCES = vlc_vdpau.c vlc_vdpau.h
+libvlc_vdpau_la_SOURCES = vlc_vdpau.c vlc_vdpau.h instance.c
 libvlc_vdpau_la_CPPFLAGS =
 libvlc_vdpau_la_LIBADD = $(X_LIBS) $(X_PRE_LIBS) -lX11 \
 	$(LIBDL) $(LIBPTHREAD)
diff --git a/modules/hw/vdpau/instance.c b/modules/hw/vdpau/instance.c
new file mode 100644
index 0000000..6194d88
--- /dev/null
+++ b/modules/hw/vdpau/instance.c
@@ -0,0 +1,235 @@
+/*****************************************************************************
+ * instance.c: VDPAU instance management for VLC
+ *****************************************************************************
+ * Copyright (C) 2013 Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <pthread.h>
+#include <X11/Xlib.h>
+#include <vlc_common.h>
+#include "vlc_vdpau.h"
+
+#pragma GCC visibility push(default)
+
+typedef struct vdp_instance
+{
+    Display *display;
+    vdp_t *vdp;
+    VdpDevice device;
+
+    int num; /**< X11 screen number */
+    char *name; /**< X11 display name */
+
+    uintptr_t refs; /**< Reference count */
+    struct vdp_instance *next;
+} vdp_instance_t;
+
+static VdpStatus vdp_instance_create(const char *name, int num,
+                                     vdp_instance_t **pp)
+{
+    size_t namelen = (name != NULL) ? (strlen(name) + 1) : 0;
+    vdp_instance_t *vi = malloc(sizeof (*vi) + namelen);
+
+    if (unlikely(vi == NULL))
+        return VDP_STATUS_RESOURCES;
+
+    vi->display = XOpenDisplay(name);
+    if (vi->display == NULL)
+    {
+        free(vi);
+        return VDP_STATUS_ERROR;
+    }
+
+    vi->next = NULL;
+    if (name != NULL)
+    {
+        vi->name = (void *)(vi + 1);
+        memcpy(vi->name, name, namelen);
+    }
+    else
+        vi->name = NULL;
+    if (num >= 0)
+        vi->num = num;
+    else
+        vi->num = XDefaultScreen(vi->display);
+    vi->refs = 1;
+
+    VdpStatus err = vdp_create_x11(vi->display, vi->num,
+                                   &vi->vdp, &vi->device);
+    if (err != VDP_STATUS_OK)
+    {
+        XCloseDisplay(vi->display);
+        free(vi);
+        return err;
+    }
+    *pp = vi;
+    return VDP_STATUS_OK;
+}
+
+static void vdp_instance_destroy(vdp_instance_t *vi)
+{
+    vdp_device_destroy(vi->vdp, vi->device);
+    vdp_destroy_x11(vi->vdp);
+    XCloseDisplay(vi->display);
+    free(vi);
+}
+
+/** Compares two string pointers that might be NULL */
+static int strnullcmp(const char *a, const char *b)
+{
+    if (b == NULL)
+        return a != NULL;
+    if (a == NULL)
+        return -1;
+    return strcmp(a, b);
+}
+
+static int vicmp(const char *name, int num, const vdp_instance_t *vi)
+{
+    int val = strnullcmp(name, vi->name);
+    if (val)
+        return val;
+
+    if (num < 0)
+        num = XDefaultScreen(vi->display);
+    return num - vi->num;
+}
+
+static vdp_instance_t *list = NULL;
+
+static vdp_instance_t *vdp_instance_lookup(const char *name, int num)
+{
+    vdp_instance_t *vi = NULL;
+
+    for (vi = list; vi != NULL; vi = vi->next)
+    {
+        int val = vicmp(name, num, vi);
+        if (val == 0)
+        {
+            assert(vi->refs < UINTPTR_MAX);
+            vi->refs++;
+            break;
+        }
+    }
+    return vi;
+}
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+/** Finds an existing VDPAU instance for the given X11 display and screen.
+ * If not found, (try to) create a new one.
+ * @param display_name X11 display string, NULL for default
+ * @param snum X11 screen number, strictly negative for default
+ **/
+VdpStatus vdp_get_x11(const char *display_name, int snum,
+                      vdp_t **restrict vdpp, VdpDevice *restrict devicep)
+{
+    vdp_instance_t *vi, *vi2;
+    VdpStatus err = VDP_STATUS_RESOURCES;
+
+    if (display_name == NULL)
+    {
+        display_name = getenv("DISPLAY");
+        if (display_name == NULL)
+            return VDP_STATUS_ERROR;
+    }
+
+    pthread_mutex_lock(&lock);
+    vi = vdp_instance_lookup(display_name, snum);
+    pthread_mutex_unlock(&lock);
+    if (vi != NULL)
+        goto found;
+
+    err = vdp_instance_create(display_name, snum, &vi);
+    if (err != VDP_STATUS_OK)
+        return err;
+
+    pthread_mutex_lock(&lock);
+    vi2 = vdp_instance_lookup(display_name, snum);
+    if (unlikely(vi2 != NULL))
+    {   /* Another thread created the instance (race condition corner case) */
+        pthread_mutex_unlock(&lock);
+        vdp_instance_destroy(vi);
+        vi = vi2;
+    }
+    else
+    {
+        vi->next = list;
+        list = vi;
+        pthread_mutex_unlock(&lock);
+    }
+found:
+    *vdpp = vi->vdp;
+    *devicep = vi->device;
+    return VDP_STATUS_OK;
+}
+
+vdp_t *vdp_hold_x11(vdp_t *vdp, VdpDevice *restrict devp)
+{
+    vdp_instance_t *vi, **pp = &list;
+
+    pthread_mutex_lock(&lock);
+    for (;;)
+    {
+        vi = *pp;
+        assert(vi != NULL);
+        if (vi->vdp == vdp)
+            break;
+        pp = &vi->next;
+    }
+
+    assert(vi->refs < UINTPTR_MAX);
+    vi->refs++;
+    pthread_mutex_unlock(&lock);
+
+    if (devp != NULL)
+        *devp = vi->device;
+   return vdp;
+}
+
+void vdp_release_x11(vdp_t *vdp)
+{
+    vdp_instance_t *vi, **pp = &list;
+
+    pthread_mutex_lock(&lock);
+    for (;;)
+    {
+        vi = *pp;
+        assert(vi != NULL);
+        if (vi->vdp == vdp)
+            break;
+        pp = &vi->next;
+    }
+
+    assert(vi->refs > 0);
+    vi->refs--;
+    if (vi->refs > 0)
+        vi = NULL; /* Keep the instance for now */
+    else
+        *pp = vi->next; /* Unlink the instance */
+    pthread_mutex_unlock(&lock);
+
+    if (vi != NULL)
+        vdp_instance_destroy(vi);
+}
diff --git a/modules/hw/vdpau/vlc_vdpau.h b/modules/hw/vdpau/vlc_vdpau.h
index 69ce11f..281f409 100644
--- a/modules/hw/vdpau/vlc_vdpau.h
+++ b/modules/hw/vdpau/vlc_vdpau.h
@@ -168,4 +168,39 @@ VdpStatus vdp_create_x11(void *dpy, int snum, vdp_t **vdpp, VdpDevice *devp);
  * vdp_device_destroy() first.
  */
 void vdp_destroy_x11(vdp_t *);
+
+/* Instance reuse */
+
+/**
+ * Finds an existing pair of VDPAU instance and VDPAU device matching the
+ * specified X11 display and screen number from within the process-wide list.
+ * If no existing instance corresponds, connect to the X11 server,
+ * create a new pair of instance and device, and set the reference count to 1.
+ * @param name X11 display name
+ * @param snum X11 screen number
+ * @param vdp memory location to hold the VDPAU instance pointer [OUT]
+ * @param dev memory location to hold the VDPAU device handle [OUT]
+ * @return VDP_STATUS_OK on success, otherwise a VDPAU error code.
+ *
+ * @note Use vdp_release_x11() to release the instance. <b>Do not use</b>
+ * vdp_device_destroy() and/or vdp_destroy_x11() with vdp_get_x11().
+ */
+VdpStatus vdp_get_x11(const char *name, int num, vdp_t **vdp, VdpDevice *dev);
+
+/**
+ * Increases the reference count of a VDPAU instance created by vdp_get_x11().
+ * @param vdp VDPAU instance (as returned by vdp_get_x11())
+ * @param device location to store the VDPAU device corresponding to the
+ *               VDPAU instance (or NULL) [OUT]
+ * @return the first pameter, always succeeds.
+ */
+vdp_t *vdp_hold_x11(vdp_t *vdp, VdpDevice *device);
+
+/**
+ * Decreases the reference count of a VDPAU instance created by vdp_get_x11().
+ * If it reaches zero, destroy the corresponding VDPAU device, then the VDPAU
+ * instance and remove the pair from the process-wide list.
+ */
+void vdp_release_x11(vdp_t *);
+
 #endif
-- 
1.8.3.2




More information about the vlc-devel mailing list