summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/gl_w32.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl/gl_w32.c')
-rw-r--r--video/out/opengl/gl_w32.c302
1 files changed, 302 insertions, 0 deletions
diff --git a/video/out/opengl/gl_w32.c b/video/out/opengl/gl_w32.c
new file mode 100644
index 0000000000..0c1fed7bfa
--- /dev/null
+++ b/video/out/opengl/gl_w32.c
@@ -0,0 +1,302 @@
+/*
+ * 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 <assert.h>
+#include <windows.h>
+#include "video/out/w32_common.h"
+#include "gl_common.h"
+
+typedef HRESULT (WINAPI *DwmFlush_t)(void);
+
+struct w32_context {
+ HGLRC context;
+ HDC hdc;
+ int flags;
+
+ HINSTANCE dwmapi_dll;
+ DwmFlush_t dwmflush;
+};
+
+static bool create_dc(struct MPGLContext *ctx, int flags)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+ HWND win = vo_w32_hwnd(ctx->vo);
+
+ if (w32_ctx->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);
+
+ int pfmt = GetPixelFormat(hdc);
+ if (DescribePixelFormat(hdc, pfmt, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) {
+ ctx->depth_r = pfd.cRedBits;
+ ctx->depth_g = pfd.cGreenBits;
+ ctx->depth_b = pfd.cBlueBits;
+ }
+
+ w32_ctx->hdc = hdc;
+ return true;
+}
+
+static void *w32gpa(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_w32_old(struct MPGLContext *ctx)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+
+ HDC windc = w32_ctx->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;
+ }
+
+ w32_ctx->context = context;
+
+ mpgl_load_functions(ctx->gl, w32gpa, NULL, ctx->vo->log);
+ return true;
+}
+
+static bool create_context_w32_gl3(struct MPGLContext *ctx)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+
+ HDC windc = w32_ctx->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)
+ = w32gpa((const GLubyte*)"wglGetExtensionsStringARB");
+
+ if (!wglGetExtensionsStringARB)
+ goto unsupported;
+
+ const char *wgl_exts = wglGetExtensionsStringARB(windc);
+ if (!strstr(wgl_exts, "WGL_ARB_create_context"))
+ goto unsupported;
+
+ HGLRC (GLAPIENTRY *wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext,
+ const int *attribList)
+ = w32gpa((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;
+ }
+
+ w32_ctx->context = context;
+
+ /* update function pointers */
+ mpgl_load_functions(ctx->gl, w32gpa, NULL, ctx->vo->log);
+
+ 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 MPGLContext *ctx = ptr;
+ struct w32_context *w32_ctx = ctx->priv;
+
+ if (!create_dc(ctx, w32_ctx->flags))
+ return;
+
+ create_context_w32_gl3(ctx);
+ if (!w32_ctx->context)
+ create_context_w32_old(ctx);
+
+ w32_ctx->dwmapi_dll = LoadLibrary(L"Dwmapi.dll");
+ if (w32_ctx->dwmapi_dll)
+ w32_ctx->dwmflush = (DwmFlush_t)GetProcAddress(w32_ctx->dwmapi_dll, "DwmFlush");
+
+ wglMakeCurrent(w32_ctx->hdc, NULL);
+}
+
+static bool config_window_w32(struct MPGLContext *ctx, int flags)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+ if (!vo_w32_config(ctx->vo, flags))
+ return false;
+
+ if (w32_ctx->context) // reuse existing context
+ return true;
+
+ w32_ctx->flags = flags;
+ vo_w32_run_on_thread(ctx->vo, create_ctx, ctx);
+
+ if (w32_ctx->context)
+ wglMakeCurrent(w32_ctx->hdc, w32_ctx->context);
+ return !!w32_ctx->context;
+}
+
+static void destroy_gl(void *ptr)
+{
+ struct MPGLContext *ctx = ptr;
+ struct w32_context *w32_ctx = ctx->priv;
+ if (w32_ctx->context)
+ wglDeleteContext(w32_ctx->context);
+ w32_ctx->context = 0;
+ if (w32_ctx->hdc)
+ ReleaseDC(vo_w32_hwnd(ctx->vo), w32_ctx->hdc);
+ w32_ctx->hdc = NULL;
+}
+
+static void releaseGlContext_w32(MPGLContext *ctx)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+ if (w32_ctx->context)
+ wglMakeCurrent(w32_ctx->hdc, 0);
+ vo_w32_run_on_thread(ctx->vo, destroy_gl, ctx);
+
+ w32_ctx->dwmflush = NULL;
+ if (w32_ctx->dwmapi_dll)
+ FreeLibrary(w32_ctx->dwmapi_dll);
+ w32_ctx->dwmapi_dll = NULL;
+}
+
+static void swapGlBuffers_w32(MPGLContext *ctx)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+ SwapBuffers(w32_ctx->hdc);
+}
+
+// opt_dwmflush: 0 - never DwmFlush, 1 - only in windowed mode, 2 - always
+// return: the current (applied if modified) SwapInterval value.
+// DwmFlush waits on DWM vsync similar to SwapBuffers but a bit more noisy.
+// SwapBuffers still needs to be called, but we SwapInterval(0) when DwmFLush is
+// used (will get applied for the following SwapBuffers calls)
+static int DwmFlush_w32(MPGLContext *ctx, int opt_dwmflush,
+ int opt_swapinterval, int current_swapinterval)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+ int new_swapinterval = opt_swapinterval; // default if we don't DwmFLush
+
+ if (w32_ctx->dwmflush &&
+ (opt_dwmflush == 2 || (opt_dwmflush == 1 && !ctx->vo->opts->fullscreen)) &&
+ S_OK == w32_ctx->dwmflush())
+ {
+ new_swapinterval = 0;
+ }
+
+ if ((new_swapinterval != current_swapinterval) && ctx->gl->SwapInterval) {
+ ctx->gl->SwapInterval(new_swapinterval);
+ MP_VERBOSE(ctx->vo, "DwmFlush: set SwapInterval(%d)\n", new_swapinterval);
+ }
+
+ return new_swapinterval;
+}
+
+void mpgl_set_backend_w32(MPGLContext *ctx)
+{
+ ctx->priv = talloc_zero(ctx, struct w32_context);
+ ctx->config_window = config_window_w32;
+ ctx->releaseGlContext = releaseGlContext_w32;
+ ctx->swapGlBuffers = swapGlBuffers_w32;
+ ctx->vo_init = vo_w32_init;
+ ctx->vo_uninit = vo_w32_uninit;
+ ctx->vo_control = vo_w32_control;
+ ctx->DwmFlush = DwmFlush_w32;
+}