diff --git a/common.h b/common.h index 977643b60..2f8b6e20d 100644 --- a/common.h +++ b/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/compton.c b/compton.c index 9e8623ea8..811bbf17e 100644 --- a/compton.c +++ b/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/compton.h b/compton.h index c16c59612..b590b0ead 100644 --- a/compton.h +++ b/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/opengl.c b/opengl.c index a0bed63bb..3f1f419b7 100644 --- a/opengl.c +++ b/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) {