From 4cc1861378c3b1e989b5d92ea49e3d02f5e4a65a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 19 Dec 2015 13:05:10 +0100 Subject: vo_opengl: prefix per-backend source files with context_ --- video/out/opengl/angle.c | 208 ----------- video/out/opengl/cocoa.c | 181 ---------- video/out/opengl/context_angle.c | 208 +++++++++++ video/out/opengl/context_cocoa.c | 181 ++++++++++ video/out/opengl/context_drm_egl.c | 439 +++++++++++++++++++++++ video/out/opengl/context_dxinterop.c | 650 +++++++++++++++++++++++++++++++++++ video/out/opengl/context_rpi.c | 244 +++++++++++++ video/out/opengl/context_rpi.h | 20 ++ video/out/opengl/context_w32.c | 349 +++++++++++++++++++ video/out/opengl/context_wayland.c | 237 +++++++++++++ video/out/opengl/context_x11.c | 331 ++++++++++++++++++ video/out/opengl/context_x11egl.c | 197 +++++++++++ video/out/opengl/drm_egl.c | 439 ----------------------- video/out/opengl/dxinterop.c | 650 ----------------------------------- video/out/opengl/rpi.c | 244 ------------- video/out/opengl/rpi.h | 20 -- video/out/opengl/w32.c | 349 ------------------- video/out/opengl/wayland.c | 237 ------------- video/out/opengl/x11.c | 331 ------------------ video/out/opengl/x11egl.c | 197 ----------- video/out/vo_rpi.c | 2 +- 21 files changed, 2857 insertions(+), 2857 deletions(-) delete mode 100644 video/out/opengl/angle.c delete mode 100644 video/out/opengl/cocoa.c create mode 100644 video/out/opengl/context_angle.c create mode 100644 video/out/opengl/context_cocoa.c create mode 100644 video/out/opengl/context_drm_egl.c create mode 100644 video/out/opengl/context_dxinterop.c create mode 100644 video/out/opengl/context_rpi.c create mode 100644 video/out/opengl/context_rpi.h create mode 100644 video/out/opengl/context_w32.c create mode 100644 video/out/opengl/context_wayland.c create mode 100644 video/out/opengl/context_x11.c create mode 100644 video/out/opengl/context_x11egl.c delete mode 100644 video/out/opengl/drm_egl.c delete mode 100644 video/out/opengl/dxinterop.c delete mode 100644 video/out/opengl/rpi.c delete mode 100644 video/out/opengl/rpi.h delete mode 100644 video/out/opengl/w32.c delete mode 100644 video/out/opengl/wayland.c delete mode 100644 video/out/opengl/x11.c delete mode 100644 video/out/opengl/x11egl.c (limited to 'video') diff --git a/video/out/opengl/angle.c b/video/out/opengl/angle.c deleted file mode 100644 index 61eb6af61b..0000000000 --- a/video/out/opengl/angle.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 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 "context.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/cocoa.c b/video/out/opengl/cocoa.c deleted file mode 100644 index 1bcfa09755..0000000000 --- a/video/out/opengl/cocoa.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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 "video/out/cocoa_common.h" -#include "osdep/macosx_versions.h" -#include "context.h" - -struct cgl_context { - CGLPixelFormatObj pix; - CGLContextObj ctx; -}; - -static int set_swap_interval(int enabled) -{ - CGLContextObj ctx = CGLGetCurrentContext(); - CGLError err = CGLSetParameter(ctx, kCGLCPSwapInterval, &enabled); - return (err == kCGLNoError) ? 0 : -1; -} - -static int cgl_color_size(struct MPGLContext *ctx) -{ - struct cgl_context *p = ctx->priv; - GLint value; - CGLDescribePixelFormat(p->pix, 0, kCGLPFAColorSize, &value); - return value > 16 ? 8 : 5; -} - -static void *cocoa_glgetaddr(const char *s) -{ - void *ret = NULL; - void *handle = dlopen( - "/System/Library/Frameworks/OpenGL.framework/OpenGL", - RTLD_LAZY | RTLD_LOCAL); - if (!handle) - return NULL; - ret = dlsym(handle, s); - dlclose(handle); - return ret; -} - -static CGLError test_gl_version(struct vo *vo, - CGLContextObj *ctx, - CGLPixelFormatObj *pix, - CGLOpenGLProfile version) -{ - CGLPixelFormatAttribute attrs[] = { - kCGLPFAOpenGLProfile, - (CGLPixelFormatAttribute) version, - kCGLPFADoubleBuffer, - kCGLPFAAccelerated, - #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8 - // leave this as the last entry of the array to not break the fallback - // code - kCGLPFASupportsAutomaticGraphicsSwitching, - #endif - 0 - }; - - GLint npix; - CGLError err; - err = CGLChoosePixelFormat(attrs, pix, &npix); - if (err == kCGLBadAttribute) { - // kCGLPFASupportsAutomaticGraphicsSwitching is probably not supported - // by the current hardware. Falling back to not using it. - attrs[MP_ARRAY_SIZE(attrs) - 2] = 0; - err = CGLChoosePixelFormat(attrs, pix, &npix); - } - - if (err != kCGLNoError) { - MP_ERR(vo, "error creating CGL pixel format: %s (%d)\n", - CGLErrorString(err), err); - goto error_out; - } - - err = CGLCreateContext(*pix, 0, ctx); - -error_out: - return err; -} - -static bool create_gl_context(struct MPGLContext *ctx, int vo_flags) -{ - struct cgl_context *p = ctx->priv; - CGLError err; - - CGLOpenGLProfile gl_versions[] = { - kCGLOGLPVersion_3_2_Core, - kCGLOGLPVersion_Legacy, - }; - - for (int n = 0; n < MP_ARRAY_SIZE(gl_versions); n++) { - err = test_gl_version(ctx->vo, &p->ctx, &p->pix, gl_versions[n]); - if (err == kCGLNoError) - break; - } - - if (err != kCGLNoError) { - MP_FATAL(ctx->vo, "error creating CGL context: %s (%d)\n", - CGLErrorString(err), err); - return false; - } - - vo_cocoa_set_opengl_ctx(ctx->vo, p->ctx); - CGLSetCurrentContext(p->ctx); - - if (vo_flags & VOFLAG_ALPHA) - CGLSetParameter(p->ctx, kCGLCPSurfaceOpacity, &(GLint){0}); - - mpgl_load_functions(ctx->gl, (void *)cocoa_glgetaddr, NULL, ctx->vo->log); - ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = cgl_color_size(ctx); - - CGLReleasePixelFormat(p->pix); - - return true; -} - -static void cocoa_uninit(MPGLContext *ctx) -{ - struct cgl_context *p = ctx->priv; - CGLReleaseContext(p->ctx); - vo_cocoa_uninit(ctx->vo); -} - -static int cocoa_init(MPGLContext *ctx, int vo_flags) -{ - vo_cocoa_init(ctx->vo); - - if (!create_gl_context(ctx, vo_flags)) - return -1; - - ctx->gl->SwapInterval = set_swap_interval; - return 0; -} - -static int cocoa_reconfig(struct MPGLContext *ctx) -{ - vo_cocoa_config_window(ctx->vo); - return 0; -} - -static int cocoa_control(struct MPGLContext *ctx, int *events, int request, - void *arg) -{ - return vo_cocoa_control(ctx->vo, events, request, arg); -} - -static void cocoa_swap_buffers(struct MPGLContext *ctx) -{ - vo_cocoa_swap_buffers(ctx->vo); -} - -const struct mpgl_driver mpgl_driver_cocoa = { - .name = "cocoa", - .priv_size = sizeof(struct cgl_context), - .init = cocoa_init, - .reconfig = cocoa_reconfig, - .swap_buffers = cocoa_swap_buffers, - .control = cocoa_control, - .uninit = cocoa_uninit, -}; \ No newline at end of file diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c new file mode 100644 index 0000000000..61eb6af61b --- /dev/null +++ b/video/out/opengl/context_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 . + * + * 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 "context.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/context_cocoa.c b/video/out/opengl/context_cocoa.c new file mode 100644 index 0000000000..1bcfa09755 --- /dev/null +++ b/video/out/opengl/context_cocoa.c @@ -0,0 +1,181 @@ +/* + * 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 "video/out/cocoa_common.h" +#include "osdep/macosx_versions.h" +#include "context.h" + +struct cgl_context { + CGLPixelFormatObj pix; + CGLContextObj ctx; +}; + +static int set_swap_interval(int enabled) +{ + CGLContextObj ctx = CGLGetCurrentContext(); + CGLError err = CGLSetParameter(ctx, kCGLCPSwapInterval, &enabled); + return (err == kCGLNoError) ? 0 : -1; +} + +static int cgl_color_size(struct MPGLContext *ctx) +{ + struct cgl_context *p = ctx->priv; + GLint value; + CGLDescribePixelFormat(p->pix, 0, kCGLPFAColorSize, &value); + return value > 16 ? 8 : 5; +} + +static void *cocoa_glgetaddr(const char *s) +{ + void *ret = NULL; + void *handle = dlopen( + "/System/Library/Frameworks/OpenGL.framework/OpenGL", + RTLD_LAZY | RTLD_LOCAL); + if (!handle) + return NULL; + ret = dlsym(handle, s); + dlclose(handle); + return ret; +} + +static CGLError test_gl_version(struct vo *vo, + CGLContextObj *ctx, + CGLPixelFormatObj *pix, + CGLOpenGLProfile version) +{ + CGLPixelFormatAttribute attrs[] = { + kCGLPFAOpenGLProfile, + (CGLPixelFormatAttribute) version, + kCGLPFADoubleBuffer, + kCGLPFAAccelerated, + #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8 + // leave this as the last entry of the array to not break the fallback + // code + kCGLPFASupportsAutomaticGraphicsSwitching, + #endif + 0 + }; + + GLint npix; + CGLError err; + err = CGLChoosePixelFormat(attrs, pix, &npix); + if (err == kCGLBadAttribute) { + // kCGLPFASupportsAutomaticGraphicsSwitching is probably not supported + // by the current hardware. Falling back to not using it. + attrs[MP_ARRAY_SIZE(attrs) - 2] = 0; + err = CGLChoosePixelFormat(attrs, pix, &npix); + } + + if (err != kCGLNoError) { + MP_ERR(vo, "error creating CGL pixel format: %s (%d)\n", + CGLErrorString(err), err); + goto error_out; + } + + err = CGLCreateContext(*pix, 0, ctx); + +error_out: + return err; +} + +static bool create_gl_context(struct MPGLContext *ctx, int vo_flags) +{ + struct cgl_context *p = ctx->priv; + CGLError err; + + CGLOpenGLProfile gl_versions[] = { + kCGLOGLPVersion_3_2_Core, + kCGLOGLPVersion_Legacy, + }; + + for (int n = 0; n < MP_ARRAY_SIZE(gl_versions); n++) { + err = test_gl_version(ctx->vo, &p->ctx, &p->pix, gl_versions[n]); + if (err == kCGLNoError) + break; + } + + if (err != kCGLNoError) { + MP_FATAL(ctx->vo, "error creating CGL context: %s (%d)\n", + CGLErrorString(err), err); + return false; + } + + vo_cocoa_set_opengl_ctx(ctx->vo, p->ctx); + CGLSetCurrentContext(p->ctx); + + if (vo_flags & VOFLAG_ALPHA) + CGLSetParameter(p->ctx, kCGLCPSurfaceOpacity, &(GLint){0}); + + mpgl_load_functions(ctx->gl, (void *)cocoa_glgetaddr, NULL, ctx->vo->log); + ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = cgl_color_size(ctx); + + CGLReleasePixelFormat(p->pix); + + return true; +} + +static void cocoa_uninit(MPGLContext *ctx) +{ + struct cgl_context *p = ctx->priv; + CGLReleaseContext(p->ctx); + vo_cocoa_uninit(ctx->vo); +} + +static int cocoa_init(MPGLContext *ctx, int vo_flags) +{ + vo_cocoa_init(ctx->vo); + + if (!create_gl_context(ctx, vo_flags)) + return -1; + + ctx->gl->SwapInterval = set_swap_interval; + return 0; +} + +static int cocoa_reconfig(struct MPGLContext *ctx) +{ + vo_cocoa_config_window(ctx->vo); + return 0; +} + +static int cocoa_control(struct MPGLContext *ctx, int *events, int request, + void *arg) +{ + return vo_cocoa_control(ctx->vo, events, request, arg); +} + +static void cocoa_swap_buffers(struct MPGLContext *ctx) +{ + vo_cocoa_swap_buffers(ctx->vo); +} + +const struct mpgl_driver mpgl_driver_cocoa = { + .name = "cocoa", + .priv_size = sizeof(struct cgl_context), + .init = cocoa_init, + .reconfig = cocoa_reconfig, + .swap_buffers = cocoa_swap_buffers, + .control = cocoa_control, + .uninit = cocoa_uninit, +}; \ No newline at end of file diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c new file mode 100644 index 0000000000..34029a1a34 --- /dev/null +++ b/video/out/opengl/context_drm_egl.c @@ -0,0 +1,439 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "context.h" +#include "common/common.h" +#include "video/out/drm_common.h" + +#define USE_MASTER 0 + +struct framebuffer +{ + struct gbm_bo *bo; + int width, height; + int fd; + int id; +}; + +struct gbm +{ + struct gbm_surface *surface; + struct gbm_device *device; + struct gbm_bo *bo; + struct gbm_bo *next_bo; +}; + +struct egl +{ + EGLDisplay display; + EGLContext context; + EGLSurface surface; +}; + +struct priv { + struct kms *kms; + + drmEventContext ev; + drmModeCrtc *old_crtc; + + struct egl egl; + struct gbm gbm; + struct framebuffer fb; + + bool active; + bool waiting_for_flip; + + bool vt_switcher_active; + struct vt_switcher vt_switcher; +}; + +static EGLConfig select_fb_config_egl(struct MPGLContext *ctx, bool es) +{ + struct priv *p = ctx->priv; + const EGLint attributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 1, + EGL_RENDERABLE_TYPE, es ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT, + EGL_NONE + }; + EGLint config_count; + EGLConfig config; + if (!eglChooseConfig(p->egl.display, attributes, &config, 1, &config_count)) { + MP_FATAL(ctx->vo, "Failed to configure EGL.\n"); + return NULL; + } + if (!config_count) { + MP_FATAL(ctx->vo, "Could not find EGL configuration!\n"); + return NULL; + } + return config; +} + +static bool init_egl(struct MPGLContext *ctx, bool es) +{ + struct priv *p = ctx->priv; + MP_VERBOSE(ctx->vo, "Initializing EGL\n"); + p->egl.display = eglGetDisplay(p->gbm.device); + if (p->egl.display == EGL_NO_DISPLAY) { + MP_ERR(ctx->vo, "Failed to get EGL display.\n"); + return false; + } + if (!eglInitialize(p->egl.display, NULL, NULL)) { + MP_ERR(ctx->vo, "Failed to initialize EGL.\n"); + return false; + } + if (!eglBindAPI(es ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) { + MP_ERR(ctx->vo, "Failed to set EGL API version.\n"); + return false; + } + EGLConfig config = select_fb_config_egl(ctx, es); + if (!config) { + MP_ERR(ctx->vo, "Failed to configure EGL.\n"); + return false; + } + p->egl.context = eglCreateContext(p->egl.display, config, EGL_NO_CONTEXT, NULL); + if (!p->egl.context) { + MP_ERR(ctx->vo, "Failed to create EGL context.\n"); + return false; + } + MP_VERBOSE(ctx->vo, "Initializing EGL surface\n"); + p->egl.surface = eglCreateWindowSurface(p->egl.display, config, p->gbm.surface, NULL); + if (p->egl.surface == EGL_NO_SURFACE) { + MP_ERR(ctx->vo, "Failed to create EGL surface.\n"); + return false; + } + return true; +} + +static bool init_gbm(struct MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + MP_VERBOSE(ctx->vo, "Creating GBM device\n"); + p->gbm.device = gbm_create_device(p->kms->fd); + if (!p->gbm.device) { + MP_ERR(ctx->vo, "Failed to create GBM device.\n"); + return false; + } + + MP_VERBOSE(ctx->vo, "Initializing GBM surface (%d x %d)\n", + p->kms->mode.hdisplay, p->kms->mode.vdisplay); + p->gbm.surface = gbm_surface_create( + p->gbm.device, + p->kms->mode.hdisplay, + p->kms->mode.vdisplay, + GBM_BO_FORMAT_XRGB8888, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (!p->gbm.surface) { + MP_ERR(ctx->vo, "Failed to create GBM surface.\n"); + return false; + } + return true; +} + +static void framebuffer_destroy_callback(struct gbm_bo *bo, void *data) +{ + struct framebuffer *fb = data; + if (fb) { + drmModeRmFB(fb->fd, fb->id); + } +} + +static void update_framebuffer_from_bo( + const struct MPGLContext *ctx, struct gbm_bo *bo) +{ + struct priv *p = ctx->priv; + p->fb.bo = bo; + p->fb.fd = p->kms->fd; + p->fb.width = gbm_bo_get_width(bo); + p->fb.height = gbm_bo_get_height(bo); + int stride = gbm_bo_get_stride(bo); + int handle = gbm_bo_get_handle(bo).u32; + + int ret = drmModeAddFB(p->kms->fd, p->fb.width, p->fb.height, + 24, 32, stride, handle, &p->fb.id); + if (ret) { + MP_ERR(ctx->vo, "Failed to create framebuffer: %s\n", mp_strerror(errno)); + } + gbm_bo_set_user_data(bo, &p->fb, framebuffer_destroy_callback); +} + +static void page_flipped(int fd, unsigned int frame, unsigned int sec, + unsigned int usec, void *data) +{ + struct priv *p = data; + p->waiting_for_flip = false; +} + +static bool crtc_setup(struct MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + if (p->active) + return true; + p->old_crtc = drmModeGetCrtc(p->kms->fd, p->kms->crtc_id); + int ret = drmModeSetCrtc(p->kms->fd, p->kms->crtc_id, + p->fb.id, + 0, + 0, + &p->kms->connector->connector_id, + 1, + &p->kms->mode); + p->active = true; + return ret == 0; +} + +static void crtc_release(struct MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + + if (!p->active) + return; + p->active = false; + + // wait for current page flip + while (p->waiting_for_flip) { + int ret = drmHandleEvent(p->kms->fd, &p->ev); + if (ret) { + MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret); + break; + } + } + + if (p->old_crtc) { + drmModeSetCrtc(p->kms->fd, + p->old_crtc->crtc_id, + p->old_crtc->buffer_id, + p->old_crtc->x, + p->old_crtc->y, + &p->kms->connector->connector_id, + 1, + &p->old_crtc->mode); + drmModeFreeCrtc(p->old_crtc); + p->old_crtc = NULL; + } +} + +static void release_vt(void *data) +{ + struct MPGLContext *ctx = data; + MP_VERBOSE(ctx->vo, "Releasing VT"); + crtc_release(ctx); + if (USE_MASTER) { + //this function enables support for switching to x, weston etc. + //however, for whatever reason, it can be called only by root users. + //until things change, this is commented. + struct priv *p = ctx->priv; + if (drmDropMaster(p->kms->fd)) { + MP_WARN(ctx->vo, "Failed to drop DRM master: %s\n", mp_strerror(errno)); + } + } +} + +static void acquire_vt(void *data) +{ + struct MPGLContext *ctx = data; + MP_VERBOSE(ctx->vo, "Acquiring VT"); + if (USE_MASTER) { + struct priv *p = ctx->priv; + if (drmSetMaster(p->kms->fd)) { + MP_WARN(ctx->vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno)); + } + } + + crtc_setup(ctx); +} + +static void drm_egl_uninit(MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + crtc_release(ctx); + + if (p->vt_switcher_active) + vt_switcher_destroy(&p->vt_switcher); + + eglMakeCurrent(p->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(p->egl.display, p->egl.context); + eglDestroySurface(p->egl.display, p->egl.surface); + gbm_surface_destroy(p->gbm.surface); + eglTerminate(p->egl.display); + gbm_device_destroy(p->gbm.device); + p->egl.context = EGL_NO_CONTEXT; + eglDestroyContext(p->egl.display, p->egl.context); + + if (p->kms) { + kms_destroy(p->kms); + p->kms = 0; + } +} + +static int drm_egl_init(struct MPGLContext *ctx, int flags) +{ + if (ctx->vo->probing) { + MP_VERBOSE(ctx->vo, "DRM EGL backend can be activated only manually.\n"); + return -1; + } + struct priv *p = ctx->priv; + p->kms = NULL; + p->old_crtc = NULL; + p->gbm.surface = NULL; + p->gbm.device = NULL; + p->active = false; + p->waiting_for_flip = false; + p->ev.version = DRM_EVENT_CONTEXT_VERSION; + p->ev.page_flip_handler = page_flipped; + + p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log); + if (p->vt_switcher_active) { + vt_switcher_acquire(&p->vt_switcher, acquire_vt, ctx); + vt_switcher_release(&p->vt_switcher, release_vt, ctx); + } else { + MP_WARN(ctx->vo, "Failed to set up VT switcher. Terminal switching will be unavailable.\n"); + } + + MP_VERBOSE(ctx->vo, "Initializing KMS\n"); + p->kms = kms_create(ctx->vo->log); + if (!p->kms) { + MP_ERR(ctx->vo, "Failed to create KMS.\n"); + return -1; + } + + // TODO: arguments should be configurable + if (!kms_setup(p->kms, "/dev/dri/card0", -1, 0)) { + MP_ERR(ctx->vo, "Failed to configure KMS.\n"); + return -1; + } + + if (!init_gbm(ctx)) { + MP_ERR(ctx->vo, "Failed to setup GBM.\n"); + return -1; + } + + if (!init_egl(ctx, flags & VOFLAG_GLES)) { + MP_ERR(ctx->vo, "Failed to setup EGL.\n"); + return -1; + } + + if (!eglMakeCurrent(p->egl.display, p->egl.surface, p->egl.surface, p->egl.context)) { + MP_ERR(ctx->vo, "Failed to make context current.\n"); + return -1; + } + + const char *egl_exts = eglQueryString(p->egl.display, EGL_EXTENSIONS); + void *(*gpa)(const GLubyte*) = (void *(*)(const GLubyte*))eglGetProcAddress; + mpgl_load_functions(ctx->gl, gpa, egl_exts, ctx->vo->log); + + // required by gbm_surface_lock_front_buffer + eglSwapBuffers(p->egl.display, p->egl.surface); + + MP_VERBOSE(ctx->vo, "Preparing framebuffer\n"); + p->gbm.bo = gbm_surface_lock_front_buffer(p->gbm.surface); + if (!p->gbm.bo) { + MP_ERR(ctx->vo, "Failed to lock GBM surface.\n"); + return -1; + } + update_framebuffer_from_bo(ctx, p->gbm.bo); + if (!p->fb.id) { + MP_ERR(ctx->vo, "Failed to create framebuffer.\n"); + return -1; + } + + if (!crtc_setup(ctx)) { + MP_ERR( + ctx->vo, + "Failed to set CRTC for connector %u: %s\n", + p->kms->connector->connector_id, + mp_strerror(errno)); + return -1; + } + + return 0; +} + +static int drm_egl_reconfig(struct MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + ctx->vo->dwidth = p->fb.width; + ctx->vo->dheight = p->fb.height; + return 0; +} + +static int drm_egl_control(struct MPGLContext *ctx, int *events, int request, + void *arg) +{ + return VO_NOTIMPL; +} + +static void drm_egl_swap_buffers(MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + eglSwapBuffers(p->egl.display, p->egl.surface); + p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface); + p->waiting_for_flip = true; + update_framebuffer_from_bo(ctx, p->gbm.next_bo); + int ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb.id, + DRM_MODE_PAGE_FLIP_EVENT, p); + if (ret) { + MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno)); + } + + // poll page flip finish event + const int timeout_ms = 3000; + struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } }; + poll(fds, 1, timeout_ms); + if (fds[0].revents & POLLIN) { + ret = drmHandleEvent(p->kms->fd, &p->ev); + if (ret != 0) { + MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret); + return; + } + } + + gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo); + p->gbm.bo = p->gbm.next_bo; +} + +const struct mpgl_driver mpgl_driver_drm_egl = { + .name = "drm-egl", + .priv_size = sizeof(struct priv), + .init = drm_egl_init, + .reconfig = drm_egl_reconfig, + .swap_buffers = drm_egl_swap_buffers, + .control = drm_egl_control, + .uninit = drm_egl_uninit, +}; diff --git a/video/out/opengl/context_dxinterop.c b/video/out/opengl/context_dxinterop.c new file mode 100644 index 0000000000..2ea2ea0ec9 --- /dev/null +++ b/video/out/opengl/context_dxinterop.c @@ -0,0 +1,650 @@ +/* + * 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 +#include "video/out/w32_common.h" +#include "context.h" + +// For WGL_ACCESS_WRITE_DISCARD_NV, etc. +#include + +// 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->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = 5; + break; + case D3DFMT_R5G6B5: + ctx->gl->fb_r = 5; ctx->gl->fb_g = 6; ctx->gl->fb_b = 5; + break; + case D3DFMT_R8G8B8: case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8: + case D3DFMT_A8B8G8R8: case D3DFMT_X8B8G8R8: default: + ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = 8; + break; + case D3DFMT_A2R10G10B10: case D3DFMT_A2B10G10R10: + ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_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); + } + p->rtarget_h = 0; + if (p->texture) + gl->DeleteTextures(1, &p->texture); + p->texture = 0; + if (p->rtarget) + IDirect3DSurface9_Release(p->rtarget); + p->rtarget = NULL; + if (p->backbuffer) + IDirect3DSurface9_Release(p->backbuffer); + p->backbuffer = NULL; + if (p->swapchain) + IDirect3DSwapChain9Ex_Release(p->swapchain); + p->swapchain = NULL; +} + +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, + .BackBufferWidth = ctx->vo->dwidth ? ctx->vo->dwidth : 1, + .BackBufferHeight = ctx->vo->dheight ? ctx->vo->dheight : 1, + // 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 + .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"); + return -1; + } + + D3DPRESENT_PARAMETERS pparams; + fill_presentparams(ctx, &pparams); + + hr = IDirect3D9Ex_CreateDeviceEx(p->d3d9ex, D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, vo_w32_hwnd(ctx->vo), + D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE | + D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | + D3DCREATE_NOWINDOWCHANGES, + &pparams, NULL, &p->device); + if (FAILED(hr)) { + MP_FATAL(ctx->vo, "Couldn't create device\n"); + return -1; + } + + // mpv expects frames to be presented right after swap_buffers() returns + IDirect3DDevice9Ex_SetMaximumFrameLatency(p->device, 1); + + // Register the Direct3D device with WGL_NV_dx_interop + p->device_h = gl->DXOpenDeviceNV(p->device); + if (!p->device_h) { + MP_FATAL(ctx->vo, "Couldn't open Direct3D from GL\n"); + return -1; + } + + return 0; +} + +static void d3d_destroy(MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + struct GL *gl = ctx->gl; + + if (p->device_h) + gl->DXCloseDeviceNV(p->device_h); + if (p->device) + IDirect3DDevice9Ex_Release(p->device); + if (p->d3d9ex) + IDirect3D9Ex_Release(p->d3d9ex); + if (p->d3d9_dll) + FreeLibrary(p->d3d9_dll); +} + +static void dxinterop_uninit(MPGLContext *ctx) +{ + d3d_size_dependent_destroy(ctx); + d3d_destroy(ctx); + os_ctx_destroy(ctx); + vo_w32_uninit(ctx->vo); + DwmEnableMMCSS(FALSE); + pump_message_loop(); +} + +static GLAPIENTRY void dxinterop_bind_framebuffer(GLenum target, + GLuint framebuffer) +{ + if (!current_ctx) + return; + struct priv *p = current_ctx->priv; + + // Keep track of whether the shared framebuffer is bound + if (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER) + p->fb_bound = (framebuffer == 0); + + // Pretend the shared framebuffer is the primary framebuffer + if (framebuffer == 0) + framebuffer = p->framebuffer; + + p->real_gl_bind_framebuffer(target, framebuffer); + + // Attach the shared texture if it is not attached already + try_attach_texture(current_ctx); +} + +static void dxinterop_reset(struct MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + HRESULT hr; + + // Check if the device actually needs to be reset + if (ctx->vo->dwidth == p->width && ctx->vo->dheight == p->height && + p->requested_swapinterval == p->swapinterval && !p->lost_device) + return; + + d3d_size_dependent_destroy(ctx); + + D3DPRESENT_PARAMETERS pparams; + fill_presentparams(ctx, &pparams); + + hr = IDirect3DDevice9Ex_ResetEx(p->device, &pparams, NULL); + if (FAILED(hr)) { + p->lost_device = true; + MP_FATAL(ctx->vo, "Couldn't reset device\n"); + return; + } + + if (d3d_size_dependent_create(ctx) < 0) { + p->lost_device = true; + MP_FATAL(ctx->vo, "Couldn't recreate Direct3D objects after reset\n"); + return; + } + + MP_VERBOSE(ctx->vo, "Direct3D device reset\n"); + p->width = ctx->vo->dwidth; + p->height = ctx->vo->dheight; + p->swapinterval = p->requested_swapinterval; + p->lost_device = false; +} + +static int GLAPIENTRY dxinterop_swap_interval(int interval) +{ + if (!current_ctx) + return 0; + struct priv *p = current_ctx->priv; + + p->requested_swapinterval = interval; + dxinterop_reset(current_ctx); + return 1; +} + +static int dxinterop_init(struct MPGLContext *ctx, int flags) +{ + struct priv *p = ctx->priv; + struct GL *gl = ctx->gl; + + p->requested_swapinterval = 1; + + if (!vo_w32_init(ctx->vo)) + goto fail; + if (os_ctx_create(ctx) < 0) + goto fail; + + // Create the shared framebuffer + gl->GenFramebuffers(1, &p->framebuffer); + + // Hook glBindFramebuffer to return the shared framebuffer instead of the + // primary one + current_ctx = ctx; + p->real_gl_bind_framebuffer = gl->BindFramebuffer; + gl->BindFramebuffer = dxinterop_bind_framebuffer; + + gl->SwapInterval = dxinterop_swap_interval; + + if (d3d_create(ctx) < 0) + goto fail; + if (d3d_size_dependent_create(ctx) < 0) + goto fail; + + // Bind the shared framebuffer. This will also attach the shared texture. + gl->BindFramebuffer(GL_FRAMEBUFFER, 0); + + // The OpenGL and Direct3D coordinate systems are flipped vertically + // relative to each other. Flip the video during rendering so it can be + // copied to the Direct3D backbuffer with a simple (and fast) StretchRect. + ctx->flip_v = true; + + DwmEnableMMCSS(TRUE); + + return 0; +fail: + dxinterop_uninit(ctx); + return -1; +} + +static int dxinterop_reconfig(struct MPGLContext *ctx) +{ + vo_w32_config(ctx->vo); + return 0; +} + +static void dxinterop_swap_buffers(MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + struct GL *gl = ctx->gl; + HRESULT hr; + + pump_message_loop(); + + // If the device is still lost, try to reset it again + if (p->lost_device) + dxinterop_reset(ctx); + if (p->lost_device) + return; + + if (!gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h)) { + MP_FATAL(ctx->vo, "Couldn't unlock rendertarget for present\n"); + return; + } + + // Blit the OpenGL rendertarget to the backbuffer + hr = IDirect3DDevice9Ex_StretchRect(p->device, p->rtarget, NULL, + p->backbuffer, NULL, D3DTEXF_NONE); + if (FAILED(hr)) { + MP_FATAL(ctx->vo, "Couldn't stretchrect for present\n"); + return; + } + + if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) { + MP_FATAL(ctx->vo, "Couldn't lock rendertarget after stretchrect\n"); + return; + } + + hr = IDirect3DDevice9Ex_PresentEx(p->device, NULL, NULL, NULL, NULL, 0); + switch (hr) { + case D3DERR_DEVICELOST: + case D3DERR_DEVICEHUNG: + MP_VERBOSE(ctx->vo, "Direct3D device lost! Resetting.\n"); + p->lost_device = true; + dxinterop_reset(ctx); + break; + default: + if (FAILED(hr)) + MP_FATAL(ctx->vo, "Couldn't present! 0x%08x\n", (unsigned)hr); + } +} + +static int dxinterop_control(MPGLContext *ctx, int *events, int request, + void *arg) +{ + int r = vo_w32_control(ctx->vo, events, request, arg); + if (*events & VO_EVENT_RESIZE) + dxinterop_reset(ctx); + return r; +} + +const struct mpgl_driver mpgl_driver_dxinterop = { + .name = "dxinterop", + .priv_size = sizeof(struct priv), + .init = dxinterop_init, + .reconfig = dxinterop_reconfig, + .swap_buffers = dxinterop_swap_buffers, + .control = dxinterop_control, + .uninit = dxinterop_uninit, +}; diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c new file mode 100644 index 0000000000..55594413a8 --- /dev/null +++ b/video/out/opengl/context_rpi.c @@ -0,0 +1,244 @@ +/* + * 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 "common/common.h" +#include "video/out/x11_common.h" +#include "context.h" + +#include "rpi.h" + +static void *get_proc_address(const GLubyte *name) +{ + void *p = eglGetProcAddress(name); + // EGL 1.4 (supported by the RPI firmware) does not necessarily return + // function pointers for core functions. + if (!p) { + void *h = dlopen("/opt/vc/lib/libGLESv2.so", RTLD_LAZY); + if (h) { + p = dlsym(h, name); + dlclose(h); + } + } + return p; +} + +static EGLConfig select_fb_config_egl(struct mp_egl_rpi *p) +{ + EGLint attributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLint config_count; + EGLConfig config; + + eglChooseConfig(p->egl_display, attributes, &config, 1, &config_count); + + if (!config_count) { + MP_FATAL(p, "Could find EGL configuration!\n"); + return NULL; + } + + return config; +} + +int mp_egl_rpi_init(struct mp_egl_rpi *p, DISPMANX_ELEMENT_HANDLE_T window, + int w, int h) +{ + p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(p->egl_display, NULL, NULL)) { + MP_FATAL(p, "EGL failed to initialize.\n"); + goto fail; + } + + eglBindAPI(EGL_OPENGL_ES_API); + + EGLConfig config = select_fb_config_egl(p); + if (!config) + goto fail; + + p->egl_window = (EGL_DISPMANX_WINDOW_T){ + .element = window, + .width = w, + .height = h, + }; + p->egl_surface = eglCreateWindowSurface(p->egl_display, config, + &p->egl_window, NULL); + + if (p->egl_surface == EGL_NO_SURFACE) { + MP_FATAL(p, "Could not create EGL surface!\n"); + goto fail; + } + + EGLint context_attributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + p->egl_context = eglCreateContext(p->egl_display, config, + EGL_NO_CONTEXT, context_attributes); + + if (p->egl_context == EGL_NO_CONTEXT) { + MP_FATAL(p, "Could not create EGL context!\n"); + goto fail; + } + + eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface, + p->egl_context); + + p->gl = talloc_zero(NULL, struct GL); + + const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS); + mpgl_load_functions(p->gl, get_proc_address, exts, p->log); + + if (!p->gl->version && !p->gl->es) + goto fail; + + return 0; + +fail: + mp_egl_rpi_destroy(p); + return -1; +} + +void mp_egl_rpi_destroy(struct mp_egl_rpi *p) +{ + if (p->egl_display) { + eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + } + if (p->egl_surface) + eglDestroySurface(p->egl_display, p->egl_surface); + if (p->egl_context) + eglDestroyContext(p->egl_display, p->egl_context); + p->egl_context = EGL_NO_CONTEXT; + eglReleaseThread(); + p->egl_display = EGL_NO_DISPLAY; + talloc_free(p->gl); + p->gl = NULL; +} + +struct priv { + DISPMANX_DISPLAY_HANDLE_T display; + DISPMANX_ELEMENT_HANDLE_T window; + DISPMANX_UPDATE_HANDLE_T update; + struct mp_egl_rpi egl; + int w, h; +}; + +static void rpi_uninit(MPGLContext *ctx) +{ + struct priv *p =