summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-10-30 20:26:51 +0100
committerwm4 <wm4@nowhere>2015-10-30 20:26:51 +0100
commit2b6241a09a136db26834e75aeb3730f00894ee54 (patch)
tree52b6401c2140d52c6d162fa8d458458ea965767b
parent93f748e77f1aeaeac3035552ed49aa33645bdfb0 (diff)
downloadmpv-2b6241a09a136db26834e75aeb3730f00894ee54.tar.bz2
mpv-2b6241a09a136db26834e75aeb3730f00894ee54.tar.xz
vo_opengl: add vsync-fences option
Yet another relatively useless option that tries to make OpenGL's sync behavior somewhat sane. The results are not too encouraging. With a value of 1, vsync jitter is gone on nVidia, but there are frame drops (less than with glfinish). With 2, I get the usual vsync jitter _and_ frame drops. There's still some hope that it might prevent too deep queuing with some GPUs, I guess. The timeout for the wait call is 1 second. The value is pretty arbitrary; it should just not be too high to freeze the process (if the GPU is un-nice), and not too low to trigger the timeout in normal cases, even if the GPU load is very high. So I guess 1 second is ok as a timeout. The idea to use fences this way to control the queue depth was stolen from RetroArch: https://github.com/libretro/RetroArch/blob/df01279cf318e7ec90ace039d60515296e3de908/gfx/drivers/gl.c#L1856
-rw-r--r--DOCS/man/vo.rst9
-rw-r--r--video/out/opengl/common.c10
-rw-r--r--video/out/opengl/common.h4
-rw-r--r--video/out/vo_opengl.c18
4 files changed, 41 insertions, 0 deletions
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 7b6358be6e..2cebd153ff 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -667,6 +667,15 @@ Available video output drivers are:
X11/GLX only.
+ ``vsync-fences=<N>``
+ Synchronize the CPU to the Nth past frame using the ``GL_ARB_sync``
+ extension. A value of 0 disables this behavior (default). A value of
+ 1 means it will synchronize to the current frame after rendering it.
+ Like ``glfinish`` and ``waitvsync``, this can lower or ruin performance.
+ Its advantage is that it can span multiple frames, and effectively limit
+ the number of frames the GPU queues ahead (which also has an influence
+ on vsync).
+
``dwmflush=<no|windowed|yes>``
Calls ``DwmFlush`` after swapping buffers on Windows (default: no).
It also sets ``SwapInterval(0)`` to ignore the OpenGL timing. Values
diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c
index f045184373..fae969ee2a 100644
--- a/video/out/opengl/common.c
+++ b/video/out/opengl/common.c
@@ -230,6 +230,16 @@ static const struct gl_functions gl_functions[] = {
.extension = "GL_ARB_texture_rg",
.provides = MPGL_CAP_TEX_RG,
},
+ {
+ .ver_core = 320,
+ .extension = "GL_ARB_sync",
+ .functions = (const struct gl_function[]) {
+ DEF_FN(FenceSync),
+ DEF_FN(ClientWaitSync),
+ DEF_FN(DeleteSync),
+ {0}
+ },
+ },
// Swap control, always an OS specific extension
// The OSX code loads this manually.
{
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index 35d303e96c..3f5ab0ebde 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -244,6 +244,10 @@ struct GL {
void (GLAPIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean,
const GLfloat *);
+ GLsync (GLAPIENTRY *FenceSync)(GLenum, GLbitfield);
+ GLenum (GLAPIENTRY *ClientWaitSync)(GLsync, GLbitfield, GLuint64);
+ void (GLAPIENTRY *DeleteSync)(GLsync sync);
+
void (GLAPIENTRY *VDPAUInitNV)(const GLvoid *, const GLvoid *);
void (GLAPIENTRY *VDPAUFiniNV)(void);
GLvdpauSurfaceNV (GLAPIENTRY *VDPAURegisterOutputSurfaceNV)
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index d55b5f5075..f5a3cdcf73 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -51,6 +51,8 @@
#include "opengl/video.h"
#include "opengl/lcms.h"
+#define NUM_VSYNC_FENCES 10
+
struct gl_priv {
struct vo *vo;
struct mp_log *log;
@@ -72,6 +74,7 @@ struct gl_priv {
int allow_sw;
int swap_interval;
int dwm_flush;
+ int opt_vsync_fences;
char *backend;
int es;
@@ -83,6 +86,9 @@ struct gl_priv {
int opt_pattern[2];
int last_pattern;
int matches, mismatches;
+
+ GLsync vsync_fences[NUM_VSYNC_FENCES];
+ int num_vsync_fences;
};
static void resize(struct gl_priv *p)
@@ -121,6 +127,12 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
+ if (gl->FenceSync && p->num_vsync_fences < p->opt_vsync_fences) {
+ GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);;
+ if (fence)
+ p->vsync_fences[p->num_vsync_fences++] = fence;
+ }
+
gl_video_render_frame(p->renderer, frame, 0);
// The playloop calls this last before waiting some time until it decides
@@ -163,6 +175,11 @@ static void flip_page(struct vo *vo)
p->opt_pattern[0] = 0;
}
}
+ while (p->opt_vsync_fences > 0 && p->num_vsync_fences >= p->opt_vsync_fences) {
+ gl->ClientWaitSync(p->vsync_fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9);
+ gl->DeleteSync(p->vsync_fences[0]);
+ MP_TARRAY_REMOVE_AT(p->vsync_fences, p->num_vsync_fences, 0);
+ }
}
static int query_format(struct vo *vo, int format)
@@ -449,6 +466,7 @@ static const struct m_option options[] = {
OPT_FLAG("sw", allow_sw, 0),
OPT_FLAG("es", es, 0),
OPT_INTPAIR("check-pattern", opt_pattern, 0),
+ OPT_INTRANGE("vsync-fences", opt_vsync_fences, 0, 0, NUM_VSYNC_FENCES),
OPT_SUBSTRUCT("", renderer_opts, gl_video_conf, 0),
OPT_SUBSTRUCT("", icc_opts, mp_icc_conf, 0),