Commit f756e0c9 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

vdpau: library mechanism to share VDPAU instance

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).
parent 8d3bd45f
......@@ -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)
......
/*****************************************************************************
* 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);
}
......@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment