summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
authorMartin Herkt <lachs0r@srsfckn.biz>2016-04-11 17:42:55 +0200
committerMartin Herkt <lachs0r@srsfckn.biz>2016-04-11 17:42:55 +0200
commit0803f4ad21c195519209bae8d18840dd810191f8 (patch)
treef9a869011ba90c106cf5c05c3e346912a669f63a /video/out
parent9d2980dab752280468620df49cabe7f4843f0551 (diff)
parentb968d779afb9114694976792e903b0591a71a816 (diff)
downloadmpv-0803f4ad21c195519209bae8d18840dd810191f8.tar.bz2
mpv-0803f4ad21c195519209bae8d18840dd810191f8.tar.xz
Merge branch 'master' into release/current
Diffstat (limited to 'video/out')
-rw-r--r--video/out/aspect.c47
-rw-r--r--video/out/opengl/common.c4
-rw-r--r--video/out/opengl/common.h1
-rw-r--r--video/out/opengl/context.c11
-rw-r--r--video/out/opengl/context.h2
-rw-r--r--video/out/opengl/context_cocoa.c1
-rw-r--r--video/out/opengl/context_dxinterop.c19
-rw-r--r--video/out/opengl/context_wayland.c3
-rw-r--r--video/out/opengl/context_x11.c20
-rw-r--r--video/out/opengl/hwdec.c4
-rw-r--r--video/out/opengl/hwdec.h1
-rw-r--r--video/out/opengl/hwdec_dxva2egl.c344
-rw-r--r--video/out/opengl/hwdec_dxva2gldx.c22
-rw-r--r--video/out/opengl/hwdec_osx.c37
-rw-r--r--video/out/opengl/lcms.c134
-rw-r--r--video/out/opengl/lcms.h6
-rw-r--r--video/out/opengl/nnedi3.c10
-rw-r--r--video/out/opengl/nnedi3.h2
-rw-r--r--video/out/opengl/osd.c32
-rw-r--r--video/out/opengl/osd.h1
-rw-r--r--video/out/opengl/superxbr.c277
-rw-r--r--video/out/opengl/superxbr.h4
-rw-r--r--video/out/opengl/utils.c222
-rw-r--r--video/out/opengl/utils.h27
-rw-r--r--video/out/opengl/video.c1109
-rw-r--r--video/out/opengl/video.h18
-rw-r--r--video/out/opengl/video_shaders.c12
-rw-r--r--video/out/opengl/video_shaders.h4
-rw-r--r--video/out/vo.c23
-rw-r--r--video/out/vo_opengl.c24
-rw-r--r--video/out/vo_opengl_cb.c2
-rw-r--r--video/out/vo_x11.c2
-rw-r--r--video/out/vo_xv.c3
-rw-r--r--video/out/wayland_common.c11
-rw-r--r--video/out/x11_common.c91
-rw-r--r--video/out/x11_common.h1
36 files changed, 1635 insertions, 896 deletions
diff --git a/video/out/aspect.c b/video/out/aspect.c
index 2d25bbdfd5..205ee3b89f 100644
--- a/video/out/aspect.c
+++ b/video/out/aspect.c
@@ -17,6 +17,7 @@
/* Stuff for correct aspect scaling. */
#include "aspect.h"
+#include "math.h"
#include "vo.h"
#include "common/msg.h"
#include "options/options.h"
@@ -25,38 +26,36 @@
#include "vo.h"
#include "sub/osd.h"
-static void aspect_calc_panscan(struct mp_log *log, struct mp_vo_opts *opts,
- int w, int h, int d_w, int d_h,
+static void aspect_calc_panscan(struct mp_vo_opts *opts,
+ int w, int h, int d_w, int d_h, bool unscaled,
int window_w, int window_h, double monitor_par,
int *out_w, int *out_h)
{
- mp_dbg(log, "aspect(0) fitin: %dx%d monitor_par: %.2f\n",
- window_w, window_h, monitor_par);
int fwidth = window_w;
int fheight = (float)window_w / d_w * d_h / monitor_par;
- mp_dbg(log, "aspect(1) wh: %dx%d (org: %dx%d)\n",
- fwidth, fheight, d_w, d_h);
if (fheight > window_h || fheight < h) {
int tmpw = (float)window_h / d_h * d_w * monitor_par;
if (tmpw <= window_w) {
fheight = window_h;
fwidth = tmpw;
- } else if (fheight > window_h) {
- mp_warn(log, "No suitable new aspect found!\n");
}
}
- mp_dbg(log, "aspect(2) wh: %dx%d (org: %dx%d)\n",
- fwidth, fheight, d_w, d_h);
int vo_panscan_area = window_h - fheight;
double f_w = fwidth / (double)fheight;
double f_h = 1;
- if (!vo_panscan_area) {
+ if (vo_panscan_area == 0) {
vo_panscan_area = window_w - fwidth;
f_w = 1;
f_h = fheight / (double)fwidth;
}
+ if (unscaled) {
+ fwidth = w * monitor_par;
+ fheight = h;
+ vo_panscan_area = 0;
+ }
+
*out_w = fwidth + vo_panscan_area * opts->panscan * f_w;
*out_h = fheight + vo_panscan_area * opts->panscan * f_h;
}
@@ -73,18 +72,13 @@ static void clamp_size(int size, int *start, int *end)
}
static void src_dst_split_scaling(int src_size, int dst_size,
- int scaled_src_size, bool unscaled,
+ int scaled_src_size,
float zoom, float align, float pan,
int *src_start, int *src_end,
int *dst_start, int *dst_end,
int *osd_margin_a, int *osd_margin_b)
{
- if (unscaled) {
- scaled_src_size = src_size;
- zoom = 0.0;
- }
-
- scaled_src_size += zoom * scaled_src_size;
+ scaled_src_size *= powf(2, zoom);
align = (align + 1) / 2;
*src_start = 0;
@@ -110,17 +104,6 @@ static void src_dst_split_scaling(int src_size, int dst_size,
*dst_end = dst_size;
}
- if (unscaled) {
- // Force unscaled by reducing the range for src or dst
- int src_s = *src_end - *src_start;
- int dst_s = *dst_end - *dst_start;
- if (src_s > dst_s) {
- *src_end = *src_start + dst_s;
- } else if (src_s < dst_s) {
- *dst_end = *dst_start + src_s;
- }
- }
-
// For sanity: avoid bothering VOs with corner cases
clamp_size(src_size, src_start, src_end);
clamp_size(dst_size, dst_start, dst_end);
@@ -152,14 +135,14 @@ void mp_get_src_dst_rects(struct mp_log *log, struct mp_vo_opts *opts,
};
if (opts->keepaspect) {
int scaled_width, scaled_height;
- aspect_calc_panscan(log, opts, src_w, src_h, src_dw, src_dh,
+ aspect_calc_panscan(opts, src_w, src_h, src_dw, src_dh, opts->unscaled,
window_w, window_h, monitor_par,
&scaled_width, &scaled_height);
- src_dst_split_scaling(src_w, window_w, scaled_width, opts->unscaled,
+ src_dst_split_scaling(src_w, window_w, scaled_width,
opts->zoom, opts->align_x, opts->pan_x,
&src.x0, &src.x1, &dst.x0, &dst.x1,
&osd.ml, &osd.mr);
- src_dst_split_scaling(src_h, window_h, scaled_height, opts->unscaled,
+ src_dst_split_scaling(src_h, window_h, scaled_height,
opts->zoom, opts->align_y, opts->pan_y,
&src.y0, &src.y1, &dst.y0, &dst.y1,
&osd.mt, &osd.mb);
diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c
index f08fd4bfd9..46cbc2fc8c 100644
--- a/video/out/opengl/common.c
+++ b/video/out/opengl/common.c
@@ -369,8 +369,10 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
int major = 0, minor = 0;
const char *version_string = gl->GetString(GL_VERSION);
- if (!version_string)
+ if (!version_string) {
+ mp_fatal(log, "glGetString(GL_VERSION) returned NULL.\n");
goto error;
+ }
mp_verbose(log, "GL_VERSION='%s'\n", version_string);
if (strncmp(version_string, "OpenGL ES ", 10) == 0) {
version_string += 10;
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index 9236ce1d6f..f790dcb166 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -89,7 +89,6 @@ struct GL {
int mpgl_caps; // Bitfield of MPGL_CAP_* constants
bool debug_context; // use of e.g. GLX_CONTEXT_DEBUG_BIT_ARB
int fb_r, fb_g, fb_b; // frame buffer bit depth (0 if unknown)
- bool fb_premultiplied; // assumption about FB alpha compositor usage
void (GLAPIENTRY *Viewport)(GLint, GLint, GLsizei, GLsizei);
void (GLAPIENTRY *Clear)(GLbitfield);
diff --git a/video/out/opengl/context.c b/video/out/opengl/context.c
index aada9df973..77e9709426 100644
--- a/video/out/opengl/context.c
+++ b/video/out/opengl/context.c
@@ -78,6 +78,17 @@ static const struct mpgl_driver *const backends[] = {
#endif
};
+// 0-terminated list of desktop GL versions a backend should try to
+// initialize. The first entry is the most preferred version.
+const int mpgl_preferred_gl_versions[] = {
+ 330,
+ 320,
+ 310,
+ 300,
+ 210,
+ 0
+};
+
int mpgl_find_backend(const char *name)
{
if (name == NULL || strcmp(name, "auto") == 0)
diff --git a/video/out/opengl/context.h b/video/out/opengl/context.h
index cd12bb9f32..df842bc8a1 100644
--- a/video/out/opengl/context.h
+++ b/video/out/opengl/context.h
@@ -34,6 +34,8 @@ enum {
VOFLAG_SW = 1 << 4, // Hint to accept a software GL renderer
};
+extern const int mpgl_preferred_gl_versions[];
+
struct MPGLContext;
// A windowing backend (like X11, win32, ...), which provides OpenGL rendering.
diff --git a/video/out/opengl/context_cocoa.c b/video/out/opengl/context_cocoa.c
index e48472ff46..271bdb7444 100644
--- a/video/out/opengl/context_cocoa.c
+++ b/video/out/opengl/context_cocoa.c
@@ -124,7 +124,6 @@ static bool create_gl_context(struct MPGLContext *ctx, int vo_flags)
mpgl_load_functions(ctx->gl, (void *)cocoa_glgetaddr, NULL, ctx->vo->log);
ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = cgl_color_size(ctx);
- ctx->gl->fb_premultiplied = true;
CGLReleasePixelFormat(p->pix);
diff --git a/video/out/opengl/context_dxinterop.c b/video/out/opengl/context_dxinterop.c
index d3287f1f28..4dfc3c2108 100644
--- a/video/out/opengl/context_dxinterop.c
+++ b/video/out/opengl/context_dxinterop.c
@@ -543,6 +543,20 @@ static int GLAPIENTRY dxinterop_swap_interval(int interval)
return 1;
}
+static void * GLAPIENTRY dxinterop_get_native_display(const char *name)
+{
+ if (!current_ctx || !name)
+ return NULL;
+ struct priv *p = current_ctx->priv;
+
+ if (p->device && strcmp("IDirect3DDevice9Ex", name) == 0) {
+ return p->device;
+ } else if (p->device_h && strcmp("dxinterop_device_HANDLE", name) == 0) {
+ return p->device_h;
+ }
+ return NULL;
+}
+
static int dxinterop_init(struct MPGLContext *ctx, int flags)
{
struct priv *p = ctx->priv;
@@ -566,6 +580,8 @@ static int dxinterop_init(struct MPGLContext *ctx, int flags)
gl->SwapInterval = dxinterop_swap_interval;
+ gl->MPGetNativeDisplay = dxinterop_get_native_display;
+
if (d3d_create(ctx) < 0)
goto fail;
if (d3d_size_dependent_create(ctx) < 0)
@@ -581,9 +597,6 @@ static int dxinterop_init(struct MPGLContext *ctx, int flags)
DwmEnableMMCSS(TRUE);
- ctx->native_display_type = "IDirect3DDevice9Ex";
- ctx->native_display = p->device;
-
return 0;
fail:
dxinterop_uninit(ctx);
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 63a14539fe..a100073780 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -183,7 +183,8 @@ static void waylandgl_uninit(MPGLContext *ctx)
if (wl->egl_context.egl.ctx) {
eglReleaseThread();
- wl_egl_window_destroy(wl->egl_context.egl_window);
+ if (wl->egl_context.egl_window)
+ wl_egl_window_destroy(wl->egl_context.egl_window);
eglDestroySurface(wl->egl_context.egl.dpy, wl->egl_context.egl_surface);
eglMakeCurrent(wl->egl_context.egl.dpy, NULL, NULL, EGL_NO_CONTEXT);
eglDestroyContext(wl->egl_context.egl.dpy, wl->egl_context.egl.ctx);
diff --git a/video/out/opengl/context_x11.c b/video/out/opengl/context_x11.c
index 6a6441cd23..d9a584e87c 100644
--- a/video/out/opengl/context_x11.c
+++ b/video/out/opengl/context_x11.c
@@ -125,10 +125,8 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
glx_ctx->fbc, 0, True,
context_attribs);
vo_x11_silence_xlib(-1);
- if (!context) {
- MP_VERBOSE(vo, "Could not create GL3 context. Retrying with legacy context.\n");
+ if (!context)
return false;
- }
// set context
if (!glXMakeCurrent(vo->x11->display, vo->x11->window, context)) {
@@ -253,9 +251,18 @@ static int glx_init(struct MPGLContext *ctx, int flags)
bool success = false;
if (!(flags & VOFLAG_GLES)) {
- success = create_context_x11_gl3(ctx, flags, 300, false);
- if (!success)
- success = create_context_x11_old(ctx);
+ for (int n = 0; mpgl_preferred_gl_versions[n]; n++) {
+ int version = mpgl_preferred_gl_versions[n];
+ MP_VERBOSE(vo, "Creating OpenGL %d.%d context...\n",
+ MPGL_VER_P(version));
+ if (version >= 300) {
+ success = create_context_x11_gl3(ctx, flags, version, false);
+ } else {
+ success = create_context_x11_old(ctx);
+ }
+ if (success)
+ break;
+ }
}
if (!success) // try ES
success = create_context_x11_gl3(ctx, flags, 200, true);
@@ -267,7 +274,6 @@ static int glx_init(struct MPGLContext *ctx, int flags)
glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_RED_SIZE, &ctx->gl->fb_r);
glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_GREEN_SIZE, &ctx->gl->fb_g);
glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_BLUE_SIZE, &ctx->gl->fb_b);
- ctx->gl->fb_premultiplied = true;
return 0;
diff --git a/video/out/opengl/hwdec.c b/video/out/opengl/hwdec.c
index 300197ac00..b58af9bae9 100644
--- a/video/out/opengl/hwdec.c
+++ b/video/out/opengl/hwdec.c
@@ -28,6 +28,7 @@ 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_videotoolbox;
extern const struct gl_hwdec_driver gl_hwdec_vdpau;
+extern const struct gl_hwdec_driver gl_hwdec_dxva2egl;
extern const struct gl_hwdec_driver gl_hwdec_dxva2gldx;
extern const struct gl_hwdec_driver gl_hwdec_dxva2;
@@ -45,6 +46,9 @@ static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = {
&gl_hwdec_videotoolbox,
#endif
#if HAVE_DXVA2_HWACCEL
+#if HAVE_EGL_ANGLE
+ &gl_hwdec_dxva2egl,
+#endif
#if HAVE_GL_DXINTEROP
&gl_hwdec_dxva2gldx,
#endif
diff --git a/video/out/opengl/hwdec.h b/video/out/opengl/hwdec.h
index c04962dd76..5126d7f0fa 100644
--- a/video/out/opengl/hwdec.h
+++ b/video/out/opengl/hwdec.h
@@ -39,6 +39,7 @@ struct gl_hwdec_driver {
// Prepare for rendering video. (E.g. create textures.)
// Called on initialization, and every time the video size changes.
// *params must be set to the format the hw textures return.
+ // This also can update hw->converted_imgfmt.
int (*reinit)(struct gl_hwdec *hw, struct mp_image_params *params);
// Return textures that contain a copy or reference of the given hw_image.
int (*map_image)(struct gl_hwdec *hw, struct mp_image *hw_image,
diff --git a/video/out/opengl/hwdec_dxva2egl.c b/video/out/opengl/hwdec_dxva2egl.c
new file mode 100644
index 0000000000..eed942618c
--- /dev/null
+++ b/video/out/opengl/hwdec_dxva2egl.c
@@ -0,0 +1,344 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv 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.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <windows.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "common/common.h"
+#include "osdep/timer.h"
+#include "osdep/windows_utils.h"
+#include "hwdec.h"
+#include "video/dxva2.h"
+#include "video/d3d.h"
+#include "video/hwdec.h"
+
+struct priv {
+ struct mp_d3d_ctx ctx;
+
+ HMODULE d3d9_dll;
+ IDirect3D9Ex *d3d9ex;
+ IDirect3DDevice9Ex *device9ex;
+ IDirect3DQuery9 *query9;
+ IDirect3DTexture9 *texture9;
+ IDirect3DSurface9 *surface9;
+
+ EGLDisplay egl_display;
+ EGLConfig egl_config;
+ EGLint alpha;
+ EGLSurface egl_surface;
+
+ GLuint gl_texture;
+};
+
+static void destroy_textures(struct gl_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+ GL *gl = hw->gl;
+
+ gl->DeleteTextures(1, &p->gl_texture);
+ p->gl_texture = 0;
+
+ if (p->egl_display && p->egl_surface) {
+ eglReleaseTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER);
+ eglDestroySurface(p->egl_display, p->egl_surface);
+ p->egl_surface = NULL;
+ }
+
+ if (p->surface9) {
+ IDirect3DSurface9_Release(p->surface9);
+ p->surface9 = NULL;
+ }
+
+ if (p->texture9) {
+ IDirect3DTexture9_Release(p->texture9);
+ p->texture9 = NULL;
+ }
+}
+
+static void destroy(struct gl_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+
+ destroy_textures(hw);
+
+ if (p->query9)
+ IDirect3DQuery9_Release(p->query9);
+
+ if (p->device9ex)
+ IDirect3DDevice9Ex_Release(p->device9ex);
+
+ if (p->d3d9ex)
+ IDirect3D9Ex_Release(p->d3d9ex);
+
+ if (p->d3d9_dll)
+ FreeLibrary(p->d3d9_dll);
+}
+
+static int create(struct gl_hwdec *hw)
+{
+ if (hw->hwctx)
+ return -1;
+
+ EGLDisplay egl_display = eglGetCurrentDisplay();
+ if (!egl_display)
+ return -1;
+
+ const char *exts = eglQueryString(egl_display, EGL_EXTENSIONS);
+ if (!exts ||
+ !strstr(exts, "EGL_ANGLE_d3d_share_handle_client_buffer")) {
+ return -1;
+ }
+
+ HRESULT hr;
+ struct priv *p = talloc_zero(hw, struct priv);
+ hw->priv = p;
+
+ p->egl_display = egl_display;
+
+ p->d3d9_dll = LoadLibraryW(L"d3d9.dll");
+ if (!p->d3d9_dll) {
+ MP_FATAL(hw, "Failed to load \"d3d9.dll\": %s\n",
+ mp_LastError_to_str());
+ goto fail;
+ }
+
+ HRESULT (WINAPI *Direct3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D);
+ Direct3DCreate9Ex = (void *)GetProcAddress(p->d3d9_dll, "Direct3DCreate9Ex");
+ if (!Direct3DCreate9Ex) {
+ MP_FATAL(hw, "Direct3D 9Ex not supported\n");
+ goto fail;
+ }
+
+ hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &p->d3d9ex);
+ if (FAILED(hr)) {
+ MP_FATAL(hw, "Couldn't create Direct3D9Ex: %s\n",
+ mp_HRESULT_to_str(hr));
+ goto fail;
+ }
+
+ // We must create our own Direct3D9Ex device. ANGLE can give us the device
+ // it's using, but that's probably a ID3D11Device.
+ // (copied from chromium dxva_video_decode_accelerator_win.cc)
+ D3DPRESENT_PARAMETERS present_params = {
+ .BackBufferWidth = 1,
+ .BackBufferHeight = 1,
+ .BackBufferFormat = D3DFMT_UNKNOWN,
+ .BackBufferCount = 1,
+ .SwapEffect = D3DSWAPEFFECT_DISCARD,
+ .hDeviceWindow = NULL,
+ .Windowed = TRUE,
+ .Flags = D3DPRESENTFLAG_VIDEO,
+ .FullScreen_RefreshRateInHz = 0,
+ .PresentationInterval = 0,
+ };
+ hr = IDirect3D9Ex_CreateDeviceEx(p->d3d9ex,
+ D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ NULL,
+ D3DCREATE_FPU_PRESERVE |
+ D3DCREATE_HARDWARE_VERTEXPROCESSING |
+ D3DCREATE_DISABLE_PSGP_THREADING |
+ D3DCREATE_MULTITHREADED,
+ &present_params,
+ NULL,
+ &p->device9ex);
+ if (FAILED(hr)) {
+ MP_FATAL(hw, "Failed to create Direct3D9Ex device: %s\n",
+ mp_HRESULT_to_str(hr));
+ goto fail;
+ }
+
+ hr = IDirect3DDevice9_CreateQuery(p->device9ex, D3DQUERYTYPE_EVENT,
+ &p->query9);
+ if (FAILED(hr)) {
+ MP_FATAL(hw, "Failed to create Direct3D query interface: %s\n",
+ mp_HRESULT_to_str(hr));
+ goto fail;
+ }
+
+ // Test the query API
+ hr = IDirect3DQuery9_Issue(p->query9, D3DISSUE_END);
+ if (FAILED(hr)) {
+ MP_FATAL(hw, "Failed to issue Direct3D END test query: %s\n",
+ mp_HRESULT_to_str(hr));
+ goto fail;
+ }
+
+ EGLint attrs[] = {
+ EGL_BUFFER_SIZE, 32,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_ALPHA_SIZE, 0,
+ EGL_NONE
+ };
+ EGLint count;
+ if (!eglChooseConfig(p->egl_display, attrs, &p->egl_config, 1, &count) ||
+ !count) {
+ MP_ERR(hw, "Failed to get EGL surface configuration\n");
+ goto fail;
+ }
+
+ if (!eglGetConfigAttrib(p->egl_display, p->egl_config,
+ EGL_BIND_TO_TEXTURE_RGBA, &p->alpha)) {
+ MP_FATAL(hw, "Failed to query EGL surface alpha\n");
+ goto fail;
+ }
+
+ hw->converted_imgfmt = IMGFMT_RGB0;
+
+ p->ctx.d3d9_device = (IDirect3DDevice9 *)p->device9ex;
+ p->ctx.hwctx.type = HWDEC_DXVA2;
+ p->ctx.hwctx.d3d_ctx = &p->ctx;
+
+ hw->hwctx = &p->ctx.hwctx;
+ return 0;
+fail:
+ destroy(hw);
+ return -1;
+}
+
+static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
+{
+ struct priv *p = hw->priv;
+ GL *gl = hw->gl;
+ HRESULT hr;
+
+ destroy_textures(hw);
+
+ assert(params->imgfmt == hw->driver->imgfmt);
+
+ HANDLE share_handle = NULL;
+ hr = IDirect3DDevice9Ex_CreateTexture(p->device9ex,
+ params->w, params->h,
+ 1, D3DUSAGE_RENDERTARGET,
+ p->alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
+ D3DPOOL_DEFAULT,
+ &p->texture9,
+ &share_handle);
+ if (FAILED(hr)) {
+ MP_ERR(hw, "Failed to create Direct3D9 texture: %s\n",
+ mp_HRESULT_to_str(hr));
+ goto fail;
+ }
+
+ hr = IDirect3DTexture9_GetSurfaceLevel(p->texture9, 0, &p->surface9);
+ if (FAILED(hr)) {
+ MP_ERR(hw, "Failed to get Direct3D9 surface from texture: %s\n",
+ mp_HRESULT_to_str(hr));
+ goto fail;
+ }
+
+ EGLint attrib_list[] = {
+ EGL_WIDTH, params->w,
+ EGL_HEIGHT, params->h,
+ EGL_TEXTURE_FORMAT, p->alpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
+ EGL_NONE
+ };
+ p->egl_surface = eglCreatePbufferFromClientBuffer(
+ p->egl_display, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
+ share_handle, p->egl_config, attrib_list);
+ if (p->egl_surface == EGL_NO_SURFACE) {
+ MP_ERR(hw, "Failed to create EGL surface\n");
+ goto fail;
+ }
+
+ gl->GenTextures(1, &p->gl_texture);
+ gl->BindTexture(GL_TEXTURE_2D, p->gl_texture);
+ 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;
+fail:
+ destroy_textures(hw);
+ return -1;
+}
+
+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;
+ if (!p->surface9 || !p->egl_surface || !p->gl_texture)
+ return -1;
+
+ HRESULT hr;
+ RECT rc = {0, 0, hw_image->w, hw_image->h};
+ IDirect3DSurface9* hw_surface = d3d9_surface_in_mp_image(hw_image);
+ hr = IDirect3DDevice9Ex_StretchRect(p->device9ex,
+ hw_surface, &rc,
+ p->surface9, &rc,
+ D3DTEXF_NONE);
+ if (FAILED(hr)) {
+ MP_ERR(hw, "Direct3D RGB conversion failed: %s\n",
+ mp_HRESULT_to_str(hr));
+ return -1;
+ }
+
+ hr = IDirect3DQuery9_Issue(p->query9, D3DISSUE_END);
+ if (FAILED(hr)) {
+ MP_ERR(hw, "Failed to issue Direct3D END query\n");
+ return -1;
+ }
+
+ // There doesn't appear to be an efficient way to do a blocking flush
+ // of the above StretchRect. Timeout of 8ms is required to reliably
+ // render 4k on Intel Haswell, Ivybridge and Cherry Trail Atom.
+ const int max_retries = 8;
+ const int64_t wait_us = 1000;
+ int retries = 0;
+ while (true) {
+ hr = IDirect3DQuery9_GetData(p->query9, NULL, 0, D3DGETDATA_FLUSH);
+ if (FAILED(hr)) {
+ MP_ERR(hw, "Failed to query Direct3D flush state\n");
+ return -1;
+ } else if (hr == S_FALSE) {
+ if (++retries > max_retries) {
+ MP_VERBOSE(hw, "Failed to flush frame after %lld ms\n",
+ (long long)(wait_us * max_retries) / 1000);
+ break;
+ }
+ mp_sleep_us(wait_us);
+ } else {
+ break;
+ }
+ }
+
+ gl->BindTexture(GL_TEXTURE_2D, p->gl_texture);
+ eglBindTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+
+ out_textures[0] = p->gl_texture;
+ return 0;
+}
+
+const struct gl_hwdec_driver gl_hwdec_dxva2egl = {
+ .name = "dxva2-egl",
+ .api = HWDEC_DXVA2,
+ .imgfmt = IMGFMT_DXVA2,
+ .create = create,
+ .reinit = reinit,
+ .map_image = map_image,
+ .destroy = destroy,
+};
diff --git a/video/out/opengl/hwdec_dxva2gldx.c b/video/out/opengl/hwdec_dxva2gldx.c
index 9e193fc3a5..69be0ccd18 100644
--- a/