diff options
Diffstat (limited to 'video/out/opengl')
-rw-r--r-- | video/out/opengl/angle.c | 208 | ||||
-rw-r--r-- | video/out/opengl/common.c | 87 | ||||
-rw-r--r-- | video/out/opengl/common.h | 30 | ||||
-rw-r--r-- | video/out/opengl/dxinterop.c | 636 | ||||
-rw-r--r-- | video/out/opengl/hwdec.c | 11 | ||||
-rw-r--r-- | video/out/opengl/hwdec.h | 6 | ||||
-rw-r--r-- | video/out/opengl/hwdec_osx.c | 28 | ||||
-rw-r--r-- | video/out/opengl/nnedi3.c | 59 | ||||
-rw-r--r-- | video/out/opengl/nnedi3.h | 3 | ||||
-rw-r--r-- | video/out/opengl/superxbr.c | 12 | ||||
-rw-r--r-- | video/out/opengl/utils.c | 47 | ||||
-rw-r--r-- | video/out/opengl/video.c | 253 | ||||
-rw-r--r-- | video/out/opengl/video.h | 2 | ||||
-rw-r--r-- | video/out/opengl/video_shaders.c | 34 | ||||
-rw-r--r-- | video/out/opengl/w32.c | 47 | ||||
-rw-r--r-- | video/out/opengl/x11.c | 77 | ||||
-rw-r--r-- | video/out/opengl/x11egl.c | 7 |
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"); |