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.
This commit is contained in:
Richard Grenville 2013-03-21 13:05:56 +08:00
parent 8208ec6dc8
commit 4b734c1fa1
6 changed files with 100 additions and 14 deletions

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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) {