summaryrefslogtreecommitdiffstats
path: root/video/out/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl')
-rw-r--r--video/out/opengl/angle.c208
-rw-r--r--video/out/opengl/common.c87
-rw-r--r--video/out/opengl/common.h30
-rw-r--r--video/out/opengl/dxinterop.c636
-rw-r--r--video/out/opengl/hwdec.c11
-rw-r--r--video/out/opengl/hwdec.h6
-rw-r--r--video/out/opengl/hwdec_osx.c28
-rw-r--r--video/out/opengl/nnedi3.c59
-rw-r--r--video/out/opengl/nnedi3.h3
-rw-r--r--video/out/opengl/superxbr.c12
-rw-r--r--video/out/opengl/utils.c47
-rw-r--r--video/out/opengl/video.c253
-rw-r--r--video/out/opengl/video.h2
-rw-r--r--video/out/opengl/video_shaders.c34
-rw-r--r--video/out/opengl/w32.c47
-rw-r--r--video/out/opengl/x11.c77
-rw-r--r--video/out/opengl/x11egl.c7
17 files changed, 1324 insertions, 223 deletions
diff --git a/video/out/opengl/angle.c b/video/out/opengl/angle.c
new file mode 100644
index 0000000000..d660f82762
--- /dev/null
+++ b/video/out/opengl/angle.c
@@ -0,0 +1,208 @@
+/*
+ * This file is part of mpv.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * You can alternatively redistribute this file 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.
+ */
+
+#include <windows.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "common/common.h"
+#include "video/out/w32_common.h"
+#include "common.h"
+
+struct priv {
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ EGLSurface egl_surface;
+};
+
+static void angle_uninit(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ if (p->egl_context) {
+ eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ eglDestroyContext(p->egl_display, p->egl_context);
+ }
+ p->egl_context = EGL_NO_CONTEXT;
+ vo_w32_uninit(ctx->vo);
+}
+
+static EGLConfig select_fb_config_egl(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ EGLint attributes[] = {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_NONE
+ };
+
+ EGLint config_count;
+ EGLConfig config;
+
+ eglChooseConfig(p->egl_display, attributes, &config, 1, &config_count);
+
+ if (!config_count) {
+ MP_FATAL(ctx->vo, "Could find EGL configuration!\n");
+ return NULL;
+ }
+
+ return config;
+}
+
+static bool create_context_egl(MPGLContext *ctx, EGLConfig config, int version)
+{
+ struct priv *p = ctx->priv;
+
+ EGLint context_attributes[] = {
+ EGL_CONTEXT_CLIENT_VERSION, version,
+ EGL_NONE
+ };
+
+ p->egl_context = eglCreateContext(p->egl_display, config,
+ EGL_NO_CONTEXT, context_attributes);
+
+ if (p->egl_context == EGL_NO_CONTEXT) {
+ MP_VERBOSE(ctx->vo, "Could not create EGL GLES %d context!\n", version);
+ return false;
+ }
+
+ eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
+ p->egl_context);
+
+ return true;
+}
+
+static void *get_proc_address(const GLubyte *proc_name)
+{
+ return eglGetProcAddress(proc_name);
+}
+
+static int angle_init(struct MPGLContext *ctx, int flags)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
+
+ if (!vo_w32_init(vo))
+ goto fail;
+
+ HDC dc = GetDC(vo_w32_hwnd(vo));
+ if (!dc) {
+ MP_FATAL(vo, "Couldn't get DC\n");
+ goto fail;
+ }
+
+ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
+ (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+ if (!eglGetPlatformDisplayEXT) {
+ MP_FATAL(vo, "Missing EGL_EXT_platform_base\n");
+ goto fail;
+ }
+
+ EGLint d3d_types[] = {EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
+ EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE};
+ for (int i = 0; i < MP_ARRAY_SIZE(d3d_types); i++) {
+ EGLint display_attributes[] = {
+ EGL_PLATFORM_ANGLE_TYPE_ANGLE,
+ d3d_types[i],
+ EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
+ EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
+ EGL_NONE,
+ };
+
+ p->egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc,
+ display_attributes);
+ if (p->egl_display != EGL_NO_DISPLAY)
+ break;
+ }
+ if (p->egl_display == EGL_NO_DISPLAY) {
+ MP_FATAL(vo, "Couldn't get display\n");
+ goto fail;
+ }
+
+ if (!eglInitialize(p->egl_display, NULL, NULL)) {
+ MP_FATAL(vo, "Couldn't initialize EGL\n");
+ goto fail;
+ }
+
+ eglBindAPI(EGL_OPENGL_ES_API);
+ if (eglGetError() != EGL_SUCCESS) {
+ MP_FATAL(vo, "Couldn't bind GLES API\n");
+ goto fail;
+ }
+
+ EGLConfig config = select_fb_config_egl(ctx);
+ if (!config)
+ goto fail;
+
+ p->egl_surface = eglCreateWindowSurface(p->egl_display, config,
+ vo_w32_hwnd(vo), NULL);
+ if (p->egl_surface == EGL_NO_SURFACE) {
+ MP_FATAL(ctx->vo, "Could not create EGL surface!\n");
+ goto fail;
+ }
+
+ if (!create_context_egl(ctx, config, 3) &&
+ !create_context_egl(ctx, config, 2))
+ {
+ MP_FATAL(ctx->vo, "Could not create EGL context!\n");
+ goto fail;
+ }
+
+ mpgl_load_functions(ctx->gl, get_proc_address, NULL, vo->log);
+
+ return 0;
+
+fail:
+ angle_uninit(ctx);
+ return -1;
+}
+
+static int angle_reconfig(struct MPGLContext *ctx)
+{
+ vo_w32_config(ctx->vo);
+ return 0;
+}
+
+static int angle_control(MPGLContext *ctx, int *events, int request, void *arg)
+{
+ return vo_w32_control(ctx->vo, events, request, arg);
+}
+
+static void angle_swap_buffers(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+const struct mpgl_driver mpgl_driver_angle = {
+ .name = "angle",
+ .priv_size = sizeof(struct priv),
+ .init = angle_init,
+ .reconfig = angle_reconfig,
+ .swap_buffers = angle_swap_buffers,
+ .control = angle_control,
+ .uninit = angle_uninit,
+};
diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c
index 54389e15cf..00cd535301 100644
--- a/video/out/opengl/common.c
+++ b/video/out/opengl/common.c
@@ -154,14 +154,13 @@ static const struct gl_functions gl_functions[] = {
// GL 2.1+ desktop only (and GLSL 120 shaders)
{
.ver_core = 210,
- .provides = MPGL_CAP_ROW_LENGTH | MPGL_CAP_1D_TEX | MPGL_CAP_3D_TEX,
+ .provides = MPGL_CAP_ROW_LENGTH | MPGL_CAP_1D_TEX,
.functions = (const struct gl_function[]) {
DEF_FN(DrawBuffer),
DEF_FN(GetTexLevelParameteriv),
DEF_FN(MapBuffer),
DEF_FN(ReadBuffer),
DEF_FN(TexImage1D),
- DEF_FN(TexImage3D),
DEF_FN(UnmapBuffer),
{0}
},
@@ -172,14 +171,31 @@ static const struct gl_functions gl_functions[] = {
.ver_es_core = 300,
.functions = (const struct gl_function[]) {
DEF_FN(BindBufferBase),
+ DEF_FN(BlitFramebuffer),
DEF_FN(GetStringi),
// for ES 3.0
- DEF_FN(GetTexLevelParameteriv),
DEF_FN(ReadBuffer),
DEF_FN(UnmapBuffer),
{0}
},
},
+ // For ES 3.1 core
+ {
+ .ver_es_core = 310,
+ .functions = (const struct gl_function[]) {
+ DEF_FN(GetTexLevelParameteriv),
+ {0}
+ },
+ },
+ {
+ .ver_core = 210,
+ .ver_es_core = 300,
+ .provides = MPGL_CAP_3D_TEX,
+ .functions = (const struct gl_function[]) {
+ DEF_FN(TexImage3D),
+ {0}
+ },
+ },
// Useful for ES 2.0
{
.ver_core = 110,
@@ -215,13 +231,6 @@ static const struct gl_functions gl_functions[] = {
{0}
}
},
- // Float textures, extension in GL 2.x, core in GL 3.x core.
- {
- .ver_core = 300,
- .ver_es_core = 300,
- .extension = "GL_ARB_texture_float",
- .provides = MPGL_CAP_FLOAT_TEX,
- },
// GL_RED / GL_RG textures, extension in GL 2.x, core in GL 3.x core.
{
.ver_core = 300,
@@ -280,6 +289,22 @@ static const struct gl_functions gl_functions[] = {
{0}
},
},
+#if HAVE_GL_DXINTEROP
+ {
+ .extension = "WGL_NV_DX_interop",
+ .provides = MPGL_CAP_DXINTEROP,
+ .functions = (const struct gl_function[]) {
+ DEF_FN_NAME(DXSetResourceShareHandleNV, "wglDXSetResourceShareHandleNV"),
+ DEF_FN_NAME(DXOpenDeviceNV, "wglDXOpenDeviceNV"),
+ DEF_FN_NAME(DXCloseDeviceNV, "wglDXCloseDeviceNV"),
+ DEF_FN_NAME(DXRegisterObjectNV, "wglDXRegisterObjectNV"),
+ DEF_FN_NAME(DXUnregisterObjectNV, "wglDXUnregisterObjectNV"),
+ DEF_FN_NAME(DXLockObjectsNV, "wglDXLockObjectsNV"),
+ DEF_FN_NAME(DXUnlockObjectsNV, "wglDXUnlockObjectsNV"),
+ {0}
+ },
+ },
+#endif
// Apple Packed YUV Formats
// For gl_hwdec_vda.c
// http://www.opengl.org/registry/specs/APPLE/rgb_422.txt
@@ -317,6 +342,7 @@ static const struct gl_functions gl_functions[] = {
// uniform buffer object extensions, requires OpenGL 3.1.
{
.ver_core = 310,
+ .ver_es_core = 300,
.extension = "GL_ARB_uniform_buffer_object",
.functions = (const struct gl_function[]) {
DEF_FN(GetUniformBlockIndex),
@@ -467,7 +493,7 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
} else {
gl->glsl_version = 110;
int glsl_major = 0, glsl_minor = 0;
- if (sscanf(shader, "%d.%d", &glsl_major, &glsl_minor) == 2)
+ if (shader && sscanf(shader, "%d.%d", &glsl_major, &glsl_minor) == 2)
gl->glsl_version = glsl_major * 100 + glsl_minor;
// GLSL 400 defines "sample" as keyword - breaks custom shaders.
gl->glsl_version = MPMIN(gl->glsl_version, 330);
@@ -478,6 +504,14 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
mp_verbose(log, "Detected suspected software renderer.\n");
}
+ // Detect 16F textures that work with GL_LINEAR filtering.
+ if ((!gl->es && (gl->version >= 300 || check_ext(gl, "GL_ARB_texture_float"))) ||
+ (gl->es && (gl->version >= 310 || check_ext(gl, "GL_OES_texture_half_float_linear"))))
+ {
+ mp_verbose(log, "Filterable half-float textures supported.\n");
+ gl->mpgl_caps |= MPGL_CAP_FLOAT_TEX;
+ }
+
// Provided for simpler handling if no framebuffer support is available.
if (!gl->BindFramebuffer)
gl->BindFramebuffer = &dummy_glBindFramebuffer;
@@ -503,10 +537,13 @@ void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
extern const struct mpgl_driver mpgl_driver_x11;
extern const struct mpgl_driver mpgl_driver_x11egl;
+extern const struct mpgl_driver mpgl_driver_x11_probe;
extern const struct mpgl_driver mpgl_driver_drm_egl;
extern const struct mpgl_driver mpgl_driver_cocoa;
extern const struct mpgl_driver mpgl_driver_wayland;
extern const struct mpgl_driver mpgl_driver_w32;
+extern const struct mpgl_driver mpgl_driver_angle;
+extern const struct mpgl_driver mpgl_driver_dxinterop;
extern const struct mpgl_driver mpgl_driver_rpi;
static const struct mpgl_driver *const backends[] = {
@@ -516,21 +553,30 @@ static const struct mpgl_driver *const backends[] = {
#if HAVE_GL_COCOA
&mpgl_driver_cocoa,
#endif
+#if HAVE_EGL_ANGLE
+ &mpgl_driver_angle,
+#endif
#if HAVE_GL_WIN32
&mpgl_driver_w32,
#endif
+#if HAVE_GL_DXINTEROP
+ &mpgl_driver_dxinterop,
+#endif
#if HAVE_GL_WAYLAND
&mpgl_driver_wayland,
#endif
+#if HAVE_GL_X11
+ &mpgl_driver_x11_probe,
+#endif
#if HAVE_EGL_X11
&mpgl_driver_x11egl,
#endif
-#if HAVE_EGL_DRM
- &mpgl_driver_drm_egl,
-#endif
#if HAVE_GL_X11
&mpgl_driver_x11,
#endif
+#if HAVE_EGL_DRM
+ &mpgl_driver_drm_egl,
+#endif
};
int mpgl_find_backend(const char *name)
@@ -596,6 +642,7 @@ static MPGLContext *init_backend(struct vo *vo, const struct mpgl_driver *driver
MP_VERBOSE(vo, "Initializing OpenGL backend '%s'\n", ctx->driver->name);
ctx->priv = talloc_zero_size(ctx, ctx->driver->priv_size);
if (ctx->driver->init(ctx, vo_flags) < 0) {
+ vo->probing = old_probing;
talloc_free(ctx);
return NULL;
}
@@ -604,8 +651,8 @@ static MPGLContext *init_backend(struct vo *vo, const struct mpgl_driver *driver
if (!ctx->gl->version && !ctx->gl->es)
goto cleanup;
- if (ctx->gl->es && vo->probing) {
- MP_INFO(ctx->vo, "Skipping experimental GLES support (use --vo=opengl).\n");
+ if (probing && ctx->gl->es && (vo_flags & VOFLAG_NO_GLES)) {
+ MP_VERBOSE(ctx->vo, "Skipping GLES backend.\n");
goto cleanup;
}
@@ -638,6 +685,14 @@ MPGLContext *mpgl_init(struct vo *vo, const char *backend_name, int vo_flags)
if (ctx)
break;
}
+ // VO forced, but no backend is ok => force the first that works at all
+ if (!ctx && !vo->probing) {
+ for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
+ ctx = init_backend(vo, backends[n], false, vo_flags);
+ if (ctx)
+ break;
+ }
+ }
} else if (index >= 0) {
ctx = init_backend(vo, backends[index], false, vo_flags);
}
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index d87be595ba..537e785e3b 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -63,6 +63,7 @@ enum {
MPGL_CAP_1D_TEX = (1 << 14),
MPGL_CAP_3D_TEX = (1 << 15),
MPGL_CAP_DEBUG = (1 << 16),
+ MPGL_CAP_DXINTEROP = (1 << 17), // WGL_NV_DX_interop
MPGL_CAP_SW = (1 << 30), // indirect or sw renderer
};
@@ -75,10 +76,11 @@ enum {
#define MPGL_VER_P(ver) MPGL_VER_GET_MAJOR(ver), MPGL_VER_GET_MINOR(ver)
enum {
- VOFLAG_GLES = 1 << 0, // Hint to prefer GLES2 if possible
- VOFLAG_GL_DEBUG = 1 << 1, // Hint to request debug OpenGL context
- VOFLAG_ALPHA = 1 << 2, // Hint to request alpha framebuffer
- VOFLAG_SW = 1 << 3, // Hint to accept a software GL renderer
+ VOFLAG_GLES = 1 << 0, // Hint to create a GLES2 context
+ VOFLAG_NO_GLES = 1 << 1, // Hint to create a desktop GL context
+ VOFLAG_GL_DEBUG = 1 << 2, // Hint to request debug OpenGL context
+ VOFLAG_ALPHA = 1 << 3, // Hint to request alpha framebuffer
+ VOFLAG_SW = 1 << 4, // Hint to accept a software GL renderer
};
struct MPGLContext;
@@ -127,6 +129,9 @@ typedef struct MPGLContext {
// Windows-specific hack. See vo_opengl dwmflush suboption.
int dwm_flush_opt;
+ // Flip the rendered image vertically. This is useful for dxinterop.
+ bool flip_v;
+
// For free use by the mpgl_driver.
void *priv;
} MPGLContext;
@@ -234,6 +239,8 @@ struct GL {
GLenum (GLAPIENTRY *CheckFramebufferStatus)(GLenum);
void (GLAPIENTRY *FramebufferTexture2D)(GLenum, GLenum, GLenum, GLuint,
GLint);
+ void (GLAPIENTRY *BlitFramebuffer)(GLint, GLint, GLint, GLint, GLint, GLint,
+ GLint, GLint, GLbitfield, GLenum);
void (GLAPIENTRY *Uniform1f)(GLint, GLfloat);
void (GLAPIENTRY *Uniform2f)(GLint, GLfloat, GLfloat);
@@ -258,6 +265,21 @@ struct GL {
void (GLAPIENTRY *VDPAUMapSurfacesNV)(GLsizei, const GLvdpauSurfaceNV *);
void (GLAPIENTRY *VDPAUUnmapSurfacesNV)(GLsizei, const GLvdpauSurfaceNV *);
+#if HAVE_GL_WIN32
+ // The HANDLE type might not be present on non-Win32
+ BOOL (GLAPIENTRY *DXSetResourceShareHandleNV)(void *dxObject,
+ HANDLE shareHandle);
+ HANDLE (GLAPIENTRY *DXOpenDeviceNV)(void *dxDevice);
+ BOOL (GLAPIENTRY *DXCloseDeviceNV)(HANDLE hDevice);
+ HANDLE (GLAPIENTRY *DXRegisterObjectNV)(HANDLE hDevice, void *dxObject,
+ GLuint name, GLenum type, GLenum access);
+ BOOL (GLAPIENTRY *DXUnregisterObjectNV)(HANDLE hDevice, HANDLE hObject);
+ BOOL (GLAPIENTRY *DXLockObjectsNV)(HANDLE hDevice, GLint count,
+ HANDLE *hObjects);
+ BOOL (GLAPIENTRY *DXUnlockObjectsNV)(HANDLE hDevice, GLint count,
+ HANDLE *hObjects);
+#endif
+
GLint (GLAPIENTRY *GetVideoSync)(GLuint *);
GLint (GLAPIENTRY *WaitVideoSync)(GLint, GLint, unsigned int *);
diff --git a/video/out/opengl/dxinterop.c b/video/out/opengl/dxinterop.c
new file mode 100644
index 0000000000..04518714d1
--- /dev/null
+++ b/video/out/opengl/dxinterop.c
@@ -0,0 +1,636 @@
+/*
+ * This file is part of mpv.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * You can alternatively redistribute this file 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.
+ */
+
+#include <windows.h>
+#include <initguid.h>
+#include <d3d9.h>
+#include <dwmapi.h>
+#include "video/out/w32_common.h"
+#include "common.h"
+
+// For WGL_ACCESS_WRITE_DISCARD_NV, etc.
+#include <GL/wglext.h>
+
+// mingw-w64 header typo?
+#ifndef IDirect3DSwapChain9Ex_GetBackBuffer
+#define IDirect3DSwapChain9Ex_GetBackBuffer IDirect3DSwapChain9EX_GetBackBuffer
+#endif
+
+struct priv {
+ HMODULE d3d9_dll;
+ HRESULT (WINAPI *Direct3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D);
+
+ // Direct3D9 device and resources
+ IDirect3D9Ex *d3d9ex;
+ IDirect3DDevice9Ex *device;
+ HANDLE device_h;
+ IDirect3DSwapChain9Ex *swapchain;
+ IDirect3DSurface9 *backbuffer;
+ IDirect3DSurface9 *rtarget;
+ HANDLE rtarget_h;
+
+ // OpenGL offscreen context
+ HWND os_wnd;
+ HDC os_dc;
+ HGLRC os_ctx;
+
+ // OpenGL resources
+ GLuint framebuffer;
+ GLuint texture;
+
+ // Is the shared framebuffer currently bound?
+ bool fb_bound;
+ // Is the shared texture currently attached?
+ bool tex_attached;
+ // Did we lose the device?
+ bool lost_device;
+
+ // Requested and current parameters
+ int requested_swapinterval;
+ int width, height, swapinterval;
+
+ void (GLAPIENTRY *real_gl_bind_framebuffer)(GLenum, GLuint);
+};
+
+static __thread struct MPGLContext *current_ctx;
+
+static void pump_message_loop(void)
+{
+ // We have a hidden window on this thread (for the OpenGL context,) so pump
+ // its message loop at regular intervals to be safe
+ MSG message;
+ while (PeekMessageW(&message, NULL, 0, 0, PM_REMOVE))
+ DispatchMessageW(&message);
+}
+
+static void *w32gpa(const GLubyte *procName)
+{
+ HMODULE oglmod;
+ void *res = wglGetProcAddress(procName);
+ if (res)
+ return res;
+ oglmod = GetModuleHandleW(L"opengl32.dll");
+ return GetProcAddress(oglmod, procName);
+}
+
+static int os_ctx_create(struct MPGLContext *ctx)
+{
+ static const wchar_t os_wnd_class[] = L"mpv offscreen gl";
+ struct priv *p = ctx->priv;
+ HGLRC legacy_context = NULL;
+
+ RegisterClassExW(&(WNDCLASSEXW) {
+ .cbSize = sizeof(WNDCLASSEXW),
+ .style = CS_OWNDC,
+ .lpfnWndProc = DefWindowProc,
+ .hInstance = GetModuleHandleW(NULL),
+ .lpszClassName = os_wnd_class,
+ });
+
+ // Create a hidden window for an offscreen OpenGL context. It might also be
+ // possible to use the VO window, but MSDN recommends against drawing to
+ // the same window with flip mode present and other APIs, so play it safe.
+ p->os_wnd = CreateWindowExW(0, os_wnd_class, os_wnd_class, 0, 0, 0, 200,
+ 200, NULL, NULL, GetModuleHandleW(NULL), NULL);
+ p->os_dc = GetDC(p->os_wnd);
+ if (!p->os_dc) {
+ MP_FATAL(ctx->vo, "Couldn't create window for offscreen rendering\n");
+ goto fail;
+ }
+
+ // Choose a pixel format. It probably doesn't matter what this is because
+ // the primary framebuffer will not be used.
+ PIXELFORMATDESCRIPTOR pfd = {
+ .nSize = sizeof pfd,
+ .nVersion = 1,
+ .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+ .iPixelType = PFD_TYPE_RGBA,
+ .cColorBits = 24,
+ .iLayerType = PFD_MAIN_PLANE,
+ };
+ int pf = ChoosePixelFormat(p->os_dc, &pfd);
+ if (!pf) {
+ MP_FATAL(ctx->vo, "Couldn't choose pixelformat for offscreen rendering\n");
+ goto fail;
+ }
+ SetPixelFormat(p->os_dc, pf, &pfd);
+
+ legacy_context = wglCreateContext(p->os_dc);
+ if (!legacy_context || !wglMakeCurrent(p->os_dc, legacy_context)) {
+ MP_FATAL(ctx->vo, "Couldn't create GL context for offscreen rendering\n");
+ goto fail;
+ }
+
+ const char *(GLAPIENTRY *wglGetExtensionsStringARB)(HDC hdc)
+ = w32gpa((const GLubyte*)"wglGetExtensionsStringARB");
+ if (!wglGetExtensionsStringARB) {
+ MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
+ goto fail;
+ }
+
+ const char *wgl_exts = wglGetExtensionsStringARB(p->os_dc);
+ if (!strstr(wgl_exts, "WGL_ARB_create_context")) {
+ MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
+ goto fail;
+ }
+
+ HGLRC (GLAPIENTRY *wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext,
+ const int *attribList)
+ = w32gpa((const GLubyte*)"wglCreateContextAttribsARB");
+ if (!wglCreateContextAttribsARB) {
+ MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
+ goto fail;
+ }
+
+ int attribs[] = {
+ WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
+ WGL_CONTEXT_MINOR_VERSION_ARB, 0,
+ WGL_CONTEXT_FLAGS_ARB, 0,
+ WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
+ 0
+ };
+
+ p->os_ctx = wglCreateContextAttribsARB(p->os_dc, 0, attribs);
+ if (!p->os_ctx) {
+ // NVidia, instead of ignoring WGL_CONTEXT_FLAGS_ARB, will error out if
+ // it's present on pre-3.2 contexts.
+ // Remove it from attribs and retry the context creation.
+ attribs[6] = attribs[7] = 0;
+ p->os_ctx = wglCreateContextAttribsARB(p->os_dc, 0, attribs);
+ }
+ if (!p->os_ctx) {
+ MP_FATAL(ctx->vo, "Couldn't create GL 3.x context for offscreen rendering\n");
+ goto fail;
+ }
+
+ wglMakeCurrent(p->os_dc, NULL);
+ wglDeleteContext(legacy_context);
+ legacy_context = NULL;
+
+ if (!wglMakeCurrent(p->os_dc, p->os_ctx)) {
+ MP_FATAL(ctx->vo, "Couldn't create GL 3.x context for offscreen rendering\n");
+ goto fail;
+ }
+
+ mpgl_load_functions(ctx->gl, w32gpa, wgl_exts, ctx->vo->log);
+ if (!(ctx->gl->mpgl_caps & MPGL_CAP_DXINTEROP)) {
+ MP_FATAL(ctx->vo, "WGL_NV_DX_interop is not supported\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ if (legacy_context) {
+ wglMakeCurrent(p->os_dc, NULL);
+ wglDeleteContext(legacy_context);
+ }
+ return -1;
+}
+
+static void os_ctx_destroy(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ if (p->os_ctx) {
+ wglMakeCurrent(p->os_dc, NULL);
+ wglDeleteContext(p->os_ctx);
+ }
+ if (p->os_dc)
+ ReleaseDC(p->os_wnd, p->os_dc);
+ if (p->os_wnd)
+ DestroyWindow(p->os_wnd);
+}
+
+static void try_attach_texture(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct GL *gl = ctx->gl;
+
+ if (p->fb_bound && !p->tex_attached) {
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, p->texture, 0);
+ p->tex_attached = true;
+ }
+}
+
+static int d3d_size_dependent_create(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct GL *gl = ctx->gl;
+ HRESULT hr;
+
+ IDirect3DSwapChain9 *sw9;
+ hr = IDirect3DDevice9Ex_GetSwapChain(p->device, 0, &sw9);
+ if (FAILED(hr)) {
+ MP_FATAL(ctx->vo, "Couldn't get swap chain\n");
+ return -1;
+ }
+
+ hr = IDirect3DSwapChain9_QueryInterface(sw9, &IID_IDirect3DSwapChain9Ex,
+ (void**)&p->swapchain);
+ if (FAILED(hr)) {
+ IDirect3DSwapChain9_Release(sw9);
+ MP_FATAL(ctx->vo, "Couldn't get swap chain\n");
+ return -1;
+ }
+ IDirect3DSwapChain9_Release(sw9);
+
+ hr = IDirect3DSwapChain9Ex_GetBackBuffer(p->swapchain, 0,
+ D3DBACKBUFFER_TYPE_MONO, &p->backbuffer);
+ if (FAILED(hr)) {
+ MP_FATAL(ctx->vo, "Couldn't get backbuffer\n");
+ return -1;
+ }
+
+ // Get the format of the backbuffer
+ D3DSURFACE_DESC bb_desc = { 0 };
+ IDirect3DSurface9_GetDesc(p->backbuffer, &bb_desc);
+
+ MP_VERBOSE(ctx->vo, "DX_interop backbuffer size: %ux%u\n",
+ (unsigned)bb_desc.Width, (unsigned)bb_desc.Height);
+ MP_VERBOSE(ctx->vo, "DX_interop backbuffer format: %u\n",
+ (unsigned)bb_desc.Format);
+
+ // Note: This backend has only been tested on an 8-bit display. It's
+ // unknown whether this code is enough to support other formats or if more
+ // work is needed.
+ switch (bb_desc.Format) {
+ case D3DFMT_X1R5G5B5: case D3DFMT_A1R5G5B5:
+ ctx->depth_r = ctx->depth_g = ctx->depth_b = 5;
+ break;
+ case D3DFMT_R5G6B5:
+ ctx->depth_r = 5; ctx->depth_g = 6; ctx->depth_b = 5;
+ break;
+ case D3DFMT_R8G8B8: case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8:
+ case D3DFMT_A8B8G8R8: case D3DFMT_X8B8G8R8: default:
+ ctx->depth_r = ctx->depth_g = ctx->depth_b = 8;
+ break;
+ case D3DFMT_A2R10G10B10: case D3DFMT_A2B10G10R10:
+ ctx->depth_r = ctx->depth_g = ctx->depth_b = 10;
+ break;
+ }
+
+ // Create a rendertarget with the same format as the backbuffer for
+ // rendering from OpenGL
+ HANDLE share_handle = NULL;
+ hr = IDirect3DDevice9Ex_CreateRenderTarget(p->device, bb_desc.Width,
+ bb_desc.Height, bb_desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE,
+ &p->rtarget, &share_handle);
+ if (FAILED(hr)) {
+ MP_FATAL(ctx->vo, "Couldn't create rendertarget\n");
+ return -1;
+ }
+
+ // Register the share handle with WGL_NV_DX_interop. Nvidia does not
+ // require the use of share handles, but Intel does.
+ if (share_handle)
+ gl->DXSetResourceShareHandleNV(p->rtarget, share_handle);
+
+ // Create the OpenGL-side texture
+ gl->GenTextures(1, &p->texture);
+ p->tex_attached = false;
+
+ // Now share the rendertarget with OpenGL as a texture
+ p->rtarget_h = gl->DXRegisterObjectNV(p->device_h, p->rtarget, p->texture,
+ GL_TEXTURE_2D, WGL_ACCESS_WRITE_DISCARD_NV);
+ if (!p->rtarget_h) {
+ MP_FATAL(ctx->vo, "Couldn't share rendertarget with GL: 0x%08x\n",
+ (unsigned)GetLastError());
+ return -1;
+ }
+
+ // Lock the rendertarget for use from OpenGL. This will only be unlocked in
+ // swap_buffers() when it is blitted to the real Direct3D backbuffer.
+ if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
+ MP_FATAL(ctx->vo, "Couldn't lock rendertarget\n");
+ return -1;
+ }
+
+ // Only attach the shared texture if the shared framebuffer is bound. If
+ // it's not, the texture will be attached when glBindFramebuffer is called.
+ try_attach_texture(ctx);
+
+ return 0;
+}
+
+static void d3d_size_dependent_destroy(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct GL *gl = ctx->gl;
+
+ if (p->rtarget_h) {
+ gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h);
+ gl->DXUnregisterObjectNV(p->device_h, p->rtarget_h);
+ }
+ if (p->texture)
+ gl->DeleteTextures(1, &p->texture);
+ if (p->rtarget)
+ IDirect3DSurface9_Release(p->rtarget);
+ if (p->backbuffer)
+ IDirect3DSurface9_Release(p->backbuffer);
+ if (p->swapchain)
+ IDirect3DSwapChain9Ex_Release(p->swapchain);
+}
+
+static void fill_presentparams(MPGLContext *ctx, D3DPRESENT_PARAMETERS *pparams)
+{
+ struct priv *p = ctx->priv;
+
+ // Present intervals other than IMMEDIATE and ONE don't seem to work. It's
+ // possible that they're not compatible with FLIPEX.
+ UINT presentation_interval;
+ switch (p->requested_swapinterval) {
+ case 0: presentation_interval = D3DPRESENT_INTERVAL_IMMEDIATE; break;
+ case 1: presentation_interval = D3DPRESENT_INTERVAL_ONE; break;
+ default: presentation_interval = D3DPRESENT_INTERVAL_ONE; break;
+ }
+
+ *pparams = (D3DPRESENT_PARAMETERS) {
+ .Windowed = TRUE,
+ // The length of the backbuffer queue shouldn't affect latency because
+ // swap_buffers() always uses the backbuffer at the head of the queue
+ // and presents it immediately. MSDN says there is a performance
+ // penalty for having a short backbuffer queue and this seems to be
+ // true, at least on Nvidia, where less than four backbuffers causes
+ // very high CPU usage. Use six to be safe.
+ .BackBufferCount = 6,
+ .SwapEffect = D3DSWAPEFFECT_FLIPEX,
+ // Automatically get the backbuffer format from the display format. The
+ // size of the backbuffer is automatically determined too.
+ .BackBufferFormat = D3DFMT_UNKNOWN,
+ .PresentationInterval = presentation_interval,
+ .hDeviceWindow = vo_w32_hwnd(ctx->vo),
+ };
+}
+
+static int d3d_create(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct GL *gl = ctx->gl;
+ HRESULT hr;
+
+ p->d3d9_dll = LoadLibraryW(L"d3d9.dll");
+ if (!p->d3d9_dll) {
+ MP_FATAL(ctx->vo, "\"d3d9.dll\" not found\n");
+ return -1;
+ }
+
+ // WGL_NV_dx_interop requires Direct3D 9Ex on WDDM systems. Direct3D 9Ex
+ // also enables flip mode present for efficient rendering with the DWM.
+ p->Direct3DCreate9Ex = (void*)GetProcAddress(p->d3d9_dll,
+ "Direct3DCreate9Ex");
+ if (!p->Direct3DCreate9Ex) {
+ MP_FATAL(ctx->vo, "Direct3D 9Ex not supported\n");
+ return -1;
+ }
+
+ hr = p->Direct3DCreate9Ex(D3D_SDK_VERSION, &p->d3d9ex);
+ if (FAILED(hr)) {
+ MP_FATAL(ctx->vo, "Couldn't create Direct3D9Ex\n");