From 75c0c06640da677477a0cfd08deee1d4e23a4361 Mon Sep 17 00:00:00 2001 From: James Ross-Gowan Date: Sat, 16 Sep 2017 01:37:28 +1000 Subject: vo_gpu: convert windows/osx hwdecs/contexts to new API --- video/out/gpu/context.c | 6 +- video/out/opengl/context_angle.c | 220 +++++++++++---------- video/out/opengl/context_cocoa.c | 70 ++++--- video/out/opengl/context_dxinterop.c | 187 +++++++++--------- video/out/opengl/context_w32.c | 355 --------------------------------- video/out/opengl/context_win.c | 369 +++++++++++++++++++++++++++++++++++ video/out/opengl/hwdec_d3d11egl.c | 2 +- video/out/opengl/hwdec_d3d11eglrgb.c | 2 +- video/out/opengl/hwdec_dxva2egl.c | 2 +- video/out/opengl/hwdec_dxva2gldx.c | 2 +- 10 files changed, 641 insertions(+), 574 deletions(-) delete mode 100644 video/out/opengl/context_w32.c create mode 100644 video/out/opengl/context_win.c (limited to 'video') diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c index dbabba8b3b..ba710ff59e 100644 --- a/video/out/gpu/context.c +++ b/video/out/gpu/context.c @@ -40,7 +40,7 @@ extern const struct ra_ctx_fns ra_ctx_cocoa; extern const struct ra_ctx_fns ra_ctx_wayland_egl; extern const struct ra_ctx_fns ra_ctx_wgl; extern const struct ra_ctx_fns ra_ctx_angle; -extern const struct ra_ctx_fns ra_ctx_dxinterop; +extern const struct ra_ctx_fns ra_ctx_dxgl; extern const struct ra_ctx_fns ra_ctx_rpi; extern const struct ra_ctx_fns ra_ctx_mali; extern const struct ra_ctx_fns ra_ctx_vdpauglx; @@ -50,7 +50,6 @@ static const struct ra_ctx_fns *contexts[] = { #if HAVE_RPI &ra_ctx_rpi, #endif -/* #if HAVE_GL_COCOA &ra_ctx_cocoa, #endif @@ -61,9 +60,8 @@ static const struct ra_ctx_fns *contexts[] = { &ra_ctx_wgl, #endif #if HAVE_GL_DXINTEROP - &ra_ctx_dxinterop, + &ra_ctx_dxgl, #endif -*/ #if HAVE_GL_X11 &ra_ctx_glx_probe, #endif diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c index f249b746cb..a8051155e1 100644 --- a/video/out/opengl/context_angle.c +++ b/video/out/opengl/context_angle.c @@ -31,6 +31,7 @@ #include "video/out/w32_common.h" #include "osdep/windows_utils.h" #include "context.h" +#include "utils.h" #ifndef EGL_D3D_TEXTURE_ANGLE #define EGL_D3D_TEXTURE_ANGLE 0x33A3 @@ -52,8 +53,6 @@ struct angle_opts { int d3d11_warp; int d3d11_feature_level; int egl_windowing; - int swapchain_length; // Currently only works with DXGI 1.2+ - int max_frame_latency; int flip; }; @@ -77,9 +76,9 @@ const struct m_sub_options angle_conf = { ({"auto", -1}, {"no", 0}, {"yes", 1})), - OPT_INTRANGE("angle-swapchain-length", swapchain_length, 0, 2, 16), - OPT_INTRANGE("angle-max-frame-latency", max_frame_latency, 0, 1, 16), OPT_FLAG("angle-flip", flip, 0), + OPT_REPLACED("angle-max-frame-latency", "swapchain-depth"), + OPT_REMOVED("angle-swapchain-length", "controlled by --swapchain-depth"), {0} }, .defaults = &(const struct angle_opts) { @@ -87,14 +86,14 @@ const struct m_sub_options angle_conf = { .d3d11_warp = -1, .d3d11_feature_level = D3D_FEATURE_LEVEL_11_0, .egl_windowing = -1, - .swapchain_length = 6, - .max_frame_latency = 3, .flip = 1, }, .size = sizeof(struct angle_opts), }; struct priv { + GL gl; + IDXGISwapChain *dxgi_swapchain; ID3D11Device *d3d11_device; @@ -110,20 +109,21 @@ struct priv { int sc_width, sc_height; // Swap chain width and height int swapinterval; + bool flipped; struct angle_opts *opts; }; -static __thread struct MPGLContext *current_ctx; +static __thread struct ra_ctx *current_ctx; -static void update_sizes(MPGLContext *ctx) +static void update_sizes(struct ra_ctx *ctx) { struct priv *p = ctx->priv; p->sc_width = ctx->vo->dwidth ? ctx->vo->dwidth : 1; p->sc_height = ctx->vo->dheight ? ctx->vo->dheight : 1; } -static void d3d11_backbuffer_release(MPGLContext *ctx) +static void d3d11_backbuffer_release(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -137,7 +137,7 @@ static void d3d11_backbuffer_release(MPGLContext *ctx) SAFE_RELEASE(p->d3d11_backbuffer); } -static bool d3d11_backbuffer_get(MPGLContext *ctx) +static bool d3d11_backbuffer_get(struct ra_ctx *ctx) { struct priv *p = ctx->priv; struct vo *vo = ctx->vo; @@ -168,7 +168,7 @@ static bool d3d11_backbuffer_get(MPGLContext *ctx) return true; } -static void d3d11_backbuffer_resize(MPGLContext *ctx) +static void d3d11_backbuffer_resize(struct ra_ctx *ctx) { struct priv *p = ctx->priv; struct vo *vo = ctx->vo; @@ -197,7 +197,7 @@ static void d3d11_backbuffer_resize(MPGLContext *ctx) MP_FATAL(vo, "Couldn't get back buffer after resize\n"); } -static void d3d11_device_destroy(MPGLContext *ctx) +static void d3d11_device_destroy(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -215,7 +215,7 @@ static void d3d11_device_destroy(MPGLContext *ctx) SAFE_RELEASE(p->d3d11_device); } -static bool d3d11_device_create(MPGLContext *ctx, int flags) +static bool d3d11_device_create(struct ra_ctx *ctx) { struct priv *p = ctx->priv; struct vo *vo = ctx->vo; @@ -226,7 +226,7 @@ static bool d3d11_device_create(MPGLContext *ctx, int flags) .force_warp = o->d3d11_warp == 1, .max_feature_level = o->d3d11_feature_level, .min_feature_level = D3D_FEATURE_LEVEL_9_3, - .max_frame_latency = o->max_frame_latency, + .max_frame_latency = ctx->opts.swapchain_depth, }; if (!mp_d3d11_create_present_device(vo->log, &device_opts, &p->d3d11_device)) return false; @@ -262,7 +262,7 @@ static bool d3d11_device_create(MPGLContext *ctx, int flags) return true; } -static void d3d11_swapchain_surface_destroy(MPGLContext *ctx) +static void d3d11_swapchain_surface_destroy(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -277,7 +277,7 @@ static void d3d11_swapchain_surface_destroy(MPGLContext *ctx) ID3D11DeviceContext_Flush(p->d3d11_context); } -static bool d3d11_swapchain_surface_create(MPGLContext *ctx, int flags) +static bool d3d11_swapchain_surface_create(struct ra_ctx *ctx) { struct priv *p = ctx->priv; struct vo *vo = ctx->vo; @@ -292,7 +292,9 @@ static bool d3d11_swapchain_surface_create(MPGLContext *ctx, int flags) .width = p->sc_width, .height = p->sc_height, .flip = o->flip, - .length = o->swapchain_length, + // Add one frame for the backbuffer and one frame of "slack" to reduce + // contention with the window manager when acquiring the backbuffer + .length = ctx->opts.swapchain_depth + 2, .usage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT, }; if (!mp_d3d11_create_swapchain(p->d3d11_device, vo->log, &swapchain_opts, @@ -301,8 +303,7 @@ static bool d3d11_swapchain_surface_create(MPGLContext *ctx, int flags) if (!d3d11_backbuffer_get(ctx)) goto fail; - // EGL_D3D_TEXTURE_ANGLE pbuffers are always flipped vertically - ctx->flip_v = true; + p->flipped = true; return true; fail: @@ -310,7 +311,7 @@ fail: return false; } -static void d3d9_device_destroy(MPGLContext *ctx) +static void d3d9_device_destroy(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -319,7 +320,7 @@ static void d3d9_device_destroy(MPGLContext *ctx) p->egl_display = EGL_NO_DISPLAY; } -static bool d3d9_device_create(MPGLContext *ctx, int flags) +static bool d3d9_device_create(struct ra_ctx *ctx) { struct priv *p = ctx->priv; struct vo *vo = ctx->vo; @@ -348,7 +349,7 @@ static bool d3d9_device_create(MPGLContext *ctx, int flags) return true; } -static void egl_window_surface_destroy(MPGLContext *ctx) +static void egl_window_surface_destroy(struct ra_ctx *ctx) { struct priv *p = ctx->priv; if (p->egl_window) { @@ -357,7 +358,7 @@ static void egl_window_surface_destroy(MPGLContext *ctx) } } -static bool egl_window_surface_create(MPGLContext *ctx, int flags) +static bool egl_window_surface_create(struct ra_ctx *ctx) { struct priv *p = ctx->priv; struct vo *vo = ctx->vo; @@ -374,7 +375,7 @@ static bool egl_window_surface_create(MPGLContext *ctx, int flags) EGL_SURFACE_ORIENTATION_ANGLE); MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len, EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE); - ctx->flip_v = true; + p->flipped = true; MP_VERBOSE(vo, "Rendering flipped.\n"); } } @@ -396,7 +397,7 @@ fail: return false; } -static void context_destroy(struct MPGLContext *ctx) +static void context_destroy(struct ra_ctx *ctx) { struct priv *p = ctx->priv; if (p->egl_context) { @@ -407,7 +408,7 @@ static void context_destroy(struct MPGLContext *ctx) p->egl_context = EGL_NO_CONTEXT; } -static bool context_init(struct MPGLContext *ctx, int flags) +static bool context_init(struct ra_ctx *ctx) { struct priv *p = ctx->priv; struct vo *vo = ctx->vo; @@ -421,8 +422,8 @@ static bool context_init(struct MPGLContext *ctx, int flags) if (exts) MP_DBG(vo, "EGL extensions: %s\n", exts); - if (!mpegl_create_context(p->egl_display, vo->log, flags | VOFLAG_GLES, - &p->egl_context, &p->egl_config)) + if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, + &p->egl_config)) { MP_FATAL(vo, "Could not create EGL context!\n"); goto fail; @@ -434,10 +435,12 @@ fail: return false; } -static void angle_uninit(struct MPGLContext *ctx) +static void angle_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; + ra_gl_ctx_uninit(ctx); + DwmEnableMMCSS(FALSE); // Uninit the EGL surface implementation that is being used. Note: This may @@ -474,17 +477,56 @@ static int GLAPIENTRY angle_swap_interval(int interval) } } -static int angle_init(struct MPGLContext *ctx, int flags) +static void d3d11_swap_buffers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; + + // Calling Present() on a flip-sequential swap chain will silently change + // the underlying storage of the back buffer to point to the next buffer in + // the chain. This results in the RTVs for the back buffer becoming + // unbound. Since ANGLE doesn't know we called Present(), it will continue + // using the unbound RTVs, so we must save and restore them ourselves. + ID3D11RenderTargetView *rtvs[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = {0}; + ID3D11DepthStencilView *dsv = NULL; + ID3D11DeviceContext_OMGetRenderTargets(p->d3d11_context, + MP_ARRAY_SIZE(rtvs), rtvs, &dsv); + + HRESULT hr = IDXGISwapChain_Present(p->dxgi_swapchain, p->swapinterval, 0); + if (FAILED(hr)) + MP_FATAL(ctx->vo, "Couldn't present: %s\n", mp_HRESULT_to_str(hr)); + + // Restore the RTVs and release the objects + ID3D11DeviceContext_OMSetRenderTargets(p->d3d11_context, + MP_ARRAY_SIZE(rtvs), rtvs, dsv); + for (int i = 0; i < MP_ARRAY_SIZE(rtvs); i++) + SAFE_RELEASE(rtvs[i]); + SAFE_RELEASE(dsv); +} + +static void egl_swap_buffers(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + eglSwapBuffers(p->egl_display, p->egl_window); +} + +static void angle_swap_buffers(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + if (p->dxgi_swapchain) + d3d11_swap_buffers(ctx); + else + egl_swap_buffers(ctx); +} + +static bool angle_init(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); struct vo *vo = ctx->vo; + GL *gl = &p->gl; p->opts = mp_get_config_group(ctx, ctx->global, &angle_conf); struct angle_opts *o = p->opts; - // DWM MMCSS cargo-cult. The dxinterop backend also does this. - DwmEnableMMCSS(TRUE); - if (!angle_load()) { MP_VERBOSE(vo, "Failed to load LIBEGL.DLL\n"); goto fail; @@ -493,19 +535,19 @@ static int angle_init(struct MPGLContext *ctx, int flags) // Create the underlying EGL device implementation bool context_ok = false; if ((!context_ok && !o->renderer) || o->renderer == RENDERER_D3D11) { - context_ok = d3d11_device_create(ctx, flags); + context_ok = d3d11_device_create(ctx); if (context_ok) { - context_ok = context_init(ctx, flags); + context_ok = context_init(ctx); if (!context_ok) d3d11_device_destroy(ctx); } } if ((!context_ok && !o->renderer) || o->renderer == RENDERER_D3D9) { - context_ok = d3d9_device_create(ctx, flags); + context_ok = d3d9_device_create(ctx); if (context_ok) { MP_VERBOSE(vo, "Using Direct3D 9\n"); - context_ok = context_init(ctx, flags); + context_ok = context_init(ctx); if (!context_ok) d3d9_device_destroy(ctx); } @@ -519,34 +561,58 @@ static int angle_init(struct MPGLContext *ctx, int flags) // Create the underlying EGL surface implementation bool surface_ok = false; if ((!surface_ok && o->egl_windowing == -1) || o->egl_windowing == 0) { - surface_ok = d3d11_swapchain_surface_create(ctx, flags); + surface_ok = d3d11_swapchain_surface_create(ctx); } if ((!surface_ok && o->egl_windowing == -1) || o->egl_windowing == 1) { - surface_ok = egl_window_surface_create(ctx, flags); + surface_ok = egl_window_surface_create(ctx); if (surface_ok) MP_VERBOSE(vo, "Using EGL windowing\n"); } if (!surface_ok) goto fail; - mpegl_load_functions(ctx->gl, vo->log); + mpegl_load_functions(gl, vo->log); current_ctx = ctx; - ctx->gl->SwapInterval = angle_swap_interval; + gl->SwapInterval = angle_swap_interval; + + // ANGLE doesn't actually need to override any of the functions (yet) + static const struct ra_swapchain_fns empty_swapchain_fns = {0}; + struct ra_gl_ctx_params params = { + .swap_buffers = angle_swap_buffers, + .flipped = p->flipped, + .external_swapchain = p->dxgi_swapchain ? &empty_swapchain_fns : NULL, + }; - return 0; + if (!ra_gl_ctx_init(ctx, gl, params)) + goto fail; + + DwmEnableMMCSS(TRUE); // DWM MMCSS cargo-cult. The dxgl backend also does this. + + return true; fail: angle_uninit(ctx); - return -1; + return false; +} + +static void resize(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + if (p->dxgi_swapchain) + d3d11_backbuffer_resize(ctx); + else + eglWaitClient(); // Should get ANGLE to resize its swapchain + ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0); } -static int angle_reconfig(struct MPGLContext *ctx) +static bool angle_reconfig(struct ra_ctx *ctx) { vo_w32_config(ctx->vo); - return 0; + resize(ctx); + return true; } -static struct mp_image *d3d11_screenshot(MPGLContext *ctx) +static struct mp_image *d3d11_screenshot(struct ra_ctx *ctx) { struct priv *p = ctx->priv; ID3D11Texture2D *frontbuffer = NULL; @@ -624,10 +690,8 @@ done: return img; } -static int angle_control(MPGLContext *ctx, int *events, int request, void *arg) +static int angle_control(struct ra_ctx *ctx, int *events, int request, void *arg) { - struct priv *p = ctx->priv; - // Try a D3D11-specific method of taking a window screenshot if (request == VOCTRL_SCREENSHOT_WIN) { struct mp_image *img = d3d11_screenshot(ctx); @@ -637,63 +701,17 @@ static int angle_control(MPGLContext *ctx, int *events, int request, void *arg) } } - int r = vo_w32_control(ctx->vo, events, request, arg); - if (*events & VO_EVENT_RESIZE) { - if (p->dxgi_swapchain) - d3d11_backbuffer_resize(ctx); - else - eglWaitClient(); // Should get ANGLE to resize its swapchain - } - return r; -} - -static void d3d11_swap_buffers(MPGLContext *ctx) -{ - struct priv *p = ctx->priv; - - // Calling Present() on a flip-sequential swap chain will silently change - // the underlying storage of the back buffer to point to the next buffer in - // the chain. This results in the RTVs for the back buffer becoming - // unbound. Since ANGLE doesn't know we called Present(), it will continue - // using the unbound RTVs, so we must save and restore them ourselves. - ID3D11RenderTargetView *rtvs[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = {0}; - ID3D11DepthStencilView *dsv = NULL; - ID3D11DeviceContext_OMGetRenderTargets(p->d3d11_context, - MP_ARRAY_SIZE(rtvs), rtvs, &dsv); - - HRESULT hr = IDXGISwapChain_Present(p->dxgi_swapchain, p->swapinterval, 0); - if (FAILED(hr)) - MP_FATAL(ctx->vo, "Couldn't present: %s\n", mp_HRESULT_to_str(hr)); - - // Restore the RTVs and release the objects - ID3D11DeviceContext_OMSetRenderTargets(p->d3d11_context, - MP_ARRAY_SIZE(rtvs), rtvs, dsv); - for (int i = 0; i < MP_ARRAY_SIZE(rtvs); i++) - SAFE_RELEASE(rtvs[i]); - SAFE_RELEASE(dsv); -} - -static void egl_swap_buffers(MPGLContext *ctx) -{ - struct priv *p = ctx->priv; - eglSwapBuffers(p->egl_display, p->egl_window); -} - -static void angle_swap_buffers(MPGLContext *ctx) -{ - struct priv *p = ctx->priv; - if (p->dxgi_swapchain) - d3d11_swap_buffers(ctx); - else - egl_swap_buffers(ctx); + int ret = vo_w32_control(ctx->vo, events, request, arg); + if (*events & VO_EVENT_RESIZE) + resize(ctx); + return ret; } -const struct mpgl_driver mpgl_driver_angle = { +const struct ra_ctx_fns ra_ctx_angle = { + .type = "opengl", .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 index cdf6faffcd..585c49f9a9 100644 --- a/video/out/opengl/context_cocoa.c +++ b/video/out/opengl/context_cocoa.c @@ -36,6 +36,7 @@ const struct m_sub_options cocoa_conf = { }; struct priv { + GL gl; CGLPixelFormatObj pix; CGLContextObj ctx; @@ -62,7 +63,7 @@ static void *cocoa_glgetaddr(const char *s) return ret; } -static CGLError test_gl_version(struct MPGLContext *ctx, CGLOpenGLProfile ver) +static CGLError test_gl_version(struct ra_ctx *ctx, CGLOpenGLProfile ver) { struct priv *p = ctx->priv; @@ -107,9 +108,10 @@ error_out: return err; } -static bool create_gl_context(struct MPGLContext *ctx, int vo_flags) +static bool create_gl_context(struct ra_ctx *ctx) { struct priv *p = ctx->priv; + GL *gl = &p->gl; CGLError err; CGLOpenGLProfile gl_versions[] = { @@ -132,60 +134,82 @@ static bool create_gl_context(struct MPGLContext *ctx, int vo_flags) vo_cocoa_set_opengl_ctx(ctx->vo, p->ctx); CGLSetCurrentContext(p->ctx); - if (vo_flags & VOFLAG_ALPHA) + if (ctx->opts.want_alpha) CGLSetParameter(p->ctx, kCGLCPSurfaceOpacity, &(GLint){0}); - mpgl_load_functions(ctx->gl, (void *)cocoa_glgetaddr, NULL, ctx->vo->log); + mpgl_load_functions(gl, (void *)cocoa_glgetaddr, NULL, ctx->vo->log); + gl->SwapInterval = set_swap_interval; CGLReleasePixelFormat(p->pix); return true; } -static void cocoa_uninit(MPGLContext *ctx) +static void cocoa_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; + ra_gl_ctx_uninit(ctx); CGLReleaseContext(p->ctx); vo_cocoa_uninit(ctx->vo); } -static int cocoa_init(MPGLContext *ctx, int vo_flags) +static void cocoa_swap_buffers(struct ra_ctx *ctx) { - struct priv *p = ctx->priv; + GL *gl = &p->gl; + vo_cocoa_swap_buffers(ctx->vo); + gl->Flush(); +} + +static bool cocoa_init(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); + GL *gl = &p->gl; p->opts = mp_get_config_group(ctx, ctx->global, &cocoa_conf); vo_cocoa_init(ctx->vo); - if (!create_gl_context(ctx, vo_flags)) - return -1; + if (!create_gl_context(ctx)) + goto fail; - ctx->gl->SwapInterval = set_swap_interval; - return 0; + struct ra_gl_ctx_params params = { + .swap_buffers = cocoa_swap_buffers, + }; + + if (!ra_gl_ctx_init(ctx, gl, params)) + goto fail; + + return true; + +fail: + cocoa_uninit(ctx); + return false; } -static int cocoa_reconfig(struct MPGLContext *ctx) +static void resize(struct ra_ctx *ctx) { - vo_cocoa_config_window(ctx->vo); - return 0; + ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0); } -static int cocoa_control(struct MPGLContext *ctx, int *events, int request, - void *arg) +static bool cocoa_reconfig(struct ra_ctx *ctx) { - return vo_cocoa_control(ctx->vo, events, request, arg); + vo_cocoa_config_window(ctx->vo); + resize(ctx); + return true; } -static void cocoa_swap_buffers(struct MPGLContext *ctx) +static int cocoa_control(struct ra_ctx *ctx, int *events, int request, + void *arg) { - vo_cocoa_swap_buffers(ctx->vo); - ctx->gl->Flush(); + int ret = vo_cocoa_control(ctx->vo, events, request, arg); + if (*events & VO_EVENT_RESIZE) + resize(ctx); + return ret; } -const struct mpgl_driver mpgl_driver_cocoa = { +const struct ra_ctx_fns ra_ctx_cocoa = { + .type = "opengl", .name = "cocoa", - .priv_size = sizeof(struct priv), .init = cocoa_init, .reconfig = cocoa_reconfig, - .swap_buffers = cocoa_swap_buffers, .control = cocoa_control, .uninit = cocoa_uninit, }; diff --git a/video/out/opengl/context_dxinterop.c b/video/out/opengl/context_dxinterop.c index 507c150d15..85d84bf677 100644 --- a/video/out/opengl/context_dxinterop.c +++ b/video/out/opengl/context_dxinterop.c @@ -22,6 +22,7 @@ #include "osdep/windows_utils.h" #include "video/out/w32_common.h" #include "context.h" +#include "utils.h" // For WGL_ACCESS_WRITE_DISCARD_NV, etc. #include @@ -35,6 +36,8 @@ EXTERN_C IMAGE_DOS_HEADER __ImageBase; #endif struct priv { + GL gl; + HMODULE d3d9_dll; HRESULT (WINAPI *Direct3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D); @@ -54,6 +57,7 @@ struct priv { // OpenGL resources GLuint texture; + GLuint main_fb; // Did we lose the device? bool lost_device; @@ -63,7 +67,7 @@ struct priv { int width, height, swapinterval; }; -static __thread struct MPGLContext *current_ctx; +static __thread struct ra_ctx *current_ctx; static void pump_message_loop(void) { @@ -84,10 +88,11 @@ static void *w32gpa(const GLubyte *procName) return GetProcAddress(oglmod, procName); } -static int os_ctx_create(struct MPGLContext *ctx) +static int os_ctx_create(struct ra_ctx *ctx) { static const wchar_t os_wnd_class[] = L"mpv offscreen gl"; struct priv *p = ctx->priv; + GL *gl = &p->gl; HGLRC legacy_context = NULL; RegisterClassExW(&(WNDCLASSEXW) { @@ -190,8 +195,8 @@ static int os_ctx_create(struct MPGLContext *ctx) goto fail; } - mpgl_load_functions(ctx->gl, w32gpa, wgl_exts, ctx->vo->log); - if (!(ctx->gl->mpgl_caps & MPGL_CAP_DXINTEROP)) { + mpgl_load_functions(gl, w32gpa, wgl_exts, ctx->vo->log); + if (!(gl->mpgl_caps & MPGL_CAP_DXINTEROP)) { MP_FATAL(ctx->vo, "WGL_NV_DX_interop is not supported\n"); goto fail; } @@ -205,7 +210,7 @@ fail: return -1; } -static void os_ctx_destroy(MPGLContext *ctx) +static void os_ctx_destroy(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -219,10 +224,10 @@ static void os_ctx_destroy(MPGLContext *ctx) DestroyWindow(p->os_wnd); } -static int d3d_size_dependent_create(MPGLContext *ctx) +static int d3d_size_dependent_create(struct ra_ctx *ctx) { struct priv *p = ctx->priv; - struct GL *gl = ctx->gl; + GL *gl = &p->gl; HRESULT hr; IDirect3DSwapChain9 *sw9; @@ -294,7 +299,7 @@ static int d3d_size_dependent_create(MPGLContext *ctx) return -1; } - gl->BindFramebuffer(GL_FRAMEBUFFER, ctx->main_fb); + gl->BindFramebuffer(GL_FRAMEBUFFER, p->main_fb); gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p->texture, 0); gl->BindFramebuffer(GL_FRAMEBUFFER, 0); @@ -302,10 +307,10 @@ static int d3d_size_dependent_create(MPGLContext *ctx) return 0; } -static void d3d_size_dependent_destroy(MPGLContext *ctx) +static void d3d_size_dependent_destroy(struct ra_ctx *ctx) { struct priv *p = ctx->priv; - struct GL *gl = ctx->gl; + GL *gl = &p->gl; if (p->rtarget_h) { gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h); @@ -321,7 +326,8 @@ static void d3d_size_dependent_destroy(MPGLContext *ctx) SAFE_RELEASE(p->swapchain); } -static void fill_presentparams(MPGLContext *ctx, D3DPRESENT_PARAMETERS *pparams) +static void fill_presentparams(struct ra_ctx *ctx, + D3DPRESENT_PARAMETERS *pparams) { struct priv *p = ctx->priv; @@ -338,13 +344,9 @@ static void fill_presentparams(MPGLContext *ctx, D3DPRESENT_PARAMETERS *pparams) .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, + // Add one frame for the backbuffer and one frame of "slack" to reduce + // contention with the window manager when acquiring the backbuffer + .BackBufferCount = ctx->opts.swapchain_depth + 2, .SwapEffect = IsWindows7OrGreater() ? D3DSWAPEFFECT_FLIPEX : D3DSWAPEFFECT_FLIP, // Automatically get the backbuffer format from the display format .BackBufferFormat = D3DFMT_UNKNOWN, @@ -353,10 +355,10 @@ static void fill_presentparams(MPGLContext *ctx, D3DPRESENT_PARAMETERS *pparams) }; } -static int d3d_create(MPGLContext *ctx) +static int d3d_create(struct ra_ctx *ctx) { struct priv *p = ctx->priv; - struct GL *gl = ctx->gl; + GL *gl = &p->gl; HRESULT hr; p->d3d9_dll = LoadLibraryW(L"d3d9.dll"); @@ -396,8 +398,7 @@ static int d3d_create(MPGLContext *ctx) return -1; } - // mpv expects frames to be presented right after swap_buffers() returns - IDirect3DDevice9Ex_SetMaximumFrameLatency(p->device, 1); + IDirect3DDevice9Ex_SetMaximumFrameLatency(p->device, ctx->opts.swapchain_depth); // Register the Direct3D device with WGL_NV_dx_interop p->device_h = gl->DXOpenDeviceNV(p->device); @@ -410,10 +411,10 @@ static int d3d_create(MPGLContext *ctx) return 0; } -static void d3d_destroy(MPGLContext *ctx) +static void d3d_destroy(struct ra_ctx *ctx) { struct priv *p = ctx->priv; - struct GL *gl = ctx->gl; + GL *gl = &p->gl; if (p->device_h) gl->DXCloseDeviceNV(p->device_h); @@ -423,8 +424,9 @@ static void d3d_destroy(MPGLContext *ctx) FreeLibrary(p->d3d9_dll); } -static void dxinterop_uninit(MPGLContext *ctx) +static void dxgl_uninit(struct ra_ctx *ctx) { + ra_gl_ctx_uninit(ctx); d3d_size_dependent_destroy(ctx); d3d_destroy(ctx); os_ctx_destroy(ctx); @@ -433,7 +435,7 @@ static void dxinterop_uninit(MPGLContext *ctx) pump_message_loop(); } -static void dxinterop_reset(struct MPGLContext *ctx) +static void dxgl_reset(struct ra_ctx *ctx) { struct priv *p = ctx->priv; HRESULT hr; @@ -468,18 +470,18 @@ static void dxinterop_reset(struct MPGLContext *ctx) p->lost_device = false; } -static int GLAPIENTRY dxinterop_swap_interval(int interval) +static int GLAPIENTRY dxgl_swap_interval(int interval) { if (!current_ctx) return 0; struct priv *p = current_ctx->priv; p->requested_swapinterval = interval; - dxinterop_reset(current_ctx); + dxgl_reset(current_ctx); return 1; } -static void * GLAPIENTRY dxinterop_get_native_display(const char *name) +static void * GLAPIENTRY dxgl_get_native_display(const char *name) { if (!current_ctx || !name) return NULL; @@ -493,60 +495,17 @@ static void * GLAPIENTRY dxinterop_get_native_display(const char *name) return NULL; } -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, &ctx->main_fb); - - current_ctx = ctx; - gl->SwapInterval = dxinterop_swap_interval; - gl->MPGetNativeDisplay = dxinterop_get_native_display; - - if (d3d_create(ctx) < 0) - goto fail; - if (d3d_size_dependent_create(ctx) < 0) - goto fail; - - // 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) +static void dxgl_swap_buffers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; - struct GL *gl = ctx->gl; + GL *gl = &p->gl; HRESULT hr; pump_message_loop(); // If the device is still lost, try to reset it again if (p->lost_device) - dxinterop_reset(ctx); + dxgl_reset(ctx); if (p->lost_device) return; @@ -571,7 +530,7 @@ static void dxinterop_swap_buffers(MPGLContext *ctx) case D3DERR_DEVICEHUNG: MP_VERBOSE(ctx->vo, "Direct3D device lost! Resetting.\n"); p->lost_device = true; - dxinterop_reset(ctx); + dxgl_reset(ctx); return; default: if (FAILED(hr)) @@ -584,21 +543,75 @@ static void dxinterop_swap_buffers(MPGLContext *ctx) } } -static int dxinterop_control(MPGLContext *ctx, int *events, int request, +static bool dxgl_init(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); + GL *gl = &p->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->main_fb); + + current_ctx = ctx; + gl->SwapInterval = dxgl_swap_interval; + gl->MPGetNativeDisplay = dxgl_get_native_display; + + if (d3d_create(ctx) < 0) + goto fail; + if (d3d_size_dependent_create(ctx) < 0) + goto fail; + + static const struct ra_swapchain_fns empty_swapchain_fns = {0}; + struct ra_gl_ctx_params params = { + .swap_buffers = dxgl_swap_buffers, + .flipped = true, + .external_swapchain = &empty_swapchain_fns, + }; + + if (!ra_gl_ctx_init(ctx, gl, params)) + goto fail; + + DwmEnableMMCSS(TRUE); + return true; +fail: + dxgl_uninit(ctx); + return false; +} + +static void resize(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + dxgl_reset(ctx); + ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, p->main_fb); +} + +static bool dxgl_reconfig(struct ra_ctx *ctx) +{ + vo_w32_config(ctx->vo); + resize(ctx); + return true; +} + +static int dxgl_control(struct ra_ctx *ctx, int *events, int request, void *arg) { - int r = vo_w32_control(ctx->vo, events, request, arg); + int ret = vo_w32_control(ctx->vo, events, request, arg); if (*events & VO_EVENT_RESIZE) - dxinterop_reset(ctx); - return r; + resize(ctx); + return ret; } -const struct mpgl_driver mpgl_driver_dxinterop = { +const struct ra_ctx_fns ra_ctx_dxgl = { + .type = "opengl", .name = "dxinterop", - .priv_size = sizeof(struct priv), - .init = dxinterop_init, - .reconfig = dxinterop_reconfig, - .swap_buffers = dxinterop_swap_buffers, - .control = dxinterop_control, - .uninit = dxinterop_uninit, + .init = dxgl_init, + .reconfig = dxgl_reconfig, + .control = dxgl_control, + .uninit = dxgl_uninit, }; diff --git a/video/out/opengl/context_w32.c b/video/out/opengl/context_w32.c deleted file mode 100644 index eb6123967b..0000000000 --- a/video/out/opengl/context_w32.c +++ /dev/null @@ -1,355 +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 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 . - */ - -#include -#include -#include - -#include "options/m_config.h" -#include "video/out/w32_common.h" -#include "video/out/win32/exclusive_hack.h" -#include "context.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 w32_context { - int opt_swapinterval; - int current_swapinterval; - - int (GLAPIENTRY *real_wglSwapInterval)(int); - - HGLRC context; - HDC hdc; - int flags; -}; - -static void w32_uninit(MPGLContext *ctx); - -static __thread struct w32_context *current_w32_context; - -static int GLAPIENTRY w32_swap_interval(int interval) -{ - if (current_w32_context) - current_w32_context->opt_swapinterval = interval; - return 0; -} - -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); - - 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); - - wglMakeCurrent(w32_ctx->hdc, NULL); -} - -static int w32_init(struct MPGLContext *ctx, int flags) -{ - if (!vo_w32_init(ctx->vo)) - goto fail; - - struct w32_context *w32_ctx = ctx->priv; - - w32_ctx->flags = flags; - vo_w32_run_on_thread(ctx->vo, create_ctx, ctx); - - if (!w32_ctx->context) - goto fail; - - if (!ctx->gl->SwapInterval) - MP_VERBOSE(ctx->vo, "WGL_EXT_swap_control missing.\n"); - w32_ctx->real_wglSwapInterval = ctx->gl->SwapInterval; - ctx->gl->SwapInterval = w32_swap_interval; - w32_ctx->current_swapinterval = -1; - - current_w32_context = w32_ctx; - wglMakeCurrent(w32_ctx->hdc, w32_ctx->context); - DwmEnableMMCSS(TRUE); - return 0; - -fail: - w32_uninit(ctx); - return -1; -} - -static int w32_reconfig(struct MPGLContext *ctx) -{ - vo_w32_config(ctx->vo); - return 0; -} - -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; - current_w32_context = NULL; -} - -static void w32_uninit(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); - - DwmEnableMMCSS(FALSE); - vo_w32_uninit(ctx->vo); -} - -static bool compositor_active(MPGLContext *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; - - // Test if a program is running in exclusive fullscreen mode. If so, it's - // probably this one, so it's not getting redirected by the compositor. - if (mp_w32_is_in_exclusive_mode()) - return false; - - return true; -} - -static void w32_swap_buffers(MPGLContext *ctx) -{ - struct w32_context *w32_ctx = ctx->priv; - SwapBuffers(w32_ctx->hdc); - - // default if we don't DwmFLush - int new_swapinterval = w32_ctx->opt_swapinterval; - - int dwm_flush_opt; - mp_read_option_raw(ctx->global, "opengl-dwmflush", &m_option_type_choice, - &dwm_flush_opt); - - if (dwm_flush_opt >= 0) { - if ((dwm_flush_opt == 1 && !ctx->vo->opts->fullscreen) || - (dwm_flush_opt == 2) || - (dwm_flush_opt == 0 && compositor_active(ctx))) - { - if (DwmFlush() == S_OK) - new_swapinterval = 0; - } - } - - if (new_swapinterval != w32_ctx->current_swapinterval && - w32_ctx->real_wglSwapInterval) - { - w32_ctx->real_wglSwapInterval(new_swapinterval); - MP_VERBOSE(ctx->vo, "set SwapInterval(%d)\n", new_swapinterval); - } - w32_ctx->current_swapinterval = new_swapinterval; -} - -static int w32_control(MPGLContext *ctx, int *events, int request, void *arg) -{ - return vo_w32_control(ctx->vo, events, request, arg); -} - -const struct mpgl_driver mpgl_driver_w32 = { - .name = "win", - .priv_size = sizeof(struct w32_context), - .init = w32_init, - .reconfig = w32_reconfig, - .swap_buffers = w32_swap_buffers, - .control = w32_control, - .uninit = w32_uninit, -}; diff --git a/video/out/opengl/context_win.c b/video/out/opengl/context_win.c new file mode 100644 index 0000000000..e650867feb --- /dev/null +++ b/video/out/opengl/context_win.c @@ -0,0 +1,369 @@ +/* + * 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 . + */ + +#include +#include +#include + +#include "options/m_config.h" +#include "video/out/w32_common.h" +#include "video/out/win32/exclusive_hack.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 priv { + GL gl; + + int opt_swapinterval; + int current_swapinterval; + + int (GLAPIENTRY *real_wglSwapInterval)(int); + + 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 (!strstr(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; + + // Test if a program is running in exclusive fullscreen mode. If so, it's + // probably this one, so it's not getting redirected by the compositor. + if (mp_w32_is_in_exclusive_mode()) + 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; + + int dwm_flush_opt; + mp_read_option_raw(ctx->global, "opengl-dwmflush", &m_option_type_choice, + &dwm_flush_opt); + + if (dwm_flush_opt >= 0) { + if ((dwm_flush_opt == 1 && !ctx->vo->opts->fullscreen) || + (dwm_flush_opt == 2) || + (dwm_flush_opt == 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; + + 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, +}; diff --git a/video/out/opengl/hwdec_d3d11egl.c b/video/out/opengl/hwdec_d3d11egl.c index 3988f8310e..0597fbbb12 100644 --- a/video/out/opengl/hwdec_d3d11egl.c +++ b/video/out/opengl/hwdec_d3d11egl.c @@ -27,7 +27,7 @@ #include "common/common.h" #include "osdep/timer.h" #include "osdep/windows_utils.h" -#include "hwdec.h" +#include "video/out/gpu/hwdec.h" #include "ra_gl.h" #include "video/hwdec.h" #include "video/decode/d3d.h" diff --git a/video/out/opengl/hwdec_d3d11eglrgb.c b/video/out/opengl/hwdec_d3d11eglrgb.c index fa3976fec6..46264ac1bf 100644 --- a/video/out/opengl/hwdec_d3d11eglrgb.c +++ b/video/out/opengl/hwdec_d3d11eglrgb.c @@ -27,7 +27,7 @@ #include "common/common.h" #include "osdep/timer.h" #include "osdep/windows_utils.h" -#include "hwdec.h" +#include "video/out/gpu/hwdec.h" #include "ra_gl.h" #include "video/hwdec.h" #include "video/decode/d3d.h" diff --git a/video/out/opengl/hwdec_dxva2egl.c b/video/out/opengl/hwdec_dxva2egl.c index 01fb482325..6a92eb8934 100644 --- a/video/out/opengl/hwdec_dxva2egl.c +++ b/video/out/opengl/hwdec_dxva2egl.c @@ -27,7 +27,7 @@ #include "common/common.h" #include "osdep/timer.h" #include "osdep/windows_utils.h" -#include "hwdec.h" +#include "video/out/gpu/hwdec.h" #include "ra_gl.h" #include "video/hwdec.h" #include "video/decode/d3d.h" diff --git a/video/out/opengl/hwdec_dxva2gldx.c b/video/out/opengl/hwdec_dxva2gldx.c index fd9c80b7a2..0b3e30fbb1 100644 --- a/video/out/opengl/hwdec_dxva2gldx.c +++ b/video/out/opengl/hwdec_dxva2gldx.c @@ -20,7 +20,7 @@ #include "common/common.h" #include "osdep/windows_utils.h" -#include "hwdec.h" +#include "video/out/gpu/hwdec.h" #include "ra_gl.h" #include "video/hwdec.h" #include "video/decode/d3d.h" -- cgit v1.2.3