Commit 44eee00d authored by Jean-Paul Saman's avatar Jean-Paul Saman

VAAPI-XCB: rewrite Surface handling.

The surface handling has been rewritten to be more consistent and to
avoid reusing surfaces that are still in use by the display thread. Some
VAAPI wrapper drivers use the DirectSurface mode, meaning that the surface
handed by the application is used directly for displaying. It does not
copy the surface into a display surface. When the driver does this, then
you cannot reuse this surface which is being displayed for rendering the
next picture too
parent d21c629b
...@@ -60,123 +60,223 @@ static void vlc_va_unlock(void) ...@@ -60,123 +60,223 @@ static void vlc_va_unlock(void)
} }
/* */ /* */
static VASurfaceID *vlc_va_alloc_surfaces( vlc_va_conn_t *conn, static vlc_va_surface_t *alloc_surfaces( vlc_va_conn_t *conn,
const int width, const int height, const int fourcc, unsigned int *requested ) const int width, const int height,
const int fourcc, unsigned int *requested )
{ {
assert( *requested > 0 ); assert( *requested > 0 );
VASurfaceID *p_surfaces = (VASurfaceID *)calloc(*requested, sizeof(VASurfaceID)); VASurfaceID *p_ids = (VASurfaceID *)calloc(*requested, sizeof(VASurfaceID));
if( p_surfaces == NULL ) if( p_ids == NULL )
return NULL; return NULL;
VAStatus status = vaCreateSurfaces( conn->p_display, width, height, fourcc, VAStatus status = vaCreateSurfaces( conn->p_display, width, height, fourcc,
*requested, p_surfaces ); *requested, p_ids );
if( status != VA_STATUS_SUCCESS ) if( status != VA_STATUS_SUCCESS )
{ {
free( p_surfaces ); free( p_ids );
return NULL;
}
vlc_va_surface_t *p_surfaces = calloc(*requested, sizeof(vlc_va_surface_t));
if( p_surfaces == NULL )
{
free( p_ids );
return NULL; return NULL;
} }
for( unsigned int i = 0; i < *requested; i++ )
{
p_surfaces[i].i_id = p_ids[i];
vlc_mutex_init(&p_surfaces[i].lock);
}
free( p_ids );
return p_surfaces; return p_surfaces;
} }
static VASurfaceID *vlc_va_realloc_surfaces( vlc_va_conn_t *conn, static vlc_va_surface_t *realloc_surfaces( vlc_va_conn_t *conn,
const int width, const int height, const int fourcc, unsigned int *requested ) const int width, const int height,
const int fourcc, unsigned int *requested )
{ {
assert( *requested > 0 ); assert( *requested > 0 );
VASurfaceID *p_surfaces = (VASurfaceID *)realloc( conn->p_surface_ids, vlc_va_surface_t *p_surfaces = (vlc_va_surface_t *)realloc( conn->pool.p_surfaces,
(*requested) * sizeof(VASurfaceID) ); (*requested) * sizeof(vlc_va_surface_t) );
if( p_surfaces == NULL ) if( p_surfaces == NULL )
return NULL; return NULL;
int count = *requested - conn->i_surface_count; const unsigned int count = *requested - conn->pool.count;
assert( count > 0 ); assert( count > 0 );
VASurfaceID *p_tmp = &p_surfaces[conn->i_surface_count + 1]; VASurfaceID *ids = calloc(count, sizeof(VASurfaceID));
if( ids == NULL )
return NULL;
VAStatus status = vaCreateSurfaces( conn->p_display, width, height, fourcc, VAStatus status = vaCreateSurfaces( conn->p_display, width, height, fourcc,
count, p_tmp ); count, ids );
if( status != VA_STATUS_SUCCESS ) if( status != VA_STATUS_SUCCESS )
abort(); /* FATAL ERROR */
for( unsigned int i = 0; i < count; i++ )
{ {
free( p_surfaces ); vlc_va_surface_t *p = &p_surfaces[conn->pool.count + i];
return NULL; p->i_id = ids[i];
vlc_mutex_init(&p->lock);
} }
conn->pool.count += count;
assert(conn->pool.count == *requested);
free(ids);
return p_surfaces; return p_surfaces;
} }
static VASurfaceID *vlc_va_create_surfaces( vlc_va_conn_t *conn, static bool create_surfaces( vlc_va_conn_t *conn,
const int width, const int height, const int fourcc, const unsigned int num ) const int width, const int height,
const int fourcc, const unsigned int num )
{ {
assert(conn); assert(conn);
if( (width <= 0) || (height <= 0) || (num <= 0) ) if( (width <= 0) || (height <= 0) || (num <= 0) )
return NULL; return false;
if( conn->i_ref_count > 1 ) if( conn->i_ref_count > 1 )
{ {
assert( conn->p_surface_ids != NULL ); assert( conn->pool.p_surfaces != NULL );
if( num > conn->i_surface_count ) if( num > conn->pool.count )
{ {
assert(0); assert(0);
/* FIXME: probably wise to destroy decoding context first /* FIXME: probably wise to destroy decoding context first
and then recreate it */ and then recreate it */
unsigned requested = num; unsigned requested = num;
vlc_va_realloc_surfaces( conn, width, height, fourcc, &requested ); realloc_surfaces( conn, width, height, fourcc, &requested );
assert( num == requested ); assert( num == requested );
} }
return conn->p_surface_ids; return true;
} }
assert( conn->p_surface_ids == NULL ); assert( conn->pool.p_surfaces == NULL );
assert( conn->i_surface_count == 0 ); assert( conn->pool.count == 0 );
unsigned requested = num; unsigned requested = num;
conn->p_surface_ids = vlc_va_alloc_surfaces( conn, width, height, conn->pool.p_surfaces = alloc_surfaces( conn, width, height,
fourcc, &requested ); fourcc, &requested );
assert( num == requested ); assert( num == requested );
if( conn->p_surface_ids ) conn->pool.current = 0;
conn->i_surface_count = requested; if( conn->pool.p_surfaces )
return conn->p_surface_ids; {
conn->pool.count = requested;
return true;
}
return false;
} }
static void vlc_va_destroy_surfaces( vlc_va_conn_t *conn ) static void destroy_surfaces( vlc_va_conn_t *conn )
{ {
assert( conn ); assert( conn );
assert( conn->i_surface_count > 0 ); assert( conn->pool.count > 0 );
assert( conn->p_surface_ids ); assert( conn->pool.p_surfaces );
if( conn->i_ref_count > 1 ) if( conn->i_ref_count > 1 )
return; return;
for( unsigned int i = 0; i < conn->i_surface_count; i++ ) for( unsigned int i = 0; i < conn->pool.count; i++ )
{ {
VASurfaceID i_id = conn->p_surface_ids[i]; vlc_va_surface_t *surface = &conn->pool.p_surfaces[i];
vlc_mutex_lock(&surface->lock);
if( surface->i_refcount > 1 )
abort();
#if VA_CHECK_VERSION(0,31,0) #if VA_CHECK_VERSION(0,31,0)
if( vaSyncSurface( conn->p_display, i_id ) ) if( vaSyncSurface( conn->p_display, surface->i_id ) )
#else #else
if( vaSyncSurface( conn->p_display, conn->i_context_id, i_id ) ) if( vaSyncSurface( conn->p_display, conn->i_context_id, surface->i_id ) )
#endif #endif
{ {
fprintf(stderr, "Fatal error reboot your system\n" ); fprintf(stderr, "Fatal error reboot your system\n" );
abort(); abort();
} }
surface->i_refcount --;
assert(surface->i_refcount == 0);
vaDestroySurfaces( conn->p_display, &surface->i_id, 1 );
vlc_mutex_unlock(&surface->lock);
vlc_mutex_destroy(&surface->lock);
} }
if( conn->p_surface_ids != NULL ) free(conn->pool.p_surfaces);
vaDestroySurfaces( conn->p_display, conn->p_surface_ids,
conn->i_surface_count );
free(conn->p_surface_ids ); conn->pool.count = 0;
conn->pool.p_surfaces = NULL;
}
conn->i_surface_count = 0; static vlc_va_surface_t *get_surface(vlc_va_conn_t *conn)
conn->p_surface_ids = NULL; {
assert( conn );
assert( conn->pool.count > 0 );
assert( conn->pool.p_surfaces );
unsigned int next = conn->pool.current;
unsigned int tries = 0;
while (tries < conn->pool.count)
{
VAStatus status;
VASurfaceStatus ready;
vlc_va_surface_t *surface;
surface = &conn->pool.p_surfaces[next];
vlc_mutex_lock(&surface->lock);
VASurfaceID id = surface->i_id;
status = vaQuerySurfaceStatus(conn->p_display, id, &ready);
if ((status == VA_STATUS_SUCCESS) &&
(ready == VASurfaceReady) &&
(surface->i_refcount == 0))
{
surface->i_refcount++;
conn->pool.current = (next + 1) % conn->pool.count;
vlc_mutex_unlock(&surface->lock);
return surface;
}
vlc_mutex_unlock(&surface->lock);
next = (next + 1) % conn->pool.count;
tries++;
}
return NULL;
}
static void release_surface(vlc_va_conn_t *conn, vlc_va_surface_t *surface)
{
assert( conn );
assert( conn->pool.count > 0 );
assert( conn->pool.p_surfaces );
assert( surface );
vlc_mutex_lock(&surface->lock);
if (surface->i_refcount == 0)
abort();
VAStatus status;
VASurfaceStatus ready;
VASurfaceID id = surface->i_id;
status = vaQuerySurfaceStatus(conn->p_display, id, &ready);
if ((status == VA_STATUS_SUCCESS) &&
(ready == VASurfaceReady))
{
surface->i_refcount--;
assert(surface->i_refcount == 0);
}
vlc_mutex_unlock(&surface->lock);
} }
/* Global VAAPI connection state */ /* Global VAAPI connection state */
static vlc_va_conn_t vlc_va_conn = { static vlc_va_conn_t vlc_va_conn = {
.x11 = NULL, .x11 = NULL,
.p_display = 0, .p_display = 0,
.b_direct_rendering = false,
/* libva version */ /* libva version */
.i_version_major = 0, .i_version_major = 0,
.i_version_minor = 0, .i_version_minor = 0,
...@@ -186,11 +286,16 @@ static vlc_va_conn_t vlc_va_conn = { ...@@ -186,11 +286,16 @@ static vlc_va_conn_t vlc_va_conn = {
.lock = vlc_va_lock, .lock = vlc_va_lock,
.unlock = vlc_va_unlock, .unlock = vlc_va_unlock,
/* surfaces - NOTE: must be called with lock held */ /* surfaces - NOTE: must be called with lock held */
.i_surface_count = 0, .pool = {
.p_surface_ids = NULL, .count = 0,
.current = 0,
.p_surfaces = NULL,
},
.i_context_id = VA_INVALID_ID, .i_context_id = VA_INVALID_ID,
.create_surfaces = vlc_va_create_surfaces, .create_surfaces = create_surfaces,
.destroy_surfaces = vlc_va_destroy_surfaces, .destroy_surfaces = destroy_surfaces,
.get_surface = get_surface,
.release_surface = release_surface,
}; };
/* */ /* */
...@@ -199,6 +304,32 @@ static vlc_va_conn_t *vlc_va_get_conn( void ) ...@@ -199,6 +304,32 @@ static vlc_va_conn_t *vlc_va_get_conn( void )
return (vlc_va_conn_t *) &vlc_va_conn; return (vlc_va_conn_t *) &vlc_va_conn;
} }
static bool uses_direct_mapping(vlc_va_conn_t *conn)
{
VADisplayAttribute attr;
VAStatus status;
#if VA_CHECK_VERSION(0,34,0)
attr.type = VADisplayAttribRenderMode;
attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
status = vaGetDisplayAttributes(conn->p_display, &attr, 1);
if (status == VA_STATUS_SUCCESS)
return !(attr.value & (VA_RENDER_MODE_LOCAL_OVERLAY|
VA_RENDER_MODE_EXTERNAL_OVERLAY));
#else
/* Does the driver use DirectSurface mode then we cannot use the
same surface while it is still being in the display pipeline. */
attr.type = VADisplayAttribDirectSurface;
attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
status = vaGetDisplayAttributes(conn->p_display, &attr, 1);
if (status == VA_STATUS_SUCCESS)
return !attr.value;
#endif
return false;
}
vlc_va_conn_t *vlc_va_Initialize( const char *display ) vlc_va_conn_t *vlc_va_Initialize( const char *display )
{ {
vlc_mutex_lock( &vlc_va_conn_lock ); vlc_mutex_lock( &vlc_va_conn_lock );
...@@ -227,6 +358,7 @@ vlc_va_conn_t *vlc_va_Initialize( const char *display ) ...@@ -227,6 +358,7 @@ vlc_va_conn_t *vlc_va_Initialize( const char *display )
goto error; goto error;
conn->i_ref_count++; conn->i_ref_count++;
conn->b_direct_rendering = uses_direct_mapping(conn);
vlc_mutex_unlock( &vlc_va_conn_lock ); vlc_mutex_unlock( &vlc_va_conn_lock );
return conn; return conn;
...@@ -246,8 +378,8 @@ void vlc_va_Terminate( vlc_va_conn_t *conn ) ...@@ -246,8 +378,8 @@ void vlc_va_Terminate( vlc_va_conn_t *conn )
conn->i_ref_count--; conn->i_ref_count--;
if( conn->i_ref_count == 0 ) if( conn->i_ref_count == 0 )
{ {
assert( conn->i_surface_count == 0 ); assert( conn->pool.count == 0 );
assert( conn->p_surface_ids == NULL ); assert( conn->pool.p_surfaces == NULL );
assert( conn->p_display ); assert( conn->p_display );
vaTerminate( conn->p_display ); vaTerminate( conn->p_display );
......
...@@ -28,6 +28,7 @@ typedef struct vlc_va_t vlc_va_t; ...@@ -28,6 +28,7 @@ typedef struct vlc_va_t vlc_va_t;
struct vlc_va_t { struct vlc_va_t {
char *description; char *description;
bool direct_rendering;
vlc_object_t *obj; vlc_object_t *obj;
int (*setup)(vlc_va_t *, void **hw, vlc_fourcc_t *output, int (*setup)(vlc_va_t *, void **hw, vlc_fourcc_t *output,
...@@ -37,6 +38,7 @@ struct vlc_va_t { ...@@ -37,6 +38,7 @@ struct vlc_va_t {
int (*extract)(vlc_va_t *, picture_t *dst, AVFrame *src); int (*extract)(vlc_va_t *, picture_t *dst, AVFrame *src);
void (*put)(vlc_va_t *, AVFrame *dst, picture_t *src); void (*put)(vlc_va_t *, AVFrame *dst, picture_t *src);
int (*display)(vlc_va_t *, picture_t *dst); int (*display)(vlc_va_t *, picture_t *dst);
bool (*query)(vlc_va_t *, picture_t *src);
void (*close)(vlc_va_t *); void (*close)(vlc_va_t *);
}; };
...@@ -61,6 +63,10 @@ static inline void vlc_va_Put(vlc_va_t *va, AVFrame *dst, picture_t *src) ...@@ -61,6 +63,10 @@ static inline void vlc_va_Put(vlc_va_t *va, AVFrame *dst, picture_t *src)
{ {
va->put(va, dst, src); va->put(va, dst, src);
} }
static inline bool vlc_va_QueryReady(vlc_va_t *va, picture_t *src)
{
return va->query(va, src);
}
static inline int vlc_va_Display(vlc_va_t *va, picture_t *dst) static inline int vlc_va_Display(vlc_va_t *va, picture_t *dst)
{ {
return va->display(va, dst); return va->display(va, dst);
......
...@@ -57,19 +57,15 @@ typedef struct ...@@ -57,19 +57,15 @@ typedef struct
vlc_va_conn_t *conn; vlc_va_conn_t *conn;
VAConfigID i_config_id; VAConfigID i_config_id;
VAContextID i_context_id;
struct vaapi_context hw_ctx; struct vaapi_context hw_ctx;
/* */ /* */
int i_surface_count; int i_surface_count;
unsigned int i_surface_order;
int i_surface_width; int i_surface_width;
int i_surface_height; int i_surface_height;
vlc_fourcc_t i_surface_chroma; vlc_fourcc_t i_surface_chroma;
vlc_va_surface_t *p_surface;
VAImage image; VAImage image;
copy_cache_t image_cache; copy_cache_t image_cache;
...@@ -117,7 +113,7 @@ static int Open( vlc_va_vaapi_t *p_va, int i_codec_id, int i_count ) ...@@ -117,7 +113,7 @@ static int Open( vlc_va_vaapi_t *p_va, int i_codec_id, int i_count )
break; break;
case CODEC_ID_H264: case CODEC_ID_H264:
i_profile = VAProfileH264High; i_profile = VAProfileH264High;
i_surface_count = 16+1; i_surface_count = 20+1;
break; break;
default: default:
return VLC_EGENERIC; return VLC_EGENERIC;
...@@ -130,7 +126,6 @@ static int Open( vlc_va_vaapi_t *p_va, int i_codec_id, int i_count ) ...@@ -130,7 +126,6 @@ static int Open( vlc_va_vaapi_t *p_va, int i_codec_id, int i_count )
/* */ /* */
memset( p_va, 0, sizeof(*p_va) ); memset( p_va, 0, sizeof(*p_va) );
p_va->i_config_id = VA_INVALID_ID; p_va->i_config_id = VA_INVALID_ID;
p_va->i_context_id = VA_INVALID_ID;
p_va->image.image_id = VA_INVALID_ID; p_va->image.image_id = VA_INVALID_ID;
/* Create a VA display */ /* Create a VA display */
...@@ -198,21 +193,6 @@ error: ...@@ -198,21 +193,6 @@ error:
return VLC_EGENERIC; return VLC_EGENERIC;
} }
static inline void DestroySurfaces( vlc_va_conn_t *conn )
{
assert( conn );
assert( conn->p_surface_ids );
conn->destroy_surfaces( conn );
}
static inline VASurfaceID *CreateSurfaces( vlc_va_conn_t *conn,
const int width, const int height, const int fourcc, const int num )
{
assert( conn );
return conn->create_surfaces( conn, width, height, fourcc, num );
}
static void DestroyDecodingContext( vlc_va_vaapi_t *p_va ) static void DestroyDecodingContext( vlc_va_vaapi_t *p_va )
{ {
p_va->conn->lock(); p_va->conn->lock();
...@@ -226,16 +206,14 @@ static void DestroyDecodingContext( vlc_va_vaapi_t *p_va ) ...@@ -226,16 +206,14 @@ static void DestroyDecodingContext( vlc_va_vaapi_t *p_va )
CopyCleanCache( &p_va->image_cache ); CopyCleanCache( &p_va->image_cache );
} }
if( p_va->i_context_id != VA_INVALID_ID ) if( p_va->conn->i_context_id != VA_INVALID_ID )
vaDestroyContext( p_va->conn->p_display, p_va->i_context_id ); vaDestroyContext( p_va->conn->p_display, p_va->conn->i_context_id );
DestroySurfaces( p_va->conn ); vlc_va_conn_DestroySurfaces( p_va->conn );
free( p_va->p_surface );
/* */ /* */
p_va->conn->i_context_id = VA_INVALID_ID;
p_va->image.image_id = VA_INVALID_ID; p_va->image.image_id = VA_INVALID_ID;
p_va->i_context_id = VA_INVALID_ID;
p_va->p_surface = NULL;
p_va->i_surface_width = 0; p_va->i_surface_width = 0;
p_va->i_surface_height = 0; p_va->i_surface_height = 0;
p_va->conn->unlock(); p_va->conn->unlock();
...@@ -249,44 +227,37 @@ static int CreateDecodingContext( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fo ...@@ -249,44 +227,37 @@ static int CreateDecodingContext( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fo
p_va->conn->lock(); p_va->conn->lock();
/* Create surfaces */ /* Create surfaces */
VASurfaceID *pi_surface_id = CreateSurfaces( p_va->conn, i_width, i_height, bool ok = vlc_va_conn_CreateSurface( p_va->conn, i_width, i_height,
VA_RT_FORMAT_YUV420, p_va->i_surface_count ); VA_RT_FORMAT_YUV420, p_va->i_surface_count );
if( pi_surface_id == NULL ) if( !ok )
{ {
p_va->i_surface_count = 0;
p_va->conn->unlock(); p_va->conn->unlock();
return VLC_EGENERIC; return VLC_EGENERIC;
} }
/* */ /* */
p_va->i_surface_count = p_va->conn->i_surface_count;
p_va->p_surface = calloc( p_va->i_surface_count, sizeof(*p_va->p_surface) );
if( !p_va->p_surface )
{
p_va->conn->unlock();
return VLC_EGENERIC;
}
p_va->image.image_id = VA_INVALID_ID; p_va->image.image_id = VA_INVALID_ID;
p_va->i_context_id = VA_INVALID_ID; assert(p_va->conn->i_context_id == VA_INVALID_ID);
for( int i = 0; i < p_va->i_surface_count; i++ ) /* Create a context */
const unsigned int count = vlc_va_conn_SurfacesCount(p_va->conn);
VASurfaceID *p_ids = calloc(count, sizeof(VASurfaceID));
if( !p_ids )
goto error;
for( unsigned int i = 0; i < count; i++ )
{ {
vlc_va_surface_t *p_surface = &p_va->p_surface[i]; p_ids[i] = p_va->conn->pool.p_surfaces[i].i_id;
p_surface->i_id = pi_surface_id[i];
p_surface->i_refcount = 0;
p_surface->i_order = 0;
} }
/* Create a context */
if( vaCreateContext( p_va->conn->p_display, p_va->i_config_id, if( vaCreateContext( p_va->conn->p_display, p_va->i_config_id,
i_width, i_height, VA_PROGRESSIVE, i_width, i_height, VA_PROGRESSIVE,
pi_surface_id, p_va->i_surface_count, &p_va->i_context_id ) ) p_ids, count,
&p_va->conn->i_context_id ) != VA_STATUS_SUCCESS )
{ {
p_va->i_context_id = VA_INVALID_ID; free(p_ids);
p_va->conn->i_context_id = VA_INVALID_ID;
goto error; goto error;
} }
p_va->conn->i_context_id = p_va->i_context_id; free(p_ids);
/* Find and create a supported image chroma */ /* Find and create a supported image chroma */
int i_fmt_count = vaMaxNumImageFormats( p_va->conn->p_display ); int i_fmt_count = vaMaxNumImageFormats( p_va->conn->p_display );
...@@ -301,7 +272,7 @@ static int CreateDecodingContext( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fo ...@@ -301,7 +272,7 @@ static int CreateDecodingContext( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fo
} }
VAImage testImage; VAImage testImage;
if(vaDeriveImage(p_va->conn->p_display, pi_surface_id[0], &testImage) == VA_STATUS_SUCCESS) if(vaDeriveImage(p_va->conn->p_display, p_va->conn->pool.p_surfaces[0].i_id, &testImage) == VA_STATUS_SUCCESS)
{ {
p_va->b_supports_derive = true; p_va->b_supports_derive = true;
vaDestroyImage(p_va->conn->p_display, testImage.image_id); vaDestroyImage(p_va->conn->p_display, testImage.image_id);
...@@ -320,7 +291,8 @@ static int CreateDecodingContext( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fo ...@@ -320,7 +291,8 @@ static int CreateDecodingContext( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fo
continue; continue;
} }
/* Validate that vaGetImage works with this format */ /* Validate that vaGetImage works with this format */
if( vaGetImage( p_va->conn->p_display, pi_surface_id[0], if( vaGetImage( p_va->conn->p_display,
p_va->conn->pool.p_surfaces[0].i_id,
0, 0, i_width, i_height, 0, 0, i_width, i_height,
p_va->image.image_id) ) p_va->image.image_id) )
{ {
...@@ -353,7 +325,7 @@ static int CreateDecodingContext( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fo ...@@ -353,7 +325,7 @@ static int CreateDecodingContext( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fo
memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) ); memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
p_va->hw_ctx.display = p_va->conn->p_display; p_va->hw_ctx.display = p_va->conn->p_display;
p_va->hw_ctx.config_id = p_va->i_config_id; p_va->hw_ctx.config_id = p_va->i_config_id;
p_va->hw_ctx.context_id = p_va->i_context_id; p_va->hw_ctx.context_id = p_va->conn->i_context_id;
/* */ /* */
p_va->i_surface_chroma = i_chroma; p_va->i_surface_chroma = i_chroma;
...@@ -406,7 +378,7 @@ static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff ) ...@@ -406,7 +378,7 @@ static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff )
#if VA_CHECK_VERSION(0,31,0) #if VA_CHECK_VERSION(0,31,0)
if( vaSyncSurface( p_va->conn->p_display, i_surface_id ) ) if( vaSyncSurface( p_va->conn->p_display, i_surface_id ) )
#else #else
if( vaSyncSurface( p_va->conn->p_display, p_va->i_context_id, i_surface_id ) ) if( vaSyncSurface( p_va->conn->p_display, p_va->conn->i_context_id, i_surface_id ) )
#endif #endif
goto error; goto error;
...@@ -490,7 +462,7 @@ static void Put(vlc_va_t *p_external, AVFrame *p_ff, picture_t *p_picture ) ...@@ -490,7 +462,7 @@ static void Put(vlc_va_t *p_external, AVFrame *p_ff, picture_t *p_picture )
abort(); abort();
picture_sys_t *p_surface = p_picture->p_sys; picture_sys_t *p_surface = p_picture->p_sys;
assert(p_surface->i_refcount == 1); assert(p_surface->i_refcount == 2);
/* */ /* */
for( int i = 0; i < 4; i++ ) for( int i = 0; i < 4; i++ )
...@@ -519,7 +491,7 @@ static int DisplayPicture( vlc_va_t *p_external, picture_t *p_picture ) ...@@ -519,7 +491,7 @@ static int DisplayPicture( vlc_va_t *p_external, picture_t *p_picture )
#if VA_CHECK_VERSION(0,31,0) #if VA_CHECK_VERSION(0,31,0)
if( vaSyncSurface( p_va->conn->p_display, i_surface_id ) ) if( vaSyncSurface( p_va->conn->p_display, i_surface_id ) )
#else #else
if( vaSyncSurface( p_va->conn->p_display, p_va->i_context_id, i_surface_id ) ) if( vaSyncSurface( p_va->conn->p_display, p_va->conn->i_context_id, i_surface_id ) )
#endif #endif
goto error; goto error;
...@@ -535,33 +507,19 @@ error: ...@@ -535,33 +507,19 @@ error:
static int Get( vlc_va_t *p_external, AVFrame *p_ff ) static int Get( vlc_va_t *p_external, AVFrame *p_ff )
{ {
vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external); vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
int i_old;
int i;
/* Grab an unused surface, in case none are, try the oldest
* XXX using the oldest is a workaround in case a problem happens with ffmpeg */
for( i = 0, i_old = 0; i < p_va->i_surface_count; i++ )
{
vlc_va_surface_t *p_surface = &p_va->p_surface[i];
if( p_surface->i_refcount == 0 ) p_va->conn->lock();
break;
if( p_surface->i_order < p_va->p_surface[i_old].i_order ) vlc_va_surface_t *p_surface = vlc_va_conn_GetSurface(p_va->conn);
i_old = i; if (!p_surface)
{
msg_Warn(p_external->obj, "No free surface found.");
p_va->conn->unlock();
return VLC_EGENERIC;
} }
if( i >= p_va->i_surface_count )
i = i_old;
vlc_va_surface_t *p_surface = &p_va->p_surface[i];
/* NOTE: reset surface refcount otherwise they will be leaked and
* mayhem may happen down the road.
*/
p_surface->i_refcount = 1;
p_surface->i_order = p_va->i_surface_order++;
/* */ /* */
vlc_mutex_lock(&p_surface->lock);
for( int i = 0; i < 4; i++ ) for( int i = 0; i < 4; i++ )
{ {
p_ff->data[i] = NULL; p_ff->data[i] = NULL;
...@@ -570,6 +528,9 @@ static int Get( vlc_va_t *p_external, AVFrame *p_ff ) ...@@ -570,6 +528,9 @@ static int Get( vlc_va_t *p_external, AVFrame *p_ff )
if( i == 0 || i == 3 ) if( i == 0 || i == 3 )
p_ff->data[i] = (void*)(uintptr_t)p_surface->i_id;/* Yummie */ p_ff->data[i] = (void*)(uintptr_t)p_surface->i_id;/* Yummie */
} }
vlc_mutex_unlock(&p_surface->lock);
p_va->conn->unlock();
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -577,18 +538,20 @@ static void Release( vlc_va_t *p_external, AVFrame *p_ff ) ...@@ -577,18 +538,20 @@ static void Release( vlc_va_t *p_external, AVFrame *p_ff )
{ {
vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external); vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
p_va->conn->lock();
VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3]; VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
for( int i = 0; i < p_va->i_surface_count; i++ ) const unsigned int count = vlc_va_conn_SurfacesCount(p_va->conn);
for( unsigned int i = 0; i < count; i++ )
{ {
vlc_va_surface_t *p_surface = &p_va->p_surface[i]; vlc_va_surface_t *p_surface = &p_va->conn->pool.p_surfaces[i];
if (!p_surface)
abort();
if( p_surface->i_id == i_surface_id ) if( p_surface->i_id == i_surface_id )
{ vlc_va_conn_ReleaseSurface(p_va->conn, p_surface);
if( p_surface->i_refcount > 0 )
p_surface->i_refcount--;
}
} }
p_va->conn->unlock();
} }
static void Close( vlc_va_vaapi_t *p_va ) static void Close( vlc_va_vaapi_t *p_va )
...@@ -615,6 +578,33 @@ static void Delete( vlc_va_t *p_external ) ...@@ -615,6 +578,33 @@ static void Delete( vlc_va_t *p_external )
free( p_va ); free( p_va );
} }
static bool QuerySurfaceReady( vlc_va_t *p_external, picture_t *pic )
{
vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
assert(pic);
assert(pic->p_sys);
assert(p_va);
if (!p_va->conn)
abort();
p_va->conn->lock();
bool ready = false;
VAStatus status;
VASurfaceStatus surface;
VASurfaceID id = pic->p_sys->i_id;
status = vaQuerySurfaceStatus(p_va->conn->p_display, id, &surface);
if ((status == VA_STATUS_SUCCESS) &&
(surface == VASurfaceReady))
ready = true;
p_va->conn->unlock();
return ready;
}
/* */ /* */
vlc_va_t *vlc_va_NewVaapi( vlc_object_t *obj, int i_codec_id, int i_count ) vlc_va_t *vlc_va_NewVaapi( vlc_object_t *obj, int i_codec_id, int i_count )
{ {
...@@ -632,6 +622,7 @@ vlc_va_t *vlc_va_NewVaapi( vlc_object_t *obj, int i_codec_id, int i_count ) ...@@ -632,6 +622,7 @@ vlc_va_t *vlc_va_NewVaapi( vlc_object_t *obj, int i_codec_id, int i_count )
} }
/* */ /* */
p_va->va.direct_rendering = p_va->conn->b_direct_rendering;
p_va->va.obj = obj; p_va->va.obj = obj;
p_va->va.setup = Setup; p_va->va.setup = Setup;
p_va->va.get = Get; p_va->va.get = Get;
...@@ -639,6 +630,7 @@ vlc_va_t *vlc_va_NewVaapi( vlc_object_t *obj, int i_codec_id, int i_count ) ...@@ -639,6 +630,7 @@ vlc_va_t *vlc_va_NewVaapi( vlc_object_t *obj, int i_codec_id, int i_count )
p_va->va.release = Release; p_va->va.release = Release;
p_va->va.extract = Extract; p_va->va.extract = Extract;
p_va->va.display = DisplayPicture; p_va->va.display = DisplayPicture;
p_va->va.query = QuerySurfaceReady;
p_va->va.close = Delete; p_va->va.close = Delete;
return &p_va->va; return &p_va->va;
} }
......
...@@ -29,10 +29,13 @@ ...@@ -29,10 +29,13 @@
* context. * context.
*/ */
typedef struct vlc_va_conn_t vlc_va_conn_t; typedef struct vlc_va_conn_t vlc_va_conn_t;
typedef struct picture_sys_t vlc_va_surface_t;
struct vlc_va_conn_t struct vlc_va_conn_t
{ {
Display *x11; /* x11 display connection */ Display *x11; /* x11 display connection */
VADisplay p_display; VADisplay p_display;
bool b_direct_rendering; /* VAAPI uses direct rendering */
int i_version_major; int i_version_major;
int i_version_minor; int i_version_minor;
int i_ref_count; int i_ref_count;
...@@ -41,14 +44,21 @@ struct vlc_va_conn_t ...@@ -41,14 +44,21 @@ struct vlc_va_conn_t
void (*lock)(void); void (*lock)(void);
void (*unlock)(void); void (*unlock)(void);
/* NOTE: must be called with lock held */ /* NOTE: must be called with conn->lock held */
unsigned int i_surface_count; struct {
VASurfaceID *p_surface_ids; unsigned int count; /* number of surfaces allocated */
unsigned int current; /* current used surface (private use only) */
vlc_va_surface_t *p_surfaces;
} pool; /* vlc_va_surface_t pool */
VAContextID i_context_id; VAContextID i_context_id;
VASurfaceID *(*create_surfaces)(vlc_va_conn_t *conn, const int width, const int height, bool (*create_surfaces)(vlc_va_conn_t *conn, const int width, const int height,
const int fourcc, const unsigned num); const int fourcc, const unsigned num);
void (*destroy_surfaces)(vlc_va_conn_t *conn); void (*destroy_surfaces)(vlc_va_conn_t *conn);
vlc_va_surface_t *(*get_surface)(vlc_va_conn_t *conn);
void (*release_surface)(vlc_va_conn_t *conn, vlc_va_surface_t *surface);
}; };
/* Initialize shared connection to libva */ /* Initialize shared connection to libva */
...@@ -57,7 +67,7 @@ vlc_va_conn_t *vlc_va_Initialize(const char *display); ...@@ -57,7 +67,7 @@ vlc_va_conn_t *vlc_va_Initialize(const char *display);
/* Deinitialize shared connection to libva */ /* Deinitialize shared connection to libva */
void vlc_va_Terminate(vlc_va_conn_t *conn); void vlc_va_Terminate(vlc_va_conn_t *conn);
static inline VASurfaceID *vlc_va_conn_CreateSurface(vlc_va_conn_t *conn, static inline bool vlc_va_conn_CreateSurface(vlc_va_conn_t *conn,
const int width, const int height, const int fourcc, const int num) const int width, const int height, const int fourcc, const int num)
{ {
return conn->create_surfaces(conn, width, height, fourcc, num); return conn->create_surfaces(conn, width, height, fourcc, num);
...@@ -68,34 +78,43 @@ static inline void vlc_va_conn_DestroySurfaces(vlc_va_conn_t *conn) ...@@ -68,34 +78,43 @@ static inline void vlc_va_conn_DestroySurfaces(vlc_va_conn_t *conn)
conn->destroy_surfaces(conn); conn->destroy_surfaces(conn);
} }
static inline VASurfaceID *vlc_va_conn_GetSurfaces(vlc_va_conn_t *conn) static inline vlc_va_surface_t *vlc_va_conn_GetSurface(vlc_va_conn_t *conn)
{ {
return conn->p_surface_ids; return conn->get_surface(conn);
} }
static inline int vlc_va_conn_SurfacesCount(vlc_va_conn_t *conn) static inline void vlc_va_conn_ReleaseSurface(vlc_va_conn_t *conn, vlc_va_surface_t *surface)
{ {
return conn->i_surface_count; conn->release_surface(conn, surface);
} }
/* vlc_va_surface_t */ static inline unsigned int vlc_va_conn_SurfacesCount(vlc_va_conn_t *conn)
typedef struct
{ {
VASurfaceID i_id; return conn->pool.count;
int i_refcount; }
unsigned int i_order;
} vlc_va_surface_t;
/* picture_sys_t */ /* picture_sys_t */
struct picture_sys_t struct picture_sys_t
{ {
/* Link surface with picture_sys_t permanently */
VASurfaceID i_id; VASurfaceID i_id;
int i_refcount; int i_refcount;
unsigned int i_cache; /* subpicture cache identifier */
/* subpicture */ vlc_mutex_t lock;
unsigned int i_cache;
}; };
static inline void vlc_va_SurfaceHold(vlc_va_surface_t *surface)
{
vlc_mutex_lock(&surface->lock);
surface->i_refcount++;
vlc_mutex_unlock(&surface->lock);
}
static inline void vlc_va_SurfaceRelease(vlc_va_surface_t *surface)
{
vlc_mutex_lock(&surface->lock);
surface->i_refcount--;
vlc_mutex_unlock(&surface->lock);
}
#endif #endif
#endif #endif
...@@ -357,7 +357,7 @@ void CloseVaapiX11(vlc_object_t *obj) ...@@ -357,7 +357,7 @@ void CloseVaapiX11(vlc_object_t *obj)
if (sys->vaconn) if (sys->vaconn)
{ {
sys->vaconn->lock(); sys->vaconn->lock();
sys->vaconn->destroy_surfaces(sys->vaconn); vlc_va_conn_DestroySurfaces(sys->vaconn);
sys->vaconn->unlock(); sys->vaconn->unlock();
vlc_va_Terminate(sys->vaconn); vlc_va_Terminate(sys->vaconn);
} }
...@@ -572,6 +572,18 @@ static void DisplayVASurface(vout_display_t *vd, picture_t *picture) ...@@ -572,6 +572,18 @@ static void DisplayVASurface(vout_display_t *vd, picture_t *picture)
vout_display_place_t place; vout_display_place_t place;
vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
/*
#define VA_FILTER_SCALING_DEFAULT 0x00000000
#define VA_FILTER_SCALING_FAST 0x00000100
#define VA_FILTER_SCALING_HQ 0x00000200
#define VA_FILTER_SCALING_NL_ANAMORPHIC 0x00000300
#define VA_FILTER_SCALING_MASK 0x00000f00
*/
unsigned int flags = VA_FILTER_SCALING_DEFAULT;
/* NOTE: vaCreateContext() specifies VA_PROGRESSIVE */
flags |= picture->b_progressive ? VA_FRAME_PICTURE :
(picture->b_top_field_first ? VA_TOP_FIELD : VA_BOTTOM_FIELD);
msg_Dbg( vd, "Displaying surface id: %d / %p", surface->i_id, picture );
VAStatus status; VAStatus status;
status = vaPutSurface(sys->vaconn->p_display, surface->i_id, sys->window, status = vaPutSurface(sys->vaconn->p_display, surface->i_id, sys->window,
...@@ -579,9 +591,7 @@ static void DisplayVASurface(vout_display_t *vd, picture_t *picture) ...@@ -579,9 +591,7 @@ static void DisplayVASurface(vout_display_t *vd, picture_t *picture)
vd->source.i_visible_width, vd->source.i_visible_height, vd->source.i_visible_width, vd->source.i_visible_height,
place.x, place.y, place.width, place.height, place.x, place.y, place.width, place.height,
NULL, 0, /* clipping */ NULL, 0, /* clipping */
/* NOTE: vaCreateContext() specifies VA_PROGRESSIVE */ flags);
picture->b_progressive ? VA_FRAME_PICTURE :
(picture->b_top_field_first ? VA_TOP_FIELD : VA_BOTTOM_FIELD));
if (status != VA_STATUS_SUCCESS) if (status != VA_STATUS_SUCCESS)
msg_Err(vd, "failed displaying picture: %d (surface id=%d): %p", msg_Err(vd, "failed displaying picture: %d (surface id=%d): %p",
status, surface->i_id, picture); status, surface->i_id, picture);
...@@ -648,9 +658,11 @@ out: ...@@ -648,9 +658,11 @@ out:
*/ */
static int PictureLock(picture_t *picture) static int PictureLock(picture_t *picture)
{ {
picture_sys_t *surface = picture->p_sys; vlc_va_surface_t *surface = picture->p_sys;
if (surface) if (surface == NULL)
surface->i_refcount++; abort();
vlc_va_SurfaceHold(surface);
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -658,12 +670,11 @@ static void PictureUnlock(picture_t *picture) ...@@ -658,12 +670,11 @@ static void PictureUnlock(picture_t *picture)
{ {
assert(picture->p_sys); assert(picture->p_sys);
picture_sys_t *surface = picture->p_sys; vlc_va_surface_t *surface = picture->p_sys;
if (surface == NULL) if (surface == NULL)
abort(); abort();
if (surface->i_refcount > 0) vlc_va_SurfaceRelease(surface);
surface->i_refcount--;
} }
/* PictureRelease is called when the picture pool is destroyed. */ /* PictureRelease is called when the picture pool is destroyed. */
...@@ -672,7 +683,6 @@ static void PictureRelease(picture_t *picture) ...@@ -672,7 +683,6 @@ static void PictureRelease(picture_t *picture)
if (picture->i_refcount > 0) if (picture->i_refcount > 0)
picture->i_refcount--; picture->i_refcount--;
free(picture->p_sys);
picture->p_sys = NULL; picture->p_sys = NULL;
} }
...@@ -689,7 +699,7 @@ static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count) ...@@ -689,7 +699,7 @@ static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count)
/* Create surfaces */ /* Create surfaces */
sys->vaconn->lock(); sys->vaconn->lock();
unsigned alloc_count = __MAX(sys->vaconn->i_surface_count, requested_count); unsigned int alloc_count = __MAX(vlc_va_conn_SurfacesCount(sys->vaconn), requested_count);
picture_t **pic_array = (picture_t **) calloc(alloc_count, sizeof(picture_t *)); picture_t **pic_array = (picture_t **) calloc(alloc_count, sizeof(picture_t *));
if (!pic_array) if (!pic_array)
...@@ -702,21 +712,11 @@ static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count) ...@@ -702,21 +712,11 @@ static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count)
if (!pic) if (!pic)
break; break;
assert(pic->p_sys == NULL); assert(pic->p_sys == NULL);
picture_sys_t *pic_sys = (picture_sys_t *) malloc(sizeof(picture_sys_t));
if (!pic_sys)
{
picture_Release(pic);
break;
}
pic->format.i_chroma = VLC_CODEC_VAAPI_SURFACE;
pic_sys->i_id = VA_INVALID_SURFACE; pic->format.i_chroma = VLC_CODEC_VAAPI_SURFACE;
pic_sys->i_refcount = 0;
pic_sys->i_cache = 0;
pic_array[count] = pic; pic_array[count] = pic;
pic_array[count]->b_progressive = true; pic_array[count]->b_progressive = true;
pic_array[count]->p_sys = pic_sys;
pic_array[count]->pf_release = PictureRelease; pic_array[count]->pf_release = PictureRelease;
} }
...@@ -725,24 +725,26 @@ static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count) ...@@ -725,24 +725,26 @@ static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count)
goto error; goto error;
unsigned int i_surface_count = __MIN(count, alloc_count); unsigned int i_surface_count = __MIN(count, alloc_count);
/* Create surfaces */
VASurfaceID *pi_surface_id;
pi_surface_id = sys->vaconn->create_surfaces(sys->vaconn,
vd->fmt.i_visible_width, vd->fmt.i_visible_height,
VA_RT_FORMAT_YUV420, i_surface_count);
assert( pi_surface_id );
assert( i_surface_count == count ); assert( i_surface_count == count );
if (pi_surface_id == NULL) msg_Info( vd, "Requested %d/%d surfaces for picture pool.",
i_surface_count, vlc_va_conn_SurfacesCount(sys->vaconn) );
/* Create surfaces */
if( !vlc_va_conn_CreateSurface( sys->vaconn,
vd->fmt.i_visible_width,
vd->fmt.i_visible_height,
VA_RT_FORMAT_YUV420, i_surface_count ) )
{
/* FAIL */
goto error; goto error;
}
for (unsigned int i = 0; i < i_surface_count; i++) for (unsigned int i = 0; i < i_surface_count; i++)
{ {
picture_sys_t *pic_sys = pic_array[i]->p_sys; vlc_va_surface_t *surface = vlc_va_conn_GetSurface( sys->vaconn );
assert(pic_sys); if( surface )
pic_array[i]->p_sys = (picture_sys_t *)surface;
pic_sys->i_id = pi_surface_id[i];
} }
/* Register pool with video output core */ /* Register pool with video output core */
...@@ -770,8 +772,15 @@ error: ...@@ -770,8 +772,15 @@ error:
{ {
picture_t *pic = pic_array[i]; picture_t *pic = pic_array[i];
if (pic) if (pic)
{
if (pic->p_sys)
{
vlc_va_surface_t *surface = (vlc_va_surface_t *) pic->p_sys;
vlc_va_conn_ReleaseSurface(sys->vaconn, surface);
}
picture_Release(pic); picture_Release(pic);
} }
}
free(pic_array); free(pic_array);
fail: fail:
......
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