Improvement #7: Add GLX_OML_sync_control VSync support

- Add "vsync-oml" VSync method, using GLX_OML_sync_control. Untested,
  because it's not supported by my driver.

- Unredirect ps->reg_win, because DRI wiki says it's related to the
  behavior of OpenGL VSync extensions.

- Add glFlush() and glXWaitX() calls, in hope they are slightly helpful
  for VSync.

- Change a few functions to make error handling more graceful. Make some
  errors fatal. Code clean-up.

- Add unused function make_text_prop().
This commit is contained in:
Richard Grenville 2013-01-30 13:41:08 +08:00
parent 42e17cb4e9
commit 1b5273c819
4 changed files with 220 additions and 118 deletions

View File

@ -44,6 +44,7 @@ endif
# ==== OpenGL VSync ====
ifeq "$(NO_VSYNC_OPENGL)" ""
CFG += -DCONFIG_VSYNC_OPENGL
# -lGL must precede some other libraries, or it segfaults on FreeBSD (#74)
LIBS := -lGL $(LIBS)
endif

View File

@ -248,12 +248,16 @@ typedef enum {
VSYNC_NONE,
VSYNC_DRM,
VSYNC_OPENGL,
VSYNC_OPENGL_OML,
NUM_VSYNC,
} vsync_t;
#ifdef CONFIG_VSYNC_OPENGL
typedef int (*f_WaitVideoSync) (int, int, unsigned *);
typedef int (*f_GetVideoSync) (unsigned *);
typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc);
typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc);
#endif
typedef struct {
@ -536,9 +540,13 @@ typedef struct {
/// GLX context.
GLXContext glx_context;
/// Pointer to glXGetVideoSyncSGI function.
f_GetVideoSync glx_get_video_sync;
f_GetVideoSync glXGetVideoSyncSGI;
/// Pointer to glXWaitVideoSyncSGI function.
f_WaitVideoSync glx_wait_video_sync;
f_WaitVideoSync glXWaitVideoSyncSGI;
/// Pointer to glXGetSyncValuesOML function.
f_GetSyncValuesOML glXGetSyncValuesOML;
/// Pointer to glXWaitForMscOML function.
f_WaitForMscOML glXWaitForMscOML;
#endif
// === X extension related ===

View File

@ -31,11 +31,30 @@ const char * const WINTYPES[NUM_WINTYPES] = {
"dnd",
};
/// Names of VSync modes
/// Names of VSync modes.
const char * const VSYNC_STRS[NUM_VSYNC] = {
"none", // VSYNC_NONE
"drm", // VSYNC_DRM
"opengl", // VSYNC_OPENGL
"none", // VSYNC_NONE
"drm", // VSYNC_DRM
"opengl", // VSYNC_OPENGL
"opengl-oml", // VSYNC_OPENGL_OML
};
/// Function pointers to init VSync modes.
static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = {
[VSYNC_DRM ] = vsync_drm_init,
[VSYNC_OPENGL ] = vsync_opengl_init,
[VSYNC_OPENGL_OML ] = vsync_opengl_oml_init,
};
/// Function pointers to wait for VSync.
static int (* const (VSYNC_FUNCS_WAIT[NUM_VSYNC]))(session_t *ps) = {
#ifdef CONFIG_VSYNC_DRM
[VSYNC_DRM ] = vsync_drm_wait,
#endif
#ifdef CONFIG_VSYNC_OPENGL
[VSYNC_OPENGL ] = vsync_opengl_wait,
[VSYNC_OPENGL_OML ] = vsync_opengl_oml_wait,
#endif
};
/// Names of root window properties that could point to a pixmap of
@ -1651,10 +1670,16 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
if (!ps->o.dbe)
XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, None);
if (VSYNC_NONE != ps->o.vsync) {
if (ps->o.vsync) {
// Make sure all previous requests are processed to achieve best
// effect
XSync(ps->dpy, False);
#ifdef CONFIG_VSYNC_OPENGL
if (ps->glx_context) {
glFlush();
glXWaitX();
}
#endif
}
// Wait for VBlank. We could do it aggressively (send the painting
@ -1685,6 +1710,13 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
XFlush(ps->dpy);
#ifdef CONFIG_VSYNC_OPENGL
if (ps->glx_context) {
glFlush();
glXWaitX();
}
#endif
#ifdef DEBUG_REPAINT
print_timestamp(ps);
struct timespec now = get_time_timespec();
@ -3952,84 +3984,85 @@ usage(void) {
/**
* Register a window as symbol, and initialize GLX context if wanted.
*/
static void
register_cm(session_t *ps, bool want_glxct) {
Atom a;
char *buf;
static bool
register_cm(session_t *ps, bool glx) {
XVisualInfo *pvi = NULL;
#ifdef CONFIG_VSYNC_OPENGL
// Create a window with the wanted GLX visual
if (want_glxct) {
XVisualInfo *pvi = NULL;
bool ret = false;
if (glx) {
// Get visual for the window
int attribs[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None };
pvi = glXChooseVisual(ps->dpy, ps->scr, attribs);
if (!pvi) {
fprintf(stderr, "register_cm(): Failed to choose visual required "
"by fake OpenGL VSync window. OpenGL VSync turned off.\n");
printf_errf("(): Failed to choose GLX visual.");
return false;
}
else {
// Create the window
XSetWindowAttributes swa = {
.colormap = XCreateColormap(ps->dpy, ps->root, pvi->visual, AllocNone),
.border_pixel = 0,
};
pvi->screen = ps->scr;
ps->reg_win = XCreateWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, pvi->depth,
InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa);
// Create the window
XSetWindowAttributes swa = {
.colormap = XCreateColormap(ps->dpy, ps->root, pvi->visual, AllocNone),
.border_pixel = 0,
};
if (!ps->reg_win)
fprintf(stderr, "register_cm(): Failed to create window required "
"by fake OpenGL VSync. OpenGL VSync turned off.\n");
else {
// Get GLX context
ps->glx_context = glXCreateContext(ps->dpy, pvi, None, GL_TRUE);
if (!ps->glx_context) {
fprintf(stderr, "register_cm(): Failed to get GLX context. "
"OpenGL VSync turned off.\n");
ps->o.vsync = VSYNC_NONE;
}
else {
// Attach GLX context
if (!(ret = glXMakeCurrent(ps->dpy, ps->reg_win, ps->glx_context)))
fprintf(stderr, "register_cm(): Failed to attach GLX context."
" OpenGL VSync turned off.\n");
}
}
pvi->screen = ps->scr;
ps->reg_win = XCreateWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, pvi->depth,
InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa);
}
// Otherwise, create a simple window
else
#endif
{
ps->reg_win = XCreateSimpleWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0,
None, None);
}
if (!ps->reg_win) {
printf_errf("(): Failed to create window.");
return false;
}
#ifdef CONFIG_VSYNC_OPENGL
if (glx) {
// Get GLX context
ps->glx_context = glXCreateContext(ps->dpy, pvi, None, GL_TRUE);
if (!ps->glx_context) {
printf_errf("(): Failed to get GLX context.");
return false;
}
if (pvi)
XFree(pvi);
if (!ret)
ps->o.vsync = VSYNC_NONE;
// Attach GLX context
if (!glXMakeCurrent(ps->dpy, ps->reg_win, ps->glx_context)) {
printf_errf("(): Failed to attach GLX context.");
return false;
}
}
#endif
if (!ps->reg_win)
ps->reg_win = XCreateSimpleWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0,
None, None);
if (pvi)
XFree(pvi);
Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr",
NULL, 0, NULL, NULL, NULL);
NULL, 0, NULL, NULL, NULL);
unsigned len = strlen(REGISTER_PROP) + 2;
int s = ps->scr;
{
unsigned len = strlen(REGISTER_PROP) + 2;
int s = ps->scr;
while (s >= 10) {
++len;
s /= 10;
while (s >= 10) {
++len;
s /= 10;
}
char *buf = malloc(len);
snprintf(buf, len, REGISTER_PROP "%d", ps->scr);
buf[len - 1] = '\0';
XSetSelectionOwner(ps->dpy, get_atom(ps, buf), ps->reg_win, 0);
free(buf);
}
buf = malloc(len);
snprintf(buf, len, REGISTER_PROP"%d", ps->scr);
a = get_atom(ps, buf);
free(buf);
XSetSelectionOwner(ps->dpy, a, ps->reg_win, 0);
return true;
}
/**
@ -4873,7 +4906,7 @@ vsync_drm_init(session_t *ps) {
#ifdef CONFIG_VSYNC_DRM
// Should we always open card0?
if ((ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) {
fprintf(stderr, "vsync_drm_init(): Failed to open device.\n");
printf_errf("(): Failed to open device.");
return false;
}
@ -4882,7 +4915,7 @@ vsync_drm_init(session_t *ps) {
return true;
#else
fprintf(stderr, "Program not compiled with DRM VSync support.\n");
printf_errf("(): Program not compiled with DRM VSync support.");
return false;
#endif
}
@ -4927,19 +4960,38 @@ static bool
vsync_opengl_init(session_t *ps) {
#ifdef CONFIG_VSYNC_OPENGL
// Get video sync functions
ps->glx_get_video_sync = (f_GetVideoSync)
ps->glXGetVideoSyncSGI = (f_GetVideoSync)
glXGetProcAddress ((const GLubyte *) "glXGetVideoSyncSGI");
ps->glx_wait_video_sync = (f_WaitVideoSync)
ps->glXWaitVideoSyncSGI = (f_WaitVideoSync)
glXGetProcAddress ((const GLubyte *) "glXWaitVideoSyncSGI");
if (!ps->glx_wait_video_sync || !ps->glx_get_video_sync) {
fprintf(stderr, "vsync_opengl_init(): "
"Failed to get glXWait/GetVideoSyncSGI function.\n");
if (!ps->glXWaitVideoSyncSGI || !ps->glXGetVideoSyncSGI) {
printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function.");
return false;
}
return true;
#else
fprintf(stderr, "Program not compiled with OpenGL VSync support.\n");
printf_errfq(1, "Program not compiled with OpenGL VSync support.");
return false;
#endif
}
static bool
vsync_opengl_oml_init(session_t *ps) {
#ifdef CONFIG_VSYNC_OPENGL
// Get video sync functions
ps->glXGetSyncValuesOML= (f_GetSyncValuesOML)
glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML");
ps->glXWaitForMscOML = (f_WaitForMscOML)
glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML");
if (!ps->glXGetSyncValuesOML || !ps->glXWaitForMscOML) {
printf_errf("(): Failed to get OML_sync_control functions.");
return false;
}
return true;
#else
printf_errfq(1, "Program not compiled with OpenGL VSync support.");
return false;
#endif
}
@ -4948,13 +5000,31 @@ vsync_opengl_init(session_t *ps) {
/**
* Wait for next VSync, OpenGL method.
*/
static void
static int
vsync_opengl_wait(session_t *ps) {
unsigned vblank_count;
unsigned vblank_count = 0;
ps->glx_get_video_sync(&vblank_count);
ps->glx_wait_video_sync(2, (vblank_count + 1) % 2, &vblank_count);
ps->glXGetVideoSyncSGI(&vblank_count);
ps->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count);
// I see some code calling glXSwapIntervalSGI(1) afterwards, is it required?
return 0;
}
/**
* Wait for next VSync, OpenGL OML method.
*
* https://mail.gnome.org/archives/clutter-list/2012-November/msg00031.html
*/
static int
vsync_opengl_oml_wait(session_t *ps) {
int64_t ust = 0, msc = 0, sbc = 0;
ps->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc);
ps->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2,
&ust, &msc, &sbc);
return 0;
}
#endif
@ -4963,27 +5033,13 @@ vsync_opengl_wait(session_t *ps) {
*/
static void
vsync_wait(session_t *ps) {
if (VSYNC_NONE == ps->o.vsync)
if (!ps->o.vsync)
return;
#ifdef CONFIG_VSYNC_DRM
if (VSYNC_DRM == ps->o.vsync) {
vsync_drm_wait(ps);
return;
}
#endif
assert(VSYNC_FUNCS_WAIT[ps->o.vsync]);
#ifdef CONFIG_VSYNC_OPENGL
if (VSYNC_OPENGL == ps->o.vsync) {
vsync_opengl_wait(ps);
return;
}
#endif
// This place should not reached!
assert(0);
return;
if (VSYNC_FUNCS_WAIT[ps->o.vsync])
VSYNC_FUNCS_WAIT[ps->o.vsync](ps);
}
/**
@ -5008,15 +5064,16 @@ init_alpha_picts(session_t *ps) {
/**
* Initialize double buffer.
*/
static void
static bool
init_dbe(session_t *ps) {
if (!(ps->root_dbe = XdbeAllocateBackBufferName(ps->dpy,
(ps->o.paint_on_overlay ? ps->overlay: ps->root), XdbeCopied))) {
fprintf(stderr, "Failed to create double buffer. Double buffering "
"turned off.\n");
ps->o.dbe = false;
return;
printf_errf("(): Failed to create double buffer. Double buffering "
"cannot work.");
return false;
}
return true;
}
/**
@ -5090,6 +5147,10 @@ redir_start(session_t *ps) {
XCompositeRedirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual);
// Unredirect reg_win as this may have an effect on VSync:
// < http://dri.freedesktop.org/wiki/CompositeSwap >
XCompositeUnredirectWindow(ps->dpy, ps->reg_win, CompositeRedirectManual);
// Must call XSync() here
XSync(ps->dpy, False);
@ -5478,8 +5539,10 @@ session_init(session_t *ps_old, int argc, char **argv) {
#ifdef CONFIG_VSYNC_OPENGL
.glx_context = None,
.glx_get_video_sync = NULL,
.glx_wait_video_sync = NULL,
.glXGetVideoSyncSGI = NULL,
.glXWaitVideoSyncSGI = NULL,
.glXGetSyncValuesOML = NULL,
.glXWaitForMscOML = NULL,
#endif
.xfixes_event = 0,
@ -5567,6 +5630,9 @@ session_init(session_t *ps_old, int argc, char **argv) {
ps->vis = DefaultVisual(ps->dpy, ps->scr);
ps->depth = DefaultDepth(ps->dpy, ps->scr);
bool want_glx = (VSYNC_OPENGL == ps->o.vsync
|| VSYNC_OPENGL_OML == ps->o.vsync);
if (!XRenderQueryExtension(ps->dpy,
&ps->render_event, &ps->render_error)) {
fprintf(stderr, "No render extension\n");
@ -5609,21 +5675,22 @@ session_init(session_t *ps_old, int argc, char **argv) {
if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error))
ps->randr_exists = true;
else
fprintf(stderr, "No XRandR extension, automatic refresh rate "
"detection impossible.\n");
printf_errf("(): No XRandR extension, automatic refresh rate "
"detection impossible.");
}
#ifdef CONFIG_VSYNC_OPENGL
// Query X GLX extension
if (VSYNC_OPENGL == ps->o.vsync) {
if (want_glx) {
#ifdef CONFIG_VSYNC_OPENGL
if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error))
ps->glx_exists = true;
else {
fprintf(stderr, "No GLX extension, OpenGL VSync impossible.\n");
ps->o.vsync = VSYNC_NONE;
printf_errfq(1, "(): No GLX extension, OpenGL VSync impossible.");
}
}
#else
printf_errfq(1, "(): OpenGL VSync support not compiled in.");
#endif
}
// Query X DBE extension
if (ps->o.dbe) {
@ -5641,23 +5708,24 @@ session_init(session_t *ps_old, int argc, char **argv) {
ps->o.dbe = false;
}
register_cm(ps, (VSYNC_OPENGL == ps->o.vsync));
if (!register_cm(ps, want_glx))
exit(1);
// Initialize software optimization
if (ps->o.sw_opti)
ps->o.sw_opti = swopti_init(ps);
// Initialize DRM/OpenGL VSync
if ((VSYNC_DRM == ps->o.vsync && !vsync_drm_init(ps))
|| (VSYNC_OPENGL == ps->o.vsync && !vsync_opengl_init(ps)))
ps->o.vsync = VSYNC_NONE;
// Initialize VSync
if (ps->o.vsync && VSYNC_FUNCS_INIT[ps->o.vsync]
&& !VSYNC_FUNCS_INIT[ps->o.vsync](ps))
exit(1);
// Overlay must be initialized before double buffer
if (ps->o.paint_on_overlay)
init_overlay(ps);
if (ps->o.dbe)
init_dbe(ps);
if (ps->o.dbe && !init_dbe(ps))
exit(1);
init_atoms(ps);
init_alpha_picts(ps);

View File

@ -216,6 +216,25 @@ ms_to_tv(int timeout) {
};
}
/**
* Create a XTextProperty of a single string.
*/
static inline XTextProperty *
make_text_prop(session_t *ps, char *str) {
XTextProperty *pprop = malloc(sizeof(XTextProperty));
if (!pprop)
printf_errfq(1, "(): Failed to allocate memory.");
if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) {
if (pprop->value)
XFree(pprop->value);
free(pprop);
pprop = NULL;
}
return pprop;
}
static void
run_fade(session_t *ps, win *w, unsigned steps);
@ -685,8 +704,8 @@ ev_window(session_t *ps, XEvent *ev);
static void __attribute__ ((noreturn))
usage(void);
static void
register_cm(session_t *ps, bool want_glxct);
static bool
register_cm(session_t *ps, bool glx);
inline static void
ev_focus_in(session_t *ps, XFocusChangeEvent *ev);
@ -878,9 +897,15 @@ vsync_drm_wait(session_t *ps);
static bool
vsync_opengl_init(session_t *ps);
static bool
vsync_opengl_oml_init(session_t *ps);
#ifdef CONFIG_VSYNC_OPENGL
static void
static int
vsync_opengl_wait(session_t *ps);
static int
vsync_opengl_oml_wait(session_t *ps);
#endif
static void
@ -889,7 +914,7 @@ vsync_wait(session_t *ps);
static void
init_alpha_picts(session_t *ps);
static void
static bool
init_dbe(session_t *ps);
static void