From 4b734c1fa1474e768bbb0ef32fab0c2b0bea5276 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 21 Mar 2013 13:05:56 +0800 Subject: [PATCH] Improvement: --glx-use-copysubbuffermesa - GLX backend: Add --glx-use-copysubbuffermesa, to use MESA_copy_sub_buffer to do partial screen update. Huge performance boost on mesa drivers for partial screen updates, but does not work for nvidia-drivers and may break VSync. Automagically overrides --glx-copy-from-front. - Add rect_is_fullscreen() to reuse code. Misc changes. --- README.md | 1 + man/compton.1.asciidoc | 17 +++++++++++++-- src/common.h | 18 ++++++++++++++++ src/compton.c | 24 ++++++++++++++++----- src/compton.h | 6 ++---- src/opengl.c | 48 +++++++++++++++++++++++++++++++++++++++--- 6 files changed, 100 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2a451fd..670505d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ partially doing this out of a desire to learn Xlib. ## Changes from xcompmgr: +* OpenGL backend (`--backend glx`), in addition to the old X Render backend. * __inactive window transparency / dimming__ * __titlebar/frame transparency__ (specified with `-e`) * menu transparency (thanks to Dana) diff --git a/man/compton.1.asciidoc b/man/compton.1.asciidoc index 97a27de..6248180 100644 --- a/man/compton.1.asciidoc +++ b/man/compton.1.asciidoc @@ -127,6 +127,7 @@ OPTIONS * 'drm': VSync with 'DRM_IOCTL_WAIT_VBLANK'. May only work on some drivers. Experimental. * 'opengl': Try to VSync with 'SGI_swap_control' OpenGL extension. Only work on some drivers. Experimental. * 'opengl-oml': Try to VSync with 'OML_sync_control' OpenGL extension. Only work on some drivers. Experimental. +* 'opengl-swc': Try to VSync with 'SGI_swap_control' OpenGL extension. Only work on some drivers. Works only with GLX backend. Known to be most effective on many drivers. Does not actually control paint timing, only buffer swap is affected, so it doesn't have the effect of *--sw-opti* unlike other methods. Experimental. (Note some VSync methods may not be enabled at compile time.) -- @@ -176,6 +177,18 @@ OPTIONS *--invert-color-include* condition:: Specify a list of conditions of windows that should be painted with inverted color. Resource-hogging, and is not well tested. +*--backend* backend:: + Specify the backend to use: 'xrender` or 'glx'. GLX (OpenGL) backend generally has much superior performance as far as you have a graphic card/chip and driver. + +*--glx-no-stencil*:: + GLX backend: Avoid using stencil buffer, useful if you don't have a stencil buffer. Might cause incorrect opacity when rendering transparent content and cannot work with *--blur-background*. May have a positive or negative effect on performance. (My test shows a 10% slowdown.) + +*--glx-copy-from-front*:: + GLX backend: Copy unmodified regions from front buffer instead of redrawing them all. My tests with nvidia-drivers show a 10% decrease in performance when the whole screen is modified, but a 20% increase when only 1/4 is. My tests on nouveau show terrible slowdown. + +*--glx-use-copysubbuffermesa*:: + GLX backend: Use MESA_copy_sub_buffer to do partial screen update. My tests on nouveau shows a 200% performance boost when only 1/4 of the screen is updated. May break VSync and is not available on some drivers. Overrides *--glx-copy-from-front*. + *--dbus*:: Enable remote control via D-Bus. See the *D-BUS API* section below for more details. @@ -312,10 +325,10 @@ $ compton -c --shadow-red 1 --shadow-green 1 --shadow-blue 1 $ compton -c --shadow-exclude 'class_g = "wbar"' ------------ -* Enable OpenGL vsync: +* Enable OpenGL SGI_swap_control VSync with GLX backend: + ------------ -$ compton --vsync opengl +$ compton --backend glx --vsync opengl-swc ------------ BUGS diff --git a/src/common.h b/src/common.h index 977643b..2f8b6e2 100644 --- a/src/common.h +++ b/src/common.h @@ -284,6 +284,8 @@ typedef int (*f_SwapIntervalSGI) (int interval); typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list); typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer); +typedef void (*f_CopySubBuffer) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); + /// @brief Wrapper of a GLX FBConfig. typedef struct { GLXFBConfig cfg; @@ -344,6 +346,8 @@ typedef struct { bool glx_no_stencil; /// Whether to copy unmodified regions from front buffer. bool glx_copy_from_front; + /// Whether to use glXCopySubBufferMESA() to update screen. + bool glx_use_copysubbuffermesa; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -624,6 +628,8 @@ typedef struct { f_BindTexImageEXT glXBindTexImageProc; /// Pointer to glXReleaseTexImageEXT function. f_ReleaseTexImageEXT glXReleaseTexImageProc; + /// Pointer to glXCopySubBufferMESA function. + f_CopySubBuffer glXCopySubBufferProc; /// FBConfig-s for GLX pixmap of different depths. glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; #ifdef CONFIG_VSYNC_OPENGL_GLSL @@ -1490,6 +1496,15 @@ free_region(session_t *ps, XserverRegion *p) { } } +/** + * Check if a rectangle includes the whole screen. + */ +static inline bool +rect_is_fullscreen(session_t *ps, int x, int y, unsigned wid, unsigned hei) { + return (x <= 0 && y <= 0 + && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height); +} + /** * Determine if a window has a specific property. * @@ -1621,6 +1636,9 @@ glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool neg, XserverRegion reg_tgt); +void +glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); + #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str); diff --git a/src/compton.c b/src/compton.c index 9e8623e..811bbf1 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1736,7 +1736,6 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } // Free up all temporary regions - XFixesDestroyRegion(ps->dpy, region); XFixesDestroyRegion(ps->dpy, reg_tmp); XFixesDestroyRegion(ps->dpy, reg_tmp2); @@ -1783,7 +1782,10 @@ paint_all(session_t *ps, XserverRegion region, win *t) { break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glXSwapBuffers(ps->dpy, get_tgt_window(ps)); + if (ps->o.glx_use_copysubbuffermesa) + glx_swap_copysubbuffermesa(ps, region); + else + glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break; #endif default: @@ -1802,6 +1804,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } #endif + XFixesDestroyRegion(ps->dpy, region); + #ifdef DEBUG_REPAINT print_timestamp(ps); struct timespec now = get_time_timespec(); @@ -4151,9 +4155,14 @@ usage(void) { " negative effect on performance. (My test shows a 10% slowdown.)\n" "--glx-copy-from-front\n" " GLX backend: Copy unmodified regions from front buffer instead of\n" - " redrawing them all. My tests show a 10% decrease in performance\n" - " when the whole screen is modified, but a 20% increase when only 1/4\n" - " is, so this optimization is not enabled by default.\n" + " redrawing them all. My tests with nvidia-drivers show a 10% decrease\n" + " in performance when the whole screen is modified, but a 20% increase\n" + " when only 1/4 is. My tests on nouveau show terrible slowdown.\n" + "--glx-use-copysubbuffermesa\n" + " GLX backend: Use MESA_copy_sub_buffer to do partial screen update.\n" + " My tests on nouveau shows a 200% performance boost when only 1/4 of\n" + " the screen is updated. May break VSync and is not available on some\n" + " drivers. Overrides --glx-copy-from-front.\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4620,6 +4629,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "glx-copy-from-front", no_argument, NULL, 292 }, { "benchmark", required_argument, NULL, 293 }, { "benchmark-wid", required_argument, NULL, 294 }, + { "glx-use-copysubbuffermesa", no_argument, NULL, 295 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4898,6 +4908,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --benchmark-wid ps->o.benchmark_wid = strtol(optarg, NULL, 0); break; + case 295: + // --glx-use-copysubbuffermesa + ps->o.glx_use_copysubbuffermesa = true; + break; default: usage(); break; diff --git a/src/compton.h b/src/compton.h index c16c596..b590b0e 100644 --- a/src/compton.h +++ b/src/compton.h @@ -427,10 +427,8 @@ dump_drawable(session_t *ps, Drawable drawable) { */ static inline bool win_is_fullscreen(session_t *ps, const win *w) { - return (w->a.x <= 0 && w->a.y <= 0 - && (w->a.x + w->widthb) >= ps->root_width - && (w->a.y + w->heightb) >= ps->root_height - && !w->bounding_shaped); + return rect_is_fullscreen(ps, w->a.x, w->a.y, w->widthb, w->heightb) + && !w->bounding_shaped; } static void diff --git a/src/opengl.c b/src/opengl.c index a0bed63..3f1f419 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -96,6 +96,15 @@ glx_init(session_t *ps, bool need_render) { printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT()."); goto glx_init_end; } + + if (ps->o.glx_use_copysubbuffermesa) { + ps->glXCopySubBufferProc = (f_CopySubBuffer) + glXGetProcAddress((const GLubyte *) "glXCopySubBufferMESA"); + if (!ps->glXCopySubBufferProc) { + printf_errf("(): Failed to acquire glXCopySubBufferMESA()."); + goto glx_init_end; + } + } } // Acquire FBConfigs @@ -543,10 +552,13 @@ void glx_paint_pre(session_t *ps, XserverRegion *preg) { ps->glx_z = 0.0; // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // OpenGL doesn't support partial repaint without GLX_MESA_copy_sub_buffer, - // we currently redraw the whole screen or copy unmodified pixels from + // we could redraw the whole screen or copy unmodified pixels from // front buffer with --glx-copy-from-front. - if (!ps->o.glx_copy_from_front || !*preg) { + if (ps->o.glx_use_copysubbuffermesa || !*preg) { + } + else if (!ps->o.glx_copy_from_front) { free_region(ps, preg); } else { @@ -612,7 +624,6 @@ glx_set_clip(session_t *ps, XserverRegion reg) { glDepthMask(GL_FALSE); glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); - glBegin(GL_QUADS); for (int i = 0; i < nrects; ++i) { @@ -853,6 +864,37 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return true; } +/** + * Swap buffer with glXCopySubBufferMESA(). + */ +void +glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { + int nrects = 0; + XRectangle *rects = XFixesFetchRegion(ps->dpy, reg, &nrects); + + if (1 == nrects && rect_is_fullscreen(ps, rects[0].x, rects[0].y, + rects[0].width, rects[0].height)) { + glXSwapBuffers(ps->dpy, get_tgt_window(ps)); + } + else { + glx_set_clip(ps, None); + for (int i = 0; i < nrects; ++i) { + const int x = rects[i].x; + const int y = ps->root_height - rects[i].y - rects[i].height; + const int wid = rects[i].width; + const int hei = rects[i].height; + +#ifdef DEBUG_GLX + printf_dbgf("(): %d, %d, %d, %d\n", x, y, wid, hei); +#endif + ps->glXCopySubBufferProc(ps->dpy, get_tgt_window(ps), x, y, wid, hei); + } + } + + if (rects) + XFree(rects); +} + #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str) {