From f5ff2656e0d192a2e25fe5f65edf219972211a48 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 11 Apr 2016 20:46:05 +0200 Subject: vaapi: determine surface format in decoder, not in renderer Until now, we have made the assumption that a driver will use only 1 hardware surface format. the format is dictated by the driver (you don't create surfaces with a specific format - you just pass a rt_format and get a surface that will be in a specific driver-chosen format). In particular, the renderer created a dummy surface to probe the format, and hoped the decoder would produce the same format. Due to a driver bug this required a workaround to actually get the same format as the driver did. Change this so that the format is determined in the decoder. The format is then passed down as hw_subfmt, which allows the renderer to configure itself with the correct format. If the hardware surface changes its format midstream, the renderer can be reconfigured using the normal mechanisms. This calls va_surface_init_subformat() each time after the decoder returns a surface. Since libavcodec/AVFrame has no concept of sub- formats, this is unavoidable. It creates and destroys a derived VAImage, but this shouldn't have any bad performance effects (at least I didn't notice any measurable effects). Note that vaDeriveImage() failures are silently ignored as some drivers (the vdpau wrapper) support neither vaDeriveImage, nor EGL interop. In addition, we still probe whether we can map an image in the EGL interop code. This is important as it's the only way to determine whether EGL interop is supported at all. With respect to the driver bug mentioned above, it doesn't matter which format the test surface has. In vf_vavpp, also remove the rt_format guessing business. I think the existing logic was a bit meaningless anyway. It's not even a given that vavpp produces the same rt_format for output. --- video/out/opengl/hwdec_vaegl.c | 50 +++++++++++------------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) (limited to 'video/out/opengl/hwdec_vaegl.c') diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c index 7b34d6bb5c..d62a20a219 100644 --- a/video/out/opengl/hwdec_vaegl.c +++ b/video/out/opengl/hwdec_vaegl.c @@ -172,30 +172,6 @@ static void destroy(struct gl_hwdec *hw) va_destroy(p->ctx); } -// Create an empty dummy VPP. This works around a weird bug that affects the -// VA surface format, as it is reported by vaDeriveImage(). Before a VPP -// context or a decoder context is created, the surface format will be reported -// as YV12. Surfaces created after context creation will report NV12 (even -// though surface creation does not take a context as argument!). Existing -// surfaces will change their format from YV12 to NV12 as soon as the decoder -// renders to them! Because we want know the surface format in advance (to -// simplify our renderer configuration logic), we hope that this hack gives -// us reasonable behavior. -// See: https://bugs.freedesktop.org/show_bug.cgi?id=79848 -static void insane_hack(struct gl_hwdec *hw) -{ - struct priv *p = hw->priv; - VAConfigID config; - if (vaCreateConfig(p->display, VAProfileNone, VAEntrypointVideoProc, - NULL, 0, &config) == VA_STATUS_SUCCESS) - { - // We want to keep this until the VADisplay is destroyed. It will - // implicitly free the context. - VAContextID context; - vaCreateContext(p->display, config, 0, 0, 0, NULL, 0, &context); - } -} - static int create(struct gl_hwdec *hw) { GL *gl = hw->gl; @@ -248,7 +224,6 @@ static int create(struct gl_hwdec *hw) MP_VERBOSE(p, "using VAAPI EGL interop\n"); - insane_hack(hw); if (!test_format(hw)) { destroy(hw); return -1; @@ -278,6 +253,18 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) } gl->BindTexture(GL_TEXTURE_2D, 0); + hw->converted_imgfmt = va_fourcc_to_imgfmt(params->hw_subfmt); + if (hw->converted_imgfmt != IMGFMT_NV12 && + hw->converted_imgfmt != IMGFMT_420P) + { + MP_FATAL(p, "unsupported VA image format %s\n", + mp_tag_str(params->hw_subfmt)); + return -1; + } + + MP_VERBOSE(p, "format: %s %s\n", mp_tag_str(params->hw_subfmt), + mp_imgfmt_to_name(hw->converted_imgfmt)); + return 0; } @@ -308,18 +295,6 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, goto err; int mpfmt = va_fourcc_to_imgfmt(va_image->format.fourcc); - if (mpfmt != IMGFMT_NV12 && mpfmt != IMGFMT_420P) { - MP_FATAL(p, "unsupported VA image format %s\n", - mp_tag_str(va_image->format.fourcc)); - goto err; - } - - if (!hw->converted_imgfmt) { - MP_VERBOSE(p, "format: %s %s\n", mp_tag_str(va_image->format.fourcc), - mp_imgfmt_to_name(mpfmt)); - hw->converted_imgfmt = mpfmt; - } - if (hw->converted_imgfmt != mpfmt) { MP_FATAL(p, "mid-stream hwdec format change (%s -> %s) not supported\n", mp_imgfmt_to_name(hw->converted_imgfmt), mp_imgfmt_to_name(mpfmt)); @@ -387,6 +362,7 @@ static bool test_format(struct gl_hwdec *hw) va_pool_set_allocator(alloc, p->ctx, VA_RT_FORMAT_YUV420); struct mp_image *surface = mp_image_pool_get(alloc, IMGFMT_VAAPI, 64, 64); if (surface) { + va_surface_init_subformat(surface); struct mp_image_params params = surface->params; if (reinit(hw, ¶ms) >= 0) { GLuint textures[4]; -- cgit v1.2.3 From f56555b5144069d0a8ac81d407abfa0c6be30c2e Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 5 May 2016 13:38:08 +0200 Subject: vo_opengl: EGL: fix hwdec probing If ANGLE was probed before (but rejected), the ANGLE API can remain "initialized", and eglGetCurrentDisplay() will return a non-NULL EGLDisplay. Then if a native GL context is used, the ANGLE/EGL API will then (apparently) keep working alongside native OpenGL API. Since GL objects are just numbers, they'll simply fail to interact, and OpenGL will get invalid textures. For some reason this will result in black textures. With VAAPI-EGL, something similar could happen in theory, but didn't in practice. --- video/out/opengl/hwdec_vaegl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'video/out/opengl/hwdec_vaegl.c') diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c index d62a20a219..6356ec4e8c 100644 --- a/video/out/opengl/hwdec_vaegl.c +++ b/video/out/opengl/hwdec_vaegl.c @@ -183,7 +183,7 @@ static int create(struct gl_hwdec *hw) if (hw->hwctx) return -1; - if (!eglGetCurrentDisplay()) + if (!eglGetCurrentContext()) return -1; const char *exts = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS); -- cgit v1.2.3 From 46fff8d31af0b79fe3de4aaee93bb66c248118a0 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 9 May 2016 19:42:03 +0200 Subject: video: refactor how VO exports hwdec device handles The main change is with video/hwdec.h. mp_hwdec_info is made opaque (and renamed to mp_hwdec_devices). Its accessors are mainly thread-safe (or documented where not), which makes the whole thing saner and cleaner. In particular, thread-safety rules become less subtle and more obvious. The new internal API makes it easier to support multiple OpenGL interop backends. (Although this is not done yet, and it's not clear whether it ever will.) This also removes all the API-specific fields from mp_hwdec_ctx and replaces them with a "ctx" field. For d3d in particular, we drop the mp_d3d_ctx struct completely, and pass the interfaces directly. Remove the emulation checks from vaapi.c and vdpau.c; they are pointless, and the checks that matter are done on the VO layer. The d3d hardware decoders might slightly change behavior: dxva2-copy will not use the VO device anymore if the VO supports proper interop. This pretty much assumes that any in such cases the VO will not use any form of exclusive mode, which makes using the VO device in copy mode unnecessary. This is a big refactor. Some things may be untested and could be broken. --- video/out/opengl/hwdec_vaegl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'video/out/opengl/hwdec_vaegl.c') diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c index 6356ec4e8c..84e6e83a73 100644 --- a/video/out/opengl/hwdec_vaegl.c +++ b/video/out/opengl/hwdec_vaegl.c @@ -169,6 +169,8 @@ static void destroy(struct gl_hwdec *hw) struct priv *p = hw->priv; unref_image(hw); destroy_textures(hw); + if (p->ctx) + hwdec_devices_remove(hw->devs, &p->ctx->hwctx); va_destroy(p->ctx); } @@ -181,8 +183,6 @@ static int create(struct gl_hwdec *hw) p->current_image.buf = p->current_image.image_id = VA_INVALID_ID; p->log = hw->log; - if (hw->hwctx) - return -1; if (!eglGetCurrentContext()) return -1; @@ -229,7 +229,8 @@ static int create(struct gl_hwdec *hw) return -1; } - hw->hwctx = &p->ctx->hwctx; + p->ctx->hwctx.driver_name = hw->driver->name; + hwdec_devices_add(hw->devs, &p->ctx->hwctx); return 0; } -- cgit v1.2.3 From b0b01aa2509778bb5191892b894e17e657152b2e Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 10 May 2016 18:29:10 +0200 Subject: vo_opengl: refactor how hwdec interop exports textures Rename gl_hwdec_driver.map_image to map_frame, and let it fill out a struct gl_hwdec_frame describing the exact texture layout. This gives more flexibility to what the hwdec interop can export. In particular, it can export strange component orders/permutations and textures with padded size. (The latter originating from cropped video.) The way gl_hwdec_frame works is in the spirit of the rest of the vo_opengl video processing code, which tends to put as much information in immediate state (as part of the dataflow), instead of declaring it globally. To some degree this duplicates the texplane and img_tex structs, but until we somehow unify those, it's better to give the hwdec state its own struct. The fact that changing the hwdec struct would require changes and testing on at least 4 platform/GPU combinations makes duplicating it almost a requirement to avoid pain later. Make gl_hwdec_driver.reinit set the new image format and remove the gl_hwdec.converted_imgfmt field. Likewise, gl_hwdec.gl_texture_target is replaced with gl_hwdec_plane.gl_target. Split out a init_image_desc function from init_format. The latter is not called in the hwdec case at all anymore. Setting up most of struct texplane is also completely separate in the hwdec and normal cases. video.c does not check whether the hwdec "mapped" image format is supported. This should not really happen anyway, and if it does, the hwdec interop backend must fail at creation time, so this is not an issue. --- video/out/opengl/hwdec_vaegl.c | 52 ++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 25 deletions(-) (limited to 'video/out/opengl/hwdec_vaegl.c') diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c index 84e6e83a73..6c52cdde11 100644 --- a/video/out/opengl/hwdec_vaegl.c +++ b/video/out/opengl/hwdec_vaegl.c @@ -114,7 +114,7 @@ struct priv { EGLImageKHR images[4]; VAImage current_image; bool buffer_acquired; - struct mp_image *current_ref; + int current_mpfmt; EGLImageKHR (EGLAPIENTRY *CreateImageKHR)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, @@ -125,7 +125,7 @@ struct priv { static bool test_format(struct gl_hwdec *hw); -static void unref_image(struct gl_hwdec *hw) +static void unmap_frame(struct gl_hwdec *hw) { struct priv *p = hw->priv; VAStatus status; @@ -149,8 +149,6 @@ static void unref_image(struct gl_hwdec *hw) p->current_image.image_id = VA_INVALID_ID; } - mp_image_unrefp(&p->current_ref); - va_unlock(p->ctx); } @@ -167,7 +165,7 @@ static void destroy_textures(struct gl_hwdec *hw) static void destroy(struct gl_hwdec *hw) { struct priv *p = hw->priv; - unref_image(hw); + unmap_frame(hw); destroy_textures(hw); if (p->ctx) hwdec_devices_remove(hw->devs, &p->ctx->hwctx); @@ -242,8 +240,6 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) // 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]); @@ -254,9 +250,9 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) } gl->BindTexture(GL_TEXTURE_2D, 0); - hw->converted_imgfmt = va_fourcc_to_imgfmt(params->hw_subfmt); - if (hw->converted_imgfmt != IMGFMT_NV12 && - hw->converted_imgfmt != IMGFMT_420P) + p->current_mpfmt = va_fourcc_to_imgfmt(params->hw_subfmt); + if (p->current_mpfmt != IMGFMT_NV12 && + p->current_mpfmt != IMGFMT_420P) { MP_FATAL(p, "unsupported VA image format %s\n", mp_tag_str(params->hw_subfmt)); @@ -264,7 +260,9 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) } MP_VERBOSE(p, "format: %s %s\n", mp_tag_str(params->hw_subfmt), - mp_imgfmt_to_name(hw->converted_imgfmt)); + mp_imgfmt_to_name(p->current_mpfmt)); + + params->imgfmt = p->current_mpfmt; return 0; } @@ -277,17 +275,15 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) attribs[num_attribs] = EGL_NONE; \ } while(0) -static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, - GLuint *out_textures) +static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image, + struct gl_hwdec_frame *out_frame) { 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); + unmap_frame(hw); va_lock(p->ctx); @@ -296,9 +292,9 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, goto err; int mpfmt = va_fourcc_to_imgfmt(va_image->format.fourcc); - if (hw->converted_imgfmt != mpfmt) { + if (p->current_mpfmt != mpfmt) { MP_FATAL(p, "mid-stream hwdec format change (%s -> %s) not supported\n", - mp_imgfmt_to_name(hw->converted_imgfmt), mp_imgfmt_to_name(mpfmt)); + mp_imgfmt_to_name(p->current_mpfmt), mp_imgfmt_to_name(mpfmt)); goto err; } @@ -337,12 +333,17 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]); p->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]); - out_textures[n] = p->gl_textures[n]; + out_frame->planes[n] = (struct gl_hwdec_plane){ + .gl_texture = p->gl_textures[n], + .gl_target = GL_TEXTURE_2D, + .tex_w = mp_image_plane_w(&layout, n), + .tex_h = mp_image_plane_h(&layout, n), + }; } gl->BindTexture(GL_TEXTURE_2D, 0); if (va_image->format.fourcc == VA_FOURCC_YV12) - MPSWAP(GLuint, out_textures[1], out_textures[2]); + MPSWAP(struct gl_hwdec_plane, out_frame->planes[1], out_frame->planes[2]); va_unlock(p->ctx); return 0; @@ -350,7 +351,7 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, err: va_unlock(p->ctx); MP_FATAL(p, "mapping VAAPI EGL image failed\n"); - unref_image(hw); + unmap_frame(hw); return -1; } @@ -366,10 +367,10 @@ static bool test_format(struct gl_hwdec *hw) va_surface_init_subformat(surface); struct mp_image_params params = surface->params; if (reinit(hw, ¶ms) >= 0) { - GLuint textures[4]; - ok = map_image(hw, surface, textures) >= 0; + struct gl_hwdec_frame frame = {0}; + ok = map_frame(hw, surface, &frame) >= 0; } - unref_image(hw); + unmap_frame(hw); } talloc_free(surface); talloc_free(alloc); @@ -383,6 +384,7 @@ const struct gl_hwdec_driver gl_hwdec_vaegl = { .imgfmt = IMGFMT_VAAPI, .create = create, .reinit = reinit, - .map_image = map_image, + .map_frame = map_frame, + .unmap = unmap_frame, .destroy = destroy, }; -- cgit v1.2.3