From 8d8a2045bde250a50228df7fa8f24124ec5b0251 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 25 Sep 2015 00:20:57 +0200 Subject: vo_opengl: support new VAAPI EGL interop Should work much better than the old GLX interop code. Requires Mesa 11, and explicitly selecting the X11 EGL backend with: --vo=opengl:backend=x11egl Should it turn out that the new interop works well, we will try to autodetect EGL by default. This code still uses some bad assumptions, like expecting surfaces to be in NV12. (This is probably ok, because virtually all HW will use this format. But we should at least check this on init or so, instead of failing to render an image if our assumption doesn't hold up.) This repo was a lot of help: https://github.com/gbeauchesne/ffvademo The kodi code was also helpful (the magic FourCC it uses for EGL_LINUX_DRM_FOURCC_EXT are nowhere documented, and EGL_IMAGE_INTERNAL_FORMAT_EXT as used in ffvademo does not actually exist). (This is the 3rd VAAPI GL interop that was implemented in this player.) --- video/out/opengl/hwdec.c | 4 + video/out/opengl/hwdec_vaegl.c | 247 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 video/out/opengl/hwdec_vaegl.c (limited to 'video') diff --git a/video/out/opengl/hwdec.c b/video/out/opengl/hwdec.c index ee602fa191..d93dc99397 100644 --- a/video/out/opengl/hwdec.c +++ b/video/out/opengl/hwdec.c @@ -29,6 +29,7 @@ #include "common/msg.h" #include "hwdec.h" +extern const struct gl_hwdec_driver gl_hwdec_vaegl; extern const struct gl_hwdec_driver gl_hwdec_vaglx; extern const struct gl_hwdec_driver gl_hwdec_vda; extern const struct gl_hwdec_driver gl_hwdec_videotoolbox; @@ -36,6 +37,9 @@ extern const struct gl_hwdec_driver gl_hwdec_vdpau; extern const struct gl_hwdec_driver gl_hwdec_dxva2; static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = { +#if HAVE_VAAPI_X_EGL + &gl_hwdec_vaegl, +#endif #if HAVE_VAAPI_GLX &gl_hwdec_vaglx, #endif diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c new file mode 100644 index 0000000000..b084d7203d --- /dev/null +++ b/video/out/opengl/hwdec_vaegl.c @@ -0,0 +1,247 @@ +/* + * This file is part of mpv. + * + * Parts based on the MPlayer VA-API patch (see vo_vaapi.c). + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#include +#include +#include + +#include + +#include + +#include "video/out/x11_common.h" +#include "hwdec.h" +#include "video/vaapi.h" +#include "video/img_fourcc.h" +#include "common.h" + +struct priv { + struct mp_log *log; + struct mp_vaapi_ctx *ctx; + VADisplay *display; + Display *xdisplay; + GLuint gl_textures[4]; + EGLImageKHR images[4]; + VAImage current_image; + struct mp_image *current_ref; +}; + +static void unref_image(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + VAStatus status; + + for (int n = 0; n < 4; n++) { + if (p->images[n]) + hw->gl->DestroyImageKHR(eglGetCurrentDisplay(), p->images[n]); + } + + va_lock(p->ctx); + + if (p->current_image.buf != VA_INVALID_ID) { + status = vaReleaseBufferHandle(p->display, p->current_image.buf); + CHECK_VA_STATUS(p, "vaReleaseBufferHandle()"); + p->current_image.buf = VA_INVALID_ID; + } + if (p->current_image.image_id != VA_INVALID_ID) { + status = vaDestroyImage(p->display, p->current_image.image_id); + CHECK_VA_STATUS(p, "vaDestroyImage()"); + p->current_image.image_id = VA_INVALID_ID; + } + + mp_image_unrefp(&p->current_ref); + + va_unlock(p->ctx); +} + +static void destroy_textures(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + GL *gl = hw->gl; + + gl->DeleteTextures(4, p->gl_textures); + for (int n = 0; n < 4; n++) + p->gl_textures[n] = 0; +} + +static void destroy(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + destroy_textures(hw); + va_destroy(p->ctx); +} + +static int create(struct gl_hwdec *hw) +{ + GL *gl = hw->gl; + + if (hw->hwctx) + return -1; + if (!eglGetCurrentDisplay()) + return -1; + + Display *x11disp = + hw->gl->MPGetNativeDisplay ? hw->gl->MPGetNativeDisplay("x11") : NULL; + if (!x11disp) + return -1; + if (!gl->CreateImageKHR || !gl->EGLImageTargetTexture2DOES || + !strstr(gl->extensions, "EXT_image_dma_buf_import") || + !(gl->mpgl_caps & MPGL_CAP_TEX_RG)) + return -1; + + struct priv *p = talloc_zero(hw, struct priv); + hw->priv = p; + for (int n = 0; n < 4; n++) + p->current_image.buf = p->current_image.image_id = VA_INVALID_ID; + p->log = hw->log; + p->xdisplay = x11disp; + p->display = vaGetDisplay(x11disp); + if (!p->display) + return -1; + + p->ctx = va_initialize(p->display, p->log, true); + if (!p->ctx) { + vaTerminate(p->display); + return -1; + } + + if (hw->reject_emulated && va_guess_if_emulated(p->ctx)) { + destroy(hw); + return -1; + } + + MP_VERBOSE(p, "using VAAPI EGL interop\n"); + + hw->hwctx = &p->ctx->hwctx; + hw->converted_imgfmt = IMGFMT_NV12; + return 0; +} + +static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) +{ + struct priv *p = hw->priv; + GL *gl = hw->gl; + + // Recreate them to get rid of all previous image data (possibly). + destroy_textures(hw); + + assert(params->imgfmt == hw->driver->imgfmt); + + gl->GenTextures(4, p->gl_textures); + for (int n = 0; n < 4; n++) { + gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + gl->BindTexture(GL_TEXTURE_2D, 0); + + return 0; +} + +#define ADD_ATTRIB(name, value) \ + do { \ + assert(num_attribs + 3 < MP_ARRAY_SIZE(attribs)); \ + attribs[num_attribs++] = (name); \ + attribs[num_attribs++] = (value); \ + attribs[num_attribs] = EGL_NONE; \ + } while(0) + +static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, + GLuint *out_textures) +{ + struct priv *p = hw->priv; + GL *gl = hw->gl; + VAStatus status; + VAImage *va_image = &p->current_image; + + unref_image(hw); + + mp_image_setrefp(&p->current_ref, hw_image); + + va_lock(p->ctx); + + status = vaDeriveImage(p->display, va_surface_id(hw_image), va_image); + if (!CHECK_VA_STATUS(p, "vaDeriveImage()")) + goto err; + + int mpfmt = va_fourcc_to_imgfmt(va_image->format.fourcc); + if (mpfmt != IMGFMT_NV12) { + MP_FATAL(p, "unsupported VA image format %s\n", + VA_STR_FOURCC(va_image->format.fourcc)); + goto err; + } + + VABufferInfo buffer_info = {.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME}; + status = vaAcquireBufferHandle(p->display, va_image->buf, &buffer_info); + if (!CHECK_VA_STATUS(p, "vaAcquireBufferHandle()")) + goto err; + + int num_planes = 2; + int plane_sizes[2] = {1, 2}; + int plane_xs[2] = {0, 1}; + int plane_ys[2] = {0, 1}; + + // (it would be nice if we could use EGL_IMAGE_INTERNAL_FORMAT_EXT) + int drm_fmts[4] = {MP_FOURCC('R', '8', ' ', ' '), + MP_FOURCC('G', 'R', '8', '8'), + 0, 0}; + + for (int n = 0; n < num_planes; n++) { + int attribs[20]; + int num_attribs = 0; + + ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, drm_fmts[plane_sizes[n] - 1]); + ADD_ATTRIB(EGL_WIDTH, mp_chroma_div_up(hw_image->w, plane_xs[n])); + ADD_ATTRIB(EGL_HEIGHT, mp_chroma_div_up(hw_image->h, plane_ys[n])); + ADD_ATTRIB(EGL_DMA_BUF_PLANE0_FD_EXT, buffer_info.handle); + ADD_ATTRIB(EGL_DMA_BUF_PLANE0_OFFSET_EXT, va_image->offsets[n]); + ADD_ATTRIB(EGL_DMA_BUF_PLANE0_PITCH_EXT, va_image->pitches[n]); + + p->images[n] = hw->gl->CreateImageKHR(eglGetCurrentDisplay(), + EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); + if (!p->images[n]) + goto err; + + gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]); + gl->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]); + + out_textures[n] = p->gl_textures[n]; + } + gl->BindTexture(GL_TEXTURE_2D, 0); + + va_unlock(p->ctx); + return 0; + +err: + va_unlock(p->ctx); + MP_FATAL(p, "mapping VAAPI EGL image failed\n"); + unref_image(hw); + return -1; +} + +const struct gl_hwdec_driver gl_hwdec_vaegl = { + .api_name = "vaapi", + .imgfmt = IMGFMT_VAAPI, + .create = create, + .reinit = reinit, + .map_image = map_image, + .destroy = destroy, +}; -- cgit v1.2.3