summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/context_win.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl/context_win.c')
-rw-r--r--video/out/opengl/context_win.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/video/out/opengl/context_win.c b/video/out/opengl/context_win.c
new file mode 100644
index 0000000..968b176
--- /dev/null
+++ b/video/out/opengl/context_win.c
@@ -0,0 +1,378 @@
+/*
+ * 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 <dwmapi.h>
+
+#include "options/m_config.h"
+#include "video/out/w32_common.h"
+#include "context.h"
+#include "utils.h"
+
+#if !defined(WGL_CONTEXT_MAJOR_VERSION_ARB)
+/* these are supposed to be defined in wingdi.h but mingw's is too old */
+/* only the bits actually used by mplayer are defined */
+/* reference: http://www.opengl.org/registry/specs/ARB/wgl_create_context.txt */
+
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#endif
+
+struct wingl_opts {
+ int wingl_dwm_flush;
+};
+
+#define OPT_BASE_STRUCT struct wingl_opts
+const struct m_sub_options wingl_conf = {
+ .opts = (const struct m_option[]) {
+ {"opengl-dwmflush", OPT_CHOICE(wingl_dwm_flush,
+ {"no", -1}, {"auto", 0}, {"windowed", 1}, {"yes", 2})},
+ {0}
+ },
+ .size = sizeof(struct wingl_opts),
+};
+
+struct priv {
+ GL gl;
+
+ int opt_swapinterval;
+ int current_swapinterval;
+
+ int (GLAPIENTRY *real_wglSwapInterval)(int);
+ struct m_config_cache *opts_cache;
+ struct wingl_opts *opts;
+
+ HGLRC context;
+ HDC hdc;
+};
+
+static void wgl_uninit(struct ra_ctx *ctx);
+
+static __thread struct priv *current_wgl_context;
+
+static int GLAPIENTRY wgl_swap_interval(int interval)
+{
+ if (current_wgl_context)
+ current_wgl_context->opt_swapinterval = interval;
+ return 0;
+}
+
+static bool create_dc(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ HWND win = vo_w32_hwnd(ctx->vo);
+
+ if (p->hdc)
+ return true;
+
+ HDC hdc = GetDC(win);
+ if (!hdc)
+ return false;
+
+ PIXELFORMATDESCRIPTOR pfd;
+ memset(&pfd, 0, sizeof pfd);
+ pfd.nSize = sizeof pfd;
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = 24;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+ int pf = ChoosePixelFormat(hdc, &pfd);
+
+ if (!pf) {
+ MP_ERR(ctx->vo, "unable to select a valid pixel format!\n");
+ ReleaseDC(win, hdc);
+ return false;
+ }
+
+ SetPixelFormat(hdc, pf, &pfd);
+
+ p->hdc = hdc;
+ return true;
+}
+
+static void *wglgpa(const GLubyte *procName)
+{
+ HMODULE oglmod;
+ void *res = wglGetProcAddress(procName);
+ if (res)
+ return res;
+ oglmod = GetModuleHandle(L"opengl32.dll");
+ return GetProcAddress(oglmod, procName);
+}
+
+static bool create_context_wgl_old(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ HDC windc = p->hdc;
+ bool res = false;
+
+ HGLRC context = wglCreateContext(windc);
+ if (!context) {
+ MP_FATAL(ctx->vo, "Could not create GL context!\n");
+ return res;
+ }
+
+ if (!wglMakeCurrent(windc, context)) {
+ MP_FATAL(ctx->vo, "Could not set GL context!\n");
+ wglDeleteContext(context);
+ return res;
+ }
+
+ p->context = context;
+ return true;
+}
+
+static bool create_context_wgl_gl3(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ HDC windc = p->hdc;
+ HGLRC context = 0;
+
+ // A legacy context is needed to get access to the new functions.
+ HGLRC legacy_context = wglCreateContext(windc);
+ if (!legacy_context) {
+ MP_FATAL(ctx->vo, "Could not create GL context!\n");
+ return false;
+ }
+
+ // set context
+ if (!wglMakeCurrent(windc, legacy_context)) {
+ MP_FATAL(ctx->vo, "Could not set GL context!\n");
+ goto out;
+ }
+
+ const char *(GLAPIENTRY *wglGetExtensionsStringARB)(HDC hdc)
+ = wglgpa((const GLubyte*)"wglGetExtensionsStringARB");
+
+ if (!wglGetExtensionsStringARB)
+ goto unsupported;
+
+ const char *wgl_exts = wglGetExtensionsStringARB(windc);
+ if (!gl_check_extension(wgl_exts, "WGL_ARB_create_context"))
+ goto unsupported;
+
+ HGLRC (GLAPIENTRY *wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext,
+ const int *attribList)
+ = wglgpa((const GLubyte*)"wglCreateContextAttribsARB");
+
+ if (!wglCreateContextAttribsARB)
+ goto unsupported;
+
+ 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
+ };
+
+ context = wglCreateContextAttribsARB(windc, 0, attribs);
+ if (!context) {
+ // 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;
+ context = wglCreateContextAttribsARB(windc, 0, attribs);
+ }
+ if (!context) {
+ int err = GetLastError();
+ MP_FATAL(ctx->vo, "Could not create an OpenGL 3.x context: error 0x%x\n", err);
+ goto out;
+ }
+
+ wglMakeCurrent(windc, NULL);
+ wglDeleteContext(legacy_context);
+
+ if (!wglMakeCurrent(windc, context)) {
+ MP_FATAL(ctx->vo, "Could not set GL3 context!\n");
+ wglDeleteContext(context);
+ return false;
+ }
+
+ p->context = context;
+ return true;
+
+unsupported:
+ MP_ERR(ctx->vo, "The OpenGL driver does not support OpenGL 3.x \n");
+out:
+ wglMakeCurrent(windc, NULL);
+ wglDeleteContext(legacy_context);
+ return false;
+}
+
+static void create_ctx(void *ptr)
+{
+ struct ra_ctx *ctx = ptr;
+ struct priv *p = ctx->priv;
+
+ if (!create_dc(ctx))
+ return;
+
+ create_context_wgl_gl3(ctx);
+ if (!p->context)
+ create_context_wgl_old(ctx);
+
+ wglMakeCurrent(p->hdc, NULL);
+}
+
+static bool compositor_active(struct ra_ctx *ctx)
+{
+ // For Windows 7.
+ BOOL enabled = 0;
+ if (FAILED(DwmIsCompositionEnabled(&enabled)) || !enabled)
+ return false;
+
+ // This works at least on Windows 8.1: it returns an error in fullscreen,
+ // which is also when we get consistent timings without DwmFlush. Might
+ // be cargo-cult.
+ DWM_TIMING_INFO info = { .cbSize = sizeof(DWM_TIMING_INFO) };
+ if (FAILED(DwmGetCompositionTimingInfo(0, &info)))
+ return false;
+
+ return true;
+}
+
+static void wgl_swap_buffers(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ SwapBuffers(p->hdc);
+
+ // default if we don't DwmFLush
+ int new_swapinterval = p->opt_swapinterval;
+
+ if (p->opts->wingl_dwm_flush >= 0) {
+ if ((p->opts->wingl_dwm_flush == 1 && !ctx->vo->opts->fullscreen) ||
+ (p->opts->wingl_dwm_flush == 2) ||
+ (p->opts->wingl_dwm_flush == 0 && compositor_active(ctx)))
+ {
+ if (DwmFlush() == S_OK)
+ new_swapinterval = 0;
+ }
+ }
+
+ if (new_swapinterval != p->current_swapinterval &&
+ p->real_wglSwapInterval)
+ {
+ p->real_wglSwapInterval(new_swapinterval);
+ MP_VERBOSE(ctx->vo, "set SwapInterval(%d)\n", new_swapinterval);
+ }
+ p->current_swapinterval = new_swapinterval;
+}
+
+static bool wgl_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
+ GL *gl = &p->gl;
+
+ p->opts_cache = m_config_cache_alloc(ctx, ctx->global, &wingl_conf);
+ p->opts = p->opts_cache->opts;
+
+ if (!vo_w32_init(ctx->vo))
+ goto fail;
+
+ vo_w32_run_on_thread(ctx->vo, create_ctx, ctx);
+ if (!p->context)
+ goto fail;
+
+ current_wgl_context = p;
+ wglMakeCurrent(p->hdc, p->context);
+
+ mpgl_load_functions(gl, wglgpa, NULL, ctx->vo->log);
+
+ if (!gl->SwapInterval)
+ MP_VERBOSE(ctx->vo, "WGL_EXT_swap_control missing.\n");
+ p->real_wglSwapInterval = gl->SwapInterval;
+ gl->SwapInterval = wgl_swap_interval;
+ p->current_swapinterval = -1;
+
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = wgl_swap_buffers,
+ };
+
+ if (!ra_gl_ctx_init(ctx, gl, params))
+ goto fail;
+
+ DwmEnableMMCSS(TRUE);
+ return true;
+
+fail:
+ wgl_uninit(ctx);
+ return false;
+}
+
+static void resize(struct ra_ctx *ctx)
+{
+ ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
+}
+
+static bool wgl_reconfig(struct ra_ctx *ctx)
+{
+ vo_w32_config(ctx->vo);
+ resize(ctx);
+ return true;
+}
+
+static void destroy_gl(void *ptr)
+{
+ struct ra_ctx *ctx = ptr;
+ struct priv *p = ctx->priv;
+ if (p->context)
+ wglDeleteContext(p->context);
+ p->context = 0;
+ if (p->hdc)
+ ReleaseDC(vo_w32_hwnd(ctx->vo), p->hdc);
+ p->hdc = NULL;
+ current_wgl_context = NULL;
+}
+
+static void wgl_uninit(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
+ if (p->context)
+ wglMakeCurrent(p->hdc, 0);
+ vo_w32_run_on_thread(ctx->vo, destroy_gl, ctx);
+
+ DwmEnableMMCSS(FALSE);
+ vo_w32_uninit(ctx->vo);
+}
+
+static int wgl_control(struct ra_ctx *ctx, int *events, int request, void *arg)
+{
+ int ret = vo_w32_control(ctx->vo, events, request, arg);
+ if (*events & VO_EVENT_RESIZE)
+ resize(ctx);
+ return ret;
+}
+
+const struct ra_ctx_fns ra_ctx_wgl = {
+ .type = "opengl",
+ .name = "win",
+ .init = wgl_init,
+ .reconfig = wgl_reconfig,
+ .control = wgl_control,
+ .uninit = wgl_uninit,
+};