diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index 5d4bbe1c61..1696b51a53 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -548,6 +548,16 @@ Available video output drivers are: X11/GLX only. + ``dwmflush`` + Calls ``DwmFlush`` after swapping buffers on Windows (default: 0). + It also sets ``SwapInterval(0)`` to ignore the OpenGL timing. Values + are: 0 (disabled), 1 (only in windowed mode), 2 (also in full screen). + This may help getting more consistent frame intervals, especially with + high-fps clips - which might also reduce dropped frames. Typically a + value of 1 should be enough since full screen may bypass the DWM. + + Windows only. + ``sw`` Continue even if a software renderer is detected. diff --git a/video/out/gl_common.h b/video/out/gl_common.h index 2e25d331c7..06960f75ea 100644 --- a/video/out/gl_common.h +++ b/video/out/gl_common.h @@ -93,6 +93,12 @@ typedef struct MPGLContext { void (*releaseGlContext)(struct MPGLContext *); void (*set_current)(struct MPGLContext *, bool current); + // Used on windows only, tries to vsync with the DWM, and modifies SwapInterval + // when it does so. Returns the possibly modified swapinterval value. + int (*DwmFlush)(struct MPGLContext *, int opt_dwmflush, + int opt_swapinterval, int current_swapinterval); + + // Resize the window, or create a new window if there isn't one yet. // On the first call, it creates a GL context according to what's specified // in MPGLContext.requested_gl_version. This is just a hint, and if the diff --git a/video/out/gl_w32.c b/video/out/gl_w32.c index f2b7cf63d3..b1985e5e1f 100644 --- a/video/out/gl_w32.c +++ b/video/out/gl_w32.c @@ -24,10 +24,15 @@ #include "w32_common.h" #include "gl_common.h" +typedef HRESULT (WINAPI *DwmFlush_t)(void); + struct w32_context { HGLRC context; HDC hdc; int flags; + + HINSTANCE dwmapi_dll; + DwmFlush_t dwmflush; }; static bool create_dc(struct MPGLContext *ctx, int flags) @@ -202,6 +207,11 @@ static void create_ctx(void *ptr) create_context_w32_gl3(ctx); if (!w32_ctx->context) create_context_w32_old(ctx); + + w32_ctx->dwmapi_dll = LoadLibrary(L"Dwmapi.dll"); + if (w32_ctx->dwmapi_dll) + w32_ctx->dwmflush = (DwmFlush_t)GetProcAddress(w32_ctx->dwmapi_dll, "DwmFlush"); + wglMakeCurrent(w32_ctx->hdc, NULL); } @@ -240,6 +250,11 @@ static void releaseGlContext_w32(MPGLContext *ctx) if (w32_ctx->context) wglMakeCurrent(w32_ctx->hdc, 0); vo_w32_run_on_thread(ctx->vo, destroy_gl, ctx); + + w32_ctx->dwmflush = NULL; + if (w32_ctx->dwmapi_dll) + FreeLibrary(w32_ctx->dwmapi_dll); + w32_ctx->dwmapi_dll = NULL; } static void swapGlBuffers_w32(MPGLContext *ctx) @@ -248,6 +263,32 @@ static void swapGlBuffers_w32(MPGLContext *ctx) SwapBuffers(w32_ctx->hdc); } +// opt_dwmflush: 0 - never DwmFlush, 1 - only in windowed mode, 2 - always +// return: the current (applied if modified) SwapInterval value. +// DwmFlush waits on DWM vsync similar to SwapBuffers but a bit more noisy. +// SwapBuffers still needs to be called, but we SwapInterval(0) when DwmFLush is +// used (will get applied for the following SwapBuffers calls) +static int DwmFlush_w32(MPGLContext *ctx, int opt_dwmflush, + int opt_swapinterval, int current_swapinterval) +{ + struct w32_context *w32_ctx = ctx->priv; + int new_swapinterval = opt_swapinterval; // default if we don't DwmFLush + + if (w32_ctx->dwmflush && + (opt_dwmflush == 2 || (opt_dwmflush == 1 && !ctx->vo->opts->fullscreen)) && + S_OK == w32_ctx->dwmflush()) + { + new_swapinterval = 0; + } + + if ((new_swapinterval != current_swapinterval) && ctx->gl->SwapInterval) { + ctx->gl->SwapInterval(new_swapinterval); + MP_VERBOSE(ctx->vo, "DwmFlush: set SwapInterval(%d)\n", new_swapinterval); + } + + return new_swapinterval; +} + void mpgl_set_backend_w32(MPGLContext *ctx) { ctx->priv = talloc_zero(ctx, struct w32_context); @@ -257,4 +298,5 @@ void mpgl_set_backend_w32(MPGLContext *ctx) ctx->vo_init = vo_w32_init; ctx->vo_uninit = vo_w32_uninit; ctx->vo_control = vo_w32_control; + ctx->DwmFlush = DwmFlush_w32; } diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c index 589ae7b0db..fb47464e9a 100644 --- a/video/out/vo_opengl.c +++ b/video/out/vo_opengl.c @@ -72,6 +72,9 @@ struct gl_priv { int use_gl_debug; int allow_sw; int swap_interval; + int current_swap_interval; + int dwm_flush; + char *backend; int vo_flipped; @@ -154,6 +157,12 @@ static void flip_page(struct vo *vo) } } + if (p->glctx->DwmFlush) { + p->current_swap_interval = p->glctx->DwmFlush(p->glctx, p->dwm_flush, + p->swap_interval, + p->current_swap_interval); + } + mpgl_unlock(p->glctx); } @@ -463,6 +472,7 @@ static int preinit(struct vo *vo) } else { MP_VERBOSE(vo, "swap_control extension missing.\n"); } + p->current_swap_interval = p->swap_interval; p->renderer = gl_video_init(p->gl, vo->log); if (!p->renderer) @@ -498,6 +508,7 @@ static const struct m_option options[] = { OPT_FLAG("glfinish", use_glFinish, 0), OPT_FLAG("waitvsync", waitvsync, 0), OPT_INT("swapinterval", swap_interval, 0, OPTDEF_INT(1)), + OPT_INT("dwmflush", dwm_flush, 0, OPTDEF_INT(0)), OPT_FLAG("debug", use_gl_debug, 0), OPT_STRING_VALIDATE("backend", backend, 0, mpgl_validate_backend_opt), OPT_FLAG("sw", allow_sw, 0),