Permalink
Browse files

vaapi: add support for new VA/EGL API.

This API allows for mapping a VA surface into as many EGLImages/textures
as there are planes in the surface. e.g. an NV12 VA surface can be mapped
into 2 textures, one for the Y plane and another one for the UV plane.

Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
  • Loading branch information...
gbeauchesne committed Aug 16, 2012
1 parent 421a543 commit b59aa75d77c20ecffd1e7ac66698f567b9833dce
View
@@ -1551,6 +1551,33 @@ if test "x$use_vaapi" != "xno"; then
INCLUDES="$INCLUDES $LIBVA_X11_CFLAGS"
LIBS="$LIBS $LIBVA_X11_LIBS"
has_va_x11="yes"], [use_vaapi="no"])
+
+ dnl Check for new VA/EGL API, i.e. vaGetSurfaceBufferEGL()
+ PKG_CHECK_MODULES([LIBVA_EGL], [libva-egl], [has_va_egl="yes"], [:])
+ if test "$has_va_egl" = "yes"; then
+ AC_CACHE_CHECK([for new VA/EGL API],
+ ac_cv_has_va_egl, [
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $LIBVA_EGL_CFLAGS"
+ saved_LIBS="$LIBS"
+ LIBS="$LIBS $LIBVA_EGL_LIBS"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <va/va_egl.h>]],
+ [[EGLClientBuffer buf; vaGetSurfaceBufferEGL(NULL,0,&buf);]])],
+ [ac_cv_has_va_egl="yes"],
+ [ac_cv_has_va_egl="no"]
+ )
+ CFLAGS="$saved_CFLAGS"
+ LIBS="$saved_LIBS"
+ ])
+ if test "$ac_cv_has_va_egl" = "yes"; then
+ INCLUDES="$INCLUDES $LIBVA_EGL_CFLAGS"
+ LIBS="$LIBS $LIBVA_EGL_LIBS"
+ else
+ has_va_egl="no"
+ fi
+ fi
fi
if test "$use_gl" = "yes"; then
PKG_CHECK_MODULES([LIBVA_GLX], [libva-glx], [
@@ -1575,6 +1602,9 @@ if test "x$use_vaapi" != "xno"; then
if test "$has_va_glx" = "yes"; then
AC_DEFINE([HAVE_VA_GLX], [1], [Defined to 1 if VA/GLX API is available])
fi
+ if test "$has_va_egl" = "yes"; then
+ AC_DEFINE([HAVE_VA_EGL], [1], [Defined to 1 if VA/EGL API is available])
+ fi
USE_VAAPI=1
fi
fi
@@ -162,6 +162,7 @@ CLinuxRendererGLES::CLinuxRendererGLES()
m_flipindex = 0;
m_currentField = FIELD_FULL;
m_reloadShaders = 0;
+ m_YUVShaderFormat = RENDER_FMT_NONE;
m_pYUVShader = NULL;
m_pVideoFilterShader = NULL;
m_scalingMethod = VS_SCALINGMETHOD_LINEAR;
@@ -668,6 +669,7 @@ void CLinuxRendererGLES::LoadShaders(int field)
m_pYUVShader->Free();
delete m_pYUVShader;
m_pYUVShader = NULL;
+ m_YUVShaderFormat = RENDER_FMT_NONE;
}
switch(requestedMethod)
@@ -715,6 +717,7 @@ void CLinuxRendererGLES::LoadShaders(int field)
if (m_pYUVShader && m_pYUVShader->CompileAndLink())
{
+ m_YUVShaderFormat = m_format;
m_renderMethod = RENDER_GLSL;
UpdateVideoFilter();
break;
@@ -724,6 +727,7 @@ void CLinuxRendererGLES::LoadShaders(int field)
m_pYUVShader->Free();
delete m_pYUVShader;
m_pYUVShader = NULL;
+ m_YUVShaderFormat = RENDER_FMT_NONE;
CLog::Log(LOGERROR, "GL: Error enabling YUV2RGB GLSL shader");
// drop through and try SW
}
@@ -1310,10 +1314,91 @@ void CLinuxRendererGLES::RenderRGB(const YUVPLANE& plane)
VerifyGLState();
}
+void CLinuxRendererGLES::RenderYUV(const YUVPLANES& planes, int numPlanes)
+{
+ int i;
+
+ glDisable(GL_DEPTH_TEST);
+
+ for (i = 0; i < numPlanes; i++)
+ {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glEnable(m_textureTarget);
+ glBindTexture(m_textureTarget, planes[i].id);
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ VerifyGLState();
+
+ m_pYUVShader->SetMatrices(g_matrices.GetMatrix(MM_PROJECTION), g_matrices.GetMatrix(MM_MODELVIEW));
+ m_pYUVShader->Enable();
+
+ GLubyte idx[4] = {0, 1, 3, 2}; //determines order of triangle strip
+ GLfloat m_vert[4][3];
+ GLfloat m_tex[3][4][2];
+
+ GLint vertLoc = m_pYUVShader->GetVertexLoc();
+ GLint Yloc = m_pYUVShader->GetYcoordLoc();
+ GLint Uloc = m_pYUVShader->GetUcoordLoc();
+ GLint Vloc = m_pYUVShader->GetVcoordLoc();
+
+ glVertexAttribPointer(vertLoc, 3, GL_FLOAT, 0, 0, m_vert);
+ glVertexAttribPointer(Yloc, 2, GL_FLOAT, 0, 0, m_tex[0]);

This comment has been minimized.

Show comment Hide comment
@theuni

theuni Aug 19, 2012

uniform?

+ glVertexAttribPointer(Uloc, 2, GL_FLOAT, 0, 0, m_tex[1]);
+ glVertexAttribPointer(Vloc, 2, GL_FLOAT, 0, 0, m_tex[2]);
+
+ glEnableVertexAttribArray(vertLoc);
+ glEnableVertexAttribArray(Yloc);
+ glEnableVertexAttribArray(Uloc);
+ glEnableVertexAttribArray(Vloc);
+
+ // Setup vertex position values
+ for(int i = 0; i < 4; i++)
+ {
+ m_vert[i][0] = m_rotatedDestCoords[i].x;
+ m_vert[i][1] = m_rotatedDestCoords[i].y;
+ m_vert[i][2] = 0.0f;// set z to 0
+ }
+
+ // Setup texture coordinates
+ for (int i=0; i<3; i++)
+ {
+ // FIXME: populate plane.rect correctly
+ m_tex[i][0][0] = m_tex[i][3][0] = 0;
+ m_tex[i][0][1] = m_tex[i][1][1] = 0;
+ m_tex[i][1][0] = m_tex[i][2][0] = 1;
+ m_tex[i][2][1] = m_tex[i][3][1] = 1;
+ }
+
+ glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx);
+
+ VerifyGLState();
+
+ m_pYUVShader->Disable();
+ VerifyGLState();
+
+ glDisableVertexAttribArray(vertLoc);
+ glDisableVertexAttribArray(Yloc);
+ glDisableVertexAttribArray(Uloc);
+ glDisableVertexAttribArray(Vloc);
+
+ for (i = 0; i < numPlanes; i++)
+ {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glDisable(m_textureTarget);
+ }
+ glActiveTexture(GL_TEXTURE0);
+
+ g_matrices.MatrixMode(MM_MODELVIEW);
+
+ VerifyGLState();
+}
+
void CLinuxRendererGLES::RenderVAAPI(int index, int field)
{
#if defined(HAVE_LIBVA)
VAAPI::CHolder &va = m_buffers[index].vaapi;
+ YV12Image &im = m_buffers[index].image;
YUVPLANES &planes = m_buffers[index].fields[0];
if (!va.surface)
@@ -1328,11 +1413,59 @@ void CLinuxRendererGLES::RenderVAAPI(int index, int field)
return;
}
+ ERenderFormat format = RENDER_FMT_NONE;
switch (va.surfegl->m_format)
{
case EGL_TEXTURE_RGBA:
RenderRGB(planes[0]);
break;
+#ifdef HAVE_VA_EGL
+ case VA_EGL_BUFFER_STRUCTURE_Y_UV:
+ format = RENDER_FMT_Y_UV;
+ break;
+ case VA_EGL_BUFFER_STRUCTURE_Y_U_V:
+ format = RENDER_FMT_Y_U_V;
+ break;
+#endif
+ }
+
+ if (format && format != m_YUVShaderFormat)
+ {
+ if (m_pYUVShader)
+ {
+ m_pYUVShader->Free();
+ delete m_pYUVShader;
+ m_pYUVShader = NULL;
+ m_YUVShaderFormat = RENDER_FMT_NONE;
+ }
+
+ // Create regular progressive scan shader
+ m_pYUVShader = new YUV2RGBProgressiveShader(false, m_iFlags, format);
+ CLog::Log(LOGNOTICE, "GLES: VAAPI - Selecting Single Pass YUV2RGB shader");
+
+ if (m_pYUVShader && m_pYUVShader->CompileAndLink())
+ {
+ m_YUVShaderFormat = format;
+ UpdateVideoFilter();
+ }
+ else
+ {
+ m_pYUVShader->Free();
+ delete m_pYUVShader;
+ m_pYUVShader = NULL;
+ m_YUVShaderFormat = RENDER_FMT_NONE;
+ CLog::Log(LOGERROR, "GLES: VAAPI - Error enabling YUV2RGB GLSL shader");
+ }
+ }
+
+ if (m_pYUVShader && m_YUVShaderFormat != RENDER_FMT_NONE)
+ {
+ UpdateVideoFilter();
+ m_pYUVShader->SetBlack(g_settings.m_currentVideoSettings.m_Brightness * 0.01f - 0.5f);
+ m_pYUVShader->SetContrast(g_settings.m_currentVideoSettings.m_Contrast * 0.02f);
+ m_pYUVShader->SetWidth(im.width);
+ m_pYUVShader->SetHeight(im.height);
+ RenderYUV(planes, va.surfegl->m_numPlanes);
}
#endif
}
@@ -1835,6 +1968,10 @@ void CLinuxRendererGLES::UploadVAAPITexture(int index)
if (!va.surfegl)
{
VAAPI::CSurfaceEGL *surface = NULL;
+#ifdef HAVE_VA_EGL
+ if (!surface)
+ surface = VAAPI::CSurfaceEGLBuffer::Create(va.display, im.width, im.height);
+#endif
#ifdef HAVE_VA_X11
if (!surface)
surface = VAAPI::CSurfaceEGLPixmap::Create(va.display, im.width, im.height);
@@ -1876,6 +2013,14 @@ void CLinuxRendererGLES::UploadVAAPITexture(int index)
case EGL_TEXTURE_RGBA:
numPlanes = 1;
break;
+#ifdef HAVE_VA_EGL
+ case VA_EGL_BUFFER_STRUCTURE_Y_UV:
+ numPlanes = 2;
+ break;
+ case VA_EGL_BUFFER_STRUCTURE_Y_U_V:
+ numPlanes = 3;
+ break;
+#endif
default:
CLog::Log(LOGERROR, "GLES: VAAPI - Unsupported VA/EGL surface format");
return;
@@ -1955,6 +2100,10 @@ bool CLinuxRendererGLES::CreateVAAPITexture(int index)
CLog::Log(LOGNOTICE, "GLES: VAAPI - Image size %dx%d", im.width, im.height);
+#ifdef HAVE_VA_EGL
+ if (!surface)
+ surface = VAAPI::CSurfaceEGLBuffer::Create(va.display, im.width, im.height);
+#endif
#ifdef HAVE_VA_X11
if (!surface)
surface = VAAPI::CSurfaceEGLPixmap::Create(va.display, im.width, im.height);
@@ -245,6 +245,7 @@ class CLinuxRendererGLES : public CBaseRenderer
typedef YUVPLANES YUVFIELDS[MAX_FIELDS];
void RenderRGB(const YUVPLANE& plane);
+ void RenderYUV(const YUVPLANES& planes, int numPlanes);
struct YUVBUFFER
{
@@ -277,6 +278,7 @@ class CLinuxRendererGLES : public CBaseRenderer
, unsigned width, unsigned height
, int stride, void* data );
+ ERenderFormat m_YUVShaderFormat;
Shaders::BaseYUV2RGBShader *m_pYUVShader;
Shaders::BaseVideoFilterShader *m_pVideoFilterShader;
ESCALINGMETHOD m_scalingMethod;
@@ -761,6 +761,99 @@ CSurfaceEGL *CSurfaceEGLPixmap::Create(CDisplayPtr& display, int width, int heig
return NULL;
}
#endif /* HAVE_VA_X11 */
+
+#ifdef HAVE_VA_EGL
+CSurfaceEGLBuffer::CSurfaceEGLBuffer(CDisplayPtr& display)
+ : CSurfaceEGL(display)
+ , m_surfaceBuffer(0)
+ , m_surfaceWidth(0)
+ , m_surfaceHeight(0)
+{
+}
+
+CSurfaceEGLBuffer::~CSurfaceEGLBuffer()
+{
+}
+
+bool CSurfaceEGLBuffer::Upload(CSurfacePtr surface, unsigned int flags)
+{
+ EGLint structure;
+ unsigned int i;
+
+ DestroyImages();
+ m_surface.reset();
+ m_surfaceBuffer = NULL;
+
+ if (surface->m_width != m_surfaceWidth ||
+ surface->m_height != m_surfaceHeight)
+ {
+ CLog::Log(LOGERROR, "VAAPI - EGL: failed to upload surface with size mismatch");
+ return false;
+ }
+
+ CHECK(vaGetSurfaceBufferEGL(surface->m_display->get(), surface->m_id,
+ &m_surfaceBuffer));
+
+ CHECK(vaGetBufferAttributeEGL(surface->m_display->get(), m_surfaceBuffer,
+ EGL_VA_BUFFER_STRUCTURE_INTEL, &structure));
+
+ switch (structure) {
+ case VA_EGL_BUFFER_STRUCTURE_RGBA:
+ m_numPlanes = 1;
+ break;
+ case VA_EGL_BUFFER_STRUCTURE_Y_UV:
+ m_numPlanes = 2;
+ break;
+ case VA_EGL_BUFFER_STRUCTURE_Y_U_V:
+ m_numPlanes = 3;
+ break;
+ default:
+ CLog::Log(LOGERROR, "VAAPI - EGL: unsupported buffer structure 0x%04x", structure);
+ return false;
+ };
+ m_format = structure;
+
+ const CEGLVTable * const egl_vtable = CEGLVTable::Get();
+ if (!egl_vtable)
+ return false;
+
+ EGLint attribs[3];
+ for (i = 0; i < m_numPlanes; i++)
+ {
+ attribs[0] = EGL_VA_BUFFER_PLANE_INTEL;
+ attribs[1] = i;
+ attribs[2] = EGL_NONE;
+ m_images[i] = egl_vtable->create_image(m_eglDisplay, EGL_NO_CONTEXT,
+ EGL_VA_PIXEL_BUFFER_INTEL, m_surfaceBuffer, attribs);
+ if (!m_images[i])
+ {
+ CLog::Log(LOGERROR, "VAAPI - EGL: failed to create EGLImage from VA surface plane %d", i);
+ return false;
+ }
+ }
+
+ m_surface = surface;
+ return true;
+}
+
+bool CSurfaceEGLBuffer::EnsureSize(int width, int height)
+{
+ m_surfaceWidth = width;
+ m_surfaceHeight = height;
+ return true;
+}
+
+CSurfaceEGL *CSurfaceEGLBuffer::Create(CDisplayPtr& display, int width, int height)
+{
+ CSurfaceEGL *surface;
+
+ surface = new CSurfaceEGLBuffer(display);
+ if (surface->EnsureSize(width, height))
+ return surface;
+ delete surface;
+ return NULL;
+}
+#endif /* HAVE_VA_EGL */
#endif /* HAS_EGL */
#endif
Oops, something went wrong.

2 comments on commit b59aa75

@theuni

This comment has been minimized.

Show comment Hide comment
@theuni

theuni Aug 19, 2012

Lots of dupe here. I'll have to look over again when it's not so late.

Lots of dupe here. I'll have to look over again when it's not so late.

@gbeauchesne

This comment has been minimized.

Show comment Hide comment
@gbeauchesne

gbeauchesne Aug 19, 2012

Owner

Yes, I know, that's why I created RenderRGB() and RenderYUV() functions. The hope is to let other renderers re-use those as well, e.g. RenderSinglePass(), the CoreVideo renderers, OMX as well probably, etc. In this case, I'd need to properly fill in the target .rect fields.

Owner

gbeauchesne replied Aug 19, 2012

Yes, I know, that's why I created RenderRGB() and RenderYUV() functions. The hope is to let other renderers re-use those as well, e.g. RenderSinglePass(), the CoreVideo renderers, OMX as well probably, etc. In this case, I'd need to properly fill in the target .rect fields.

Please sign in to comment.