summaryrefslogtreecommitdiffstats
path: root/video/out/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl')
-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
27 files changed, 1514 insertions, 813 deletions
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/video/out/opengl/hwdec_dxva2gldx.c
+++ b/video/out/opengl/hwdec_dxva2gldx.c
@@ -72,13 +72,8 @@ static void destroy_objects(struct gl_hwdec *hw)
static void destroy(struct gl_hwdec *hw)
{
struct priv *p = hw->priv;
- GL *gl = hw->gl;
destroy_objects(hw);
- if (!gl->DXCloseDeviceNV(p->device_h))
- MP_ERR(hw, "Failed to close Direct3D device in OpenGL %s\n",
- mp_LastError_to_str());
-
if (p->device)
IDirect3DDevice9Ex_Release(p->device);
}
@@ -94,28 +89,25 @@ static int create(struct gl_hwdec *hw)
struct priv *p = talloc_zero(hw, struct priv);
hw->priv = p;
+ // AMD drivers won't open multiple dxinterop HANDLES on the same D3D device,
+ // so we request the one already in use by context_dxinterop
+ p->device_h = gl->MPGetNativeDisplay("dxinterop_device_HANDLE");
+ if (!p->device_h)
+ return -1;
+
+ // But we also still need the actual D3D device
p->device = gl->MPGetNativeDisplay("IDirect3DDevice9Ex");
if (!p->device)
return -1;
IDirect3DDevice9Ex_AddRef(p->device);
p->ctx.d3d9_device = (IDirect3DDevice9 *)p->device;
- p->device_h = gl->DXOpenDeviceNV(p->device);
- if (!p->device_h) {
- MP_ERR(hw, "Failed to open Direct3D device in OpenGL: %s\n",
- mp_LastError_to_str());
- goto fail;
- }
-
p->ctx.hwctx.type = HWDEC_DXVA2;
p->ctx.hwctx.d3d_ctx = &p->ctx;
hw->hwctx = &p->ctx.hwctx;
hw->converted_imgfmt = SHARED_SURFACE_MPFMT;
return 0;
-fail:
- destroy(hw);
- return -1;
}
static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
diff --git a/video/out/opengl/hwdec_osx.c b/video/out/opengl/hwdec_osx.c
index 78d01b3894..addc16f404 100644
--- a/video/out/opengl/hwdec_osx.c
+++ b/video/out/opengl/hwdec_osx.c
@@ -101,16 +101,15 @@ static struct mp_image *download_image(struct mp_hwdec_ctx *ctx,
if (hw_image->imgfmt != IMGFMT_VIDEOTOOLBOX)
return NULL;
+ struct mp_image *image = NULL;
CVPixelBufferRef pbuf = (CVPixelBufferRef)hw_image->planes[3];
- CVPixelBufferLockBaseAddress(pbuf, 0);
+ CVPixelBufferLockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
size_t width = CVPixelBufferGetWidth(pbuf);
size_t height = CVPixelBufferGetHeight(pbuf);
uint32_t cvpixfmt = CVPixelBufferGetPixelFormatType(pbuf);
struct vt_format *f = vt_get_gl_format(cvpixfmt);
- if (!f) {
- CVPixelBufferUnlockBaseAddress(pbuf, 0);
- return NULL;
- }
+ if (!f)
+ goto unlock;
struct mp_image img = {0};
mp_image_setfmt(&img, f->imgfmt);
@@ -125,8 +124,10 @@ static struct mp_image *download_image(struct mp_hwdec_ctx *ctx,
mp_image_copy_attributes(&img, hw_image);
- struct mp_image *image = mp_image_pool_new_copy(swpool, &img);
- CVPixelBufferUnlockBaseAddress(pbuf, 0);
+ image = mp_image_pool_new_copy(swpool, &img);
+
+unlock:
+ CVPixelBufferUnlockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
return image;
}
@@ -146,14 +147,21 @@ static bool check_hwdec(struct gl_hwdec *hw)
return true;
}
+static uint32_t get_vt_fmt(struct mp_hwdec_ctx *ctx)
+{
+ struct gl_hwdec *hw = ctx->priv;
+ struct vt_format *f =
+ vt_get_gl_format_from_imgfmt(hw->global->opts->videotoolbox_format);
+ return f ? f->cvpixfmt : (uint32_t)-1;
+}
+
static int create(struct gl_hwdec *hw)
{
if (!check_hwdec(hw))
return -1;
struct priv *p = talloc_zero(hw, struct priv);
- struct vt_format *f =
- vt_get_gl_format_from_imgfmt(hw->global->opts->videotoolbox_format);
+ struct vt_format *f = vt_get_gl_format_from_imgfmt(IMGFMT_NV12);
if (!f)
return -1;
@@ -162,17 +170,26 @@ static int create(struct gl_hwdec *hw)
hw->hwctx = &p->hwctx;
hw->hwctx->download_image = download_image;
hw->hwctx->type = HWDEC_VIDEOTOOLBOX;
- hw->hwctx->priv = (void *)(uintptr_t)f->cvpixfmt;
+ hw->hwctx->get_vt_fmt = get_vt_fmt;
hw->gl_texture_target = GL_TEXTURE_RECTANGLE;
hw->gl->GenTextures(MP_MAX_PLANES, p->gl_planes);
+ hw->hwctx->priv = hw;
return 0;
}
static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
{
assert(params->imgfmt == hw->driver->imgfmt);
+
+ struct vt_format *f = vt_get_gl_format(params->hw_subfmt);
+ if (!f) {
+ MP_ERR(hw, "Unsupported CVPixelBuffer format.\n");
+ return -1;
+ }
+
+ hw->converted_imgfmt = f->imgfmt;
return 0;
}
diff --git a/video/out/opengl/lcms.c b/video/out/opengl/lcms.c
index c956127a75..7db8da6f70 100644
--- a/video/out/opengl/lcms.c
+++ b/video/out/opengl/lcms.c
@@ -44,6 +44,8 @@ struct gl_lcms {
size_t icc_size;
char *icc_path;
bool changed;
+ enum mp_csp_prim prev_prim;
+ enum mp_csp_trc prev_trc;
struct mp_log *log;
struct mpv_global *global;
@@ -166,16 +168,99 @@ void gl_lcms_set_memory_profile(struct gl_lcms *p, bstr *profile)
p->icc_size = profile->len;
}
-// Return and _reset_ whether the lookul table has changed since the last call.
-// If it has changed, gl_lcms_get_lut3d() should be called.
-bool gl_lcms_has_changed(struct gl_lcms *p)
+// Return and _reset_ whether the profile or config has changed since the last
+// call. If it has changed, gl_lcms_get_lut3d() should be called.
+bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
+ enum mp_csp_trc trc)
{
- bool change = p->changed;
+ bool change = p->changed || p->prev_prim != prim || p->prev_trc != trc;
p->changed = false;
+ p->prev_prim = prim;
+ p->prev_trc = trc;
return change;
}
-bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d)
+static cmsHPROFILE get_vid_profile(cmsContext cms, cmsHPROFILE disp_profile,
+ enum mp_csp_prim prim, enum mp_csp_trc trc)
+{
+ // The input profile for the transformation is dependent on the video
+ // primaries and transfer characteristics
+ struct mp_csp_primaries csp = mp_get_csp_primaries(prim);
+ cmsCIExyY wp_xyY = {csp.white.x, csp.white.y, 1.0};
+ cmsCIExyYTRIPLE prim_xyY = {
+ .Red = {csp.red.x, csp.red.y, 1.0},
+ .Green = {csp.green.x, csp.green.y, 1.0},
+ .Blue = {csp.blue.x, csp.blue.y, 1.0},
+ };
+
+ cmsToneCurve *tonecurve[3] = {0};
+ switch (trc) {
+ case MP_CSP_TRC_LINEAR: tonecurve[0] = cmsBuildGamma(cms, 1.0); break;
+ case MP_CSP_TRC_GAMMA18: tonecurve[0] = cmsBuildGamma(cms, 1.8); break;
+ case MP_CSP_TRC_GAMMA22: tonecurve[0] = cmsBuildGamma(cms, 2.2); break;
+ case MP_CSP_TRC_GAMMA28: tonecurve[0] = cmsBuildGamma(cms, 2.8); break;
+
+ case MP_CSP_TRC_SRGB:
+ // Values copied from Little-CMS
+ tonecurve[0] = cmsBuildParametricToneCurve(cms, 4,
+ (double[5]){2.40, 1/1.055, 0.055/1.055, 1/12.92, 0.04045});
+ break;
+
+ case MP_CSP_TRC_PRO_PHOTO:
+ tonecurve[0] = cmsBuildParametricToneCurve(cms, 4,
+ (double[5]){1.8, 1.0, 0.0, 1/16.0, 0.03125});
+ break;
+
+ case MP_CSP_TRC_BT_1886: {
+ // To build an appropriate BT.1886 transformation we need access to
+ // the display's black point, so we use the reverse mappings
+ cmsToneCurve *linear = cmsBuildGamma(cms, 1.0);
+ cmsHPROFILE rev_profile = cmsCreateRGBProfileTHR(cms, &wp_xyY, &prim_xyY,
+ (cmsToneCurve*[3]){linear, linear, linear});
+ cmsHTRANSFORM disp2src = cmsCreateTransformTHR(cms,
+ disp_profile, TYPE_RGB_16, rev_profile, TYPE_RGB_DBL,
+ INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsFreeToneCurve(linear);
+ cmsCloseProfile(rev_profile);
+ if (!disp2src)
+ return false;
+
+ uint64_t disp_black[3] = {0};
+ double src_black[3];
+ cmsDoTransform(disp2src, disp_black, src_black, 1);
+
+ // Build the parametric BT.1886 transfer curve, one per channel
+ for (int i = 0; i < 3; i++) {
+ const double gamma = 2.40;
+ double