diff --git a/common.h b/common.h index bc057aa8d..4bc57bb8f 100644 --- a/common.h +++ b/common.h @@ -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 === diff --git a/compton.c b/compton.c index d42ca3e28..2389337e4 100644 --- a/compton.c +++ b/compton.c @@ -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); - 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"); - } - } - } - if (pvi) - XFree(pvi); + // Create the window + XSetWindowAttributes swa = { + .colormap = XCreateColormap(ps->dpy, ps->root, pvi->visual, AllocNone), + .border_pixel = 0, + }; - if (!ret) - ps->o.vsync = VSYNC_NONE; + 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 - - if (!ps->reg_win) + { ps->reg_win = XCreateSimpleWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, None, None); + } - Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", - NULL, 0, NULL, NULL, NULL); + if (!ps->reg_win) { + printf_errf("(): Failed to create window."); + return false; + } - unsigned len = strlen(REGISTER_PROP) + 2; - int s = ps->scr; +#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; + } - while (s >= 10) { - ++len; - s /= 10; + // Attach GLX context + if (!glXMakeCurrent(ps->dpy, ps->reg_win, ps->glx_context)) { + printf_errf("(): Failed to attach GLX context."); + return false; + } } +#endif - buf = malloc(len); - snprintf(buf, len, REGISTER_PROP"%d", ps->scr); + if (pvi) + XFree(pvi); - a = get_atom(ps, buf); - free(buf); + Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", + NULL, 0, NULL, NULL, NULL); + + { + unsigned len = strlen(REGISTER_PROP) + 2; + int s = ps->scr; - XSetSelectionOwner(ps->dpy, a, ps->reg_win, 0); + 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); + } + + 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 - -#ifdef CONFIG_VSYNC_OPENGL - if (VSYNC_OPENGL == ps->o.vsync) { - vsync_opengl_wait(ps); - return; - } -#endif + assert(VSYNC_FUNCS_WAIT[ps->o.vsync]); - // 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); diff --git a/compton.h b/compton.h index eac944127..ddc17044f 100644 --- a/compton.h +++ b/compton.h @@ -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