From e76ec2c96300c828b2a96ae71199128295f8451b Mon Sep 17 00:00:00 2001 From: James Ross-Gowan Date: Sat, 14 Nov 2015 00:04:30 +1100 Subject: [PATCH] vo_opengl: add initial ANGLE support ANGLE is a GLES2 implementation for Windows that uses Direct3D 11 for rendering, enabling vo_opengl to work on systems with poor OpenGL drivers and bypassing some of the problems with native GL, such as VSync in fullscreen mode. Unfortunately, using GLES2 means that most of vo_opengl's advanced features will not work, however ANGLE is under rapid development and GLES3 support is supposed to be coming soon. --- video/out/opengl/angle.c | 215 ++++++++++++++++++++++++++++++++++++++ video/out/opengl/common.c | 4 + wscript | 8 ++ wscript_build.py | 1 + 4 files changed, 228 insertions(+) create mode 100644 video/out/opengl/angle.c diff --git a/video/out/opengl/angle.c b/video/out/opengl/angle.c new file mode 100644 index 0000000000..7aac2bd29d --- /dev/null +++ b/video/out/opengl/angle.c @@ -0,0 +1,215 @@ +/* + * 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 . + * + * 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 +#include +#include + +#include "common/common.h" +#include "video/out/w32_common.h" +#include "common.h" + +struct priv { + EGLDisplay egl_display; + EGLContext egl_context; + EGLSurface egl_surface; + HMODULE libglesv2; +}; + +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; + + if (p->libglesv2) + FreeLibrary(p->libglesv2); + + 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, + EGLNativeWindowType window) +{ + struct priv *p = ctx->priv; + + EGLint context_attributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + p->egl_surface = eglCreateWindowSurface(p->egl_display, config, window, NULL); + + if (p->egl_surface == EGL_NO_SURFACE) { + MP_FATAL(ctx->vo, "Could not create EGL surface!\n"); + return false; + } + + p->egl_context = eglCreateContext(p->egl_display, config, + EGL_NO_CONTEXT, context_attributes); + + if (p->egl_context == EGL_NO_CONTEXT) { + MP_FATAL(ctx->vo, "Could not create EGL context!\n"); + 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) +{ + void *res = eglGetProcAddress(proc_name); + if (res) + return res; + + // ANGLE's eglGetProcAddress only works for extensions + return GetProcAddress(GetModuleHandleW(L"libGLESv2.dll"), 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; + + p->libglesv2 = LoadLibraryW(L"libGLESv2.dll"); + if (!p->libglesv2) { + MP_FATAL(vo, "Couldn't load GLES functions\n"); + 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 display_attributes[] = { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + 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) { + 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; + + if (!create_context_egl(ctx, config, vo_w32_hwnd(vo))) + 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 3d24f9818f..660b4b2717 100644 --- a/video/out/opengl/common.c +++ b/video/out/opengl/common.c @@ -509,6 +509,7 @@ 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_rpi; static const struct mpgl_driver *const backends[] = { @@ -521,6 +522,9 @@ static const struct mpgl_driver *const backends[] = { #if HAVE_GL_WIN32 &mpgl_driver_w32, #endif +#if HAVE_EGL_ANGLE + &mpgl_driver_angle, +#endif #if HAVE_GL_WAYLAND &mpgl_driver_wayland, #endif diff --git a/wscript b/wscript index 858dcc901c..0f113002ba 100644 --- a/wscript +++ b/wscript @@ -658,6 +658,14 @@ video_output_features = [ 'groups': [ 'gl' ], 'func': check_statement('windows.h', 'wglCreateContext(0)', lib='opengl32') + } , { + 'name': '--egl-angle', + 'desc': 'OpenGL Win32 ANGLE Backend', + 'deps_any': [ 'os-win32', 'os-cygwin' ], + 'groups': [ 'gl' ], + 'func': check_statement(['EGL/egl.h'], + 'eglCreateWindowSurface(0, 0, 0, 0)', + lib='EGL') } , { 'name': '--vdpau', 'desc': 'VDPAU acceleration', diff --git a/wscript_build.py b/wscript_build.py index 76b3929c95..6829554c17 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -334,6 +334,7 @@ def build(ctx): ( "video/out/opengl/video.c", "gl" ), ( "video/out/opengl/video_shaders.c", "gl" ), ( "video/out/opengl/w32.c", "gl-win32" ), + ( "video/out/opengl/angle.c", "egl-angle" ), ( "video/out/opengl/wayland.c", "gl-wayland" ), ( "video/out/opengl/x11.c", "gl-x11" ), ( "video/out/opengl/x11egl.c", "egl-x11" ),