[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