diff --git a/src/common.h b/src/common.h index 3b66208..978a9ec 100644 --- a/src/common.h +++ b/src/common.h @@ -297,6 +297,7 @@ struct _glx_texture { GLuint texture; GLXPixmap glpixmap; Pixmap pixmap; + GLenum target; unsigned width; unsigned height; unsigned depth; @@ -341,6 +342,8 @@ typedef struct { enum backend backend; /// Whether to avoid using stencil buffer under GLX backend. Might be unsafe. bool glx_no_stencil; + /// Whether to copy unmodified regions from front buffer. + bool glx_copy_from_front; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -359,6 +362,10 @@ typedef struct { bool dbus; /// Path to log file. char *logpath; + /// Number of cycles to paint in benchmark mode. 0 for disabled. + int benchmark; + /// Window to constantly repaint in benchmark mode. 0 for full-screen. + Window benchmark_wid; /// Whether to work under synchronized mode for debugging. bool synchronize; @@ -1578,6 +1585,9 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, void glx_release_pixmap(session_t *ps, glx_texture_t *ptex); +void +glx_paint_pre(session_t *ps, XserverRegion *preg); + /** * Check if a texture is binded, or is binded to the given pixmap. */ diff --git a/src/compton.c b/src/compton.c index a2fbee2..2b1ca92 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1569,12 +1569,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) { XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; #ifdef CONFIG_VSYNC_OPENGL - // GLX backend: OpenGL doesn't support partial repaint without - // GLX_MESA_copy_sub_buffer if (BKEND_GLX == ps->o.backend) { - free_region(ps, ®ion); - // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - ps->glx_z = 0.0; + glx_paint_pre(ps, ®ion); } #endif @@ -4134,9 +4130,14 @@ usage(void) { "--backend backend\n" " Choose backend. Possible choices are xrender and glx" WARNING ".\n" "--glx-no-stencil\n" - " Avoid using stencil buffer under GLX backend. Might cause issues\n" - " when rendering transparent content, may have a positive or\n" + " GLX backend: Avoid using stencil buffer. Might cause issues\n" + " when rendering transparent content. May have a positive or\n" " 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" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4145,7 +4146,13 @@ usage(void) { #endif "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" - " man page for more details." WARNING "\n"; + " man page for more details." WARNING "\n" + "--benchmark cycles\n" + " Benchmark mode. Repeatedly paint until reaching the specified cycles.\n" + "--benchmark-wid window-id\n" + " Specify window ID to repaint in benchmark mode. If omitted or is 0,\n" + " the whole screen is repainted.\n" + ; fputs(usage_text , stderr); #undef WARNING #undef WARNING_DISABLED @@ -4594,6 +4601,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "opengl", no_argument, NULL, 289 }, { "backend", required_argument, NULL, 290 }, { "glx-no-stencil", no_argument, NULL, 291 }, + { "glx-copy-from-front", no_argument, NULL, 292 }, + { "benchmark", required_argument, NULL, 293 }, + { "benchmark-wid", required_argument, NULL, 294 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4860,6 +4870,18 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --glx-no-stencil ps->o.glx_no_stencil = true; break; + case 292: + // --glx-copy-from-front + ps->o.glx_copy_from_front = true; + break; + case 293: + // --benchmark + ps->o.benchmark = atoi(optarg); + break; + case 294: + // --benchmark-wid + ps->o.benchmark_wid = strtol(optarg, NULL, 0); + break; default: usage(); break; @@ -5575,7 +5597,7 @@ mainloop(session_t *ps) { struct timeval *ptv = NULL; { // Consider ev_received firstly - if (ps->ev_received) { + if (ps->ev_received || ps->o.benchmark) { ptv = malloc(sizeof(struct timeval)); ptv->tv_sec = 0L; ptv->tv_usec = 0L; @@ -5658,6 +5680,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .display = NULL, .backend = BKEND_XRENDER, .glx_no_stencil = false, + .glx_copy_from_front = false, .mark_wmwin_focused = false, .mark_ovredir_focused = false, .fork_after_register = false, @@ -5666,6 +5689,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .paint_on_overlay = false, .unredir_if_possible = false, .dbus = false, + .benchmark = 0, + .benchmark_wid = None, .logpath = NULL, .refresh_rate = 0, @@ -6249,6 +6274,21 @@ session_run(session_t *ps) { while (mainloop(ps)) continue; + if (ps->o.benchmark) { + if (ps->o.benchmark_wid) { + win *w = find_win(ps, ps->o.benchmark_wid); + if (!w) { + printf_errf("(): Couldn't find specified benchmark window."); + session_destroy(ps); + exit(1); + } + add_damage_win(ps, w); + } + else { + force_repaint(ps); + } + } + // idling will be turned off during paint_preprocess() if needed ps->idling = true; @@ -6263,6 +6303,8 @@ session_run(session_t *ps) { paint_all(ps, ps->all_damage, t); ps->reg_ignore_expire = false; paint++; + if (ps->o.benchmark && paint >= ps->o.benchmark) + exit(0); XSync(ps->dpy, False); ps->all_damage = None; } diff --git a/src/opengl.c b/src/opengl.c index 92b0292..eca9166 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -163,7 +163,7 @@ glx_on_root_change(session_t *ps) { // Initialize matrix, copied from dcompmgr glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0); + glOrtho(0, ps->root_width, 0, ps->root_height, -1000.0, 1000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } @@ -328,7 +328,6 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, return false; } - const GLenum target = GL_TEXTURE_2D; glx_texture_t *ptex = *pptex; bool need_release = true; @@ -338,6 +337,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, .texture = 0, .glpixmap = 0, .pixmap = 0, + .target = 0, .width = 0, .height = 0, .depth = 0, @@ -350,37 +350,11 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, *pptex = ptex; } - glEnable(target); - // Release pixmap if parameters are inconsistent if (ptex->texture && ptex->pixmap != pixmap) { glx_release_pixmap(ps, ptex); } - // Create texture - if (!ptex->texture) { - need_release = false; - - GLuint texture = 0; - glGenTextures(1, &texture); - glBindTexture(target, texture); - - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glBindTexture(target, 0); - - ptex->texture = texture; - } - if (!ptex->texture) { - printf_errf("(): Failed to allocate texture."); - return false; - } - - glBindTexture(target, ptex->texture); - // Create GLX pixmap if (!ptex->glpixmap) { need_release = false; @@ -409,7 +383,9 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, } // Determine texture target, copied from compiz - GLint tex_tgt = 0; + // The assumption we made here is the target never changes based on any + // pixmap-specific parameters, and this may change in the future + GLenum tex_tgt = 0; if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts && ps->glx_has_texture_non_power_of_two) tex_tgt = GLX_TEXTURE_2D_EXT; @@ -435,6 +411,8 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs); ptex->pixmap = pixmap; + ptex->target = (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D: + GL_TEXTURE_RECTANGLE); ptex->width = width; ptex->height = height; ptex->depth = depth; @@ -445,6 +423,32 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, return false; } + glEnable(ptex->target); + + // Create texture + if (!ptex->texture) { + need_release = false; + + GLuint texture = 0; + glGenTextures(1, &texture); + glBindTexture(ptex->target, texture); + + glTexParameteri(ptex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(ptex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(ptex->target, 0); + + ptex->texture = texture; + } + if (!ptex->texture) { + printf_errf("(): Failed to allocate texture."); + return false; + } + + glBindTexture(ptex->target, ptex->texture); + // The specification requires rebinding whenever the content changes... // We can't follow this, too slow. if (need_release) @@ -453,8 +457,8 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); // Cleanup - glBindTexture(target, 0); - glDisable(target); + glBindTexture(ptex->target, 0); + glDisable(ptex->target); return true; } @@ -466,9 +470,9 @@ void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { // Release binding if (ptex->glpixmap && ptex->texture) { - glBindTexture(GL_TEXTURE_2D, ptex->texture); + glBindTexture(ptex->target, ptex->texture); ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); - glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(ptex->target, 0); } // Free GLX Pixmap @@ -478,6 +482,41 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { } } +/** + * Preprocess function before start painting. + */ +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 + // front buffer with --glx-copy-from-front. + if (!ps->o.glx_copy_from_front || !*preg) { + free_region(ps, preg); + } + else { + { + XserverRegion reg_copy = XFixesCreateRegion(ps->dpy, NULL, 0); + XFixesSubtractRegion(ps->dpy, reg_copy, ps->screen_reg, *preg); + glx_set_clip(ps, reg_copy); + free_region(ps, ®_copy); + } + + { + GLfloat raster_pos[4]; + glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos); + glReadBuffer(GL_FRONT); + glRasterPos2f(0.0, 0.0); + glCopyPixels(0, 0, ps->root_width, ps->root_height, GL_COLOR); + glReadBuffer(GL_BACK); + glRasterPos4fv(raster_pos); + } + } + + glx_set_clip(ps, *preg); +} + /** * Set clipping region on the target window. */ @@ -524,9 +563,9 @@ glx_set_clip(session_t *ps, XserverRegion reg) { for (int i = 0; i < nrects; ++i) { GLint rx = rects[i].x; - GLint ry = rects[i].y; + GLint ry = ps->root_height - rects[i].y; GLint rxe = rx + rects[i].width; - GLint rye = ry + rects[i].height; + GLint rye = ry - rects[i].height; GLint z = 0; #ifdef DEBUG_GLX @@ -550,6 +589,78 @@ glx_set_clip(session_t *ps, XserverRegion reg) { XFree(rects); } +bool +glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { + // Read destination pixels into a texture + GLuint tex_scr = 0; + glGenTextures(1, &tex_scr); + if (!tex_scr) { + printf_errf("(): Failed to allocate texture."); + return false; + } + + GLenum tex_tgt = GL_TEXTURE_RECTANGLE; + if (ps->glx_has_texture_non_power_of_two) + tex_tgt = GL_TEXTURE_2D; + + glEnable(tex_tgt); + glBindTexture(tex_tgt, tex_scr); + glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(tex_tgt, 0, GL_RGB, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + glCopyTexSubImage2D(tex_tgt, 0, 0, 0, dx, ps->root_height - dy - height, width, height); + +#ifdef DEBUG_GLX + printf_dbgf("(): %d, %d, %d, %d\n", dx, ps->root_height - dy - height, width, height); +#endif + + // Paint it back + // TODO: Blur function. We are using color negation for testing now. + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + + glBegin(GL_QUADS); + + { + const GLfloat rx = 0.0; + const GLfloat ry = 1.0; + const GLfloat rxe = 1.0; + const GLfloat rye = 0.0; + const GLint rdx = dx; + const GLint rdy = ps->root_height - dy; + const GLint rdxe = rdx + width; + const GLint rdye = rdy - height; + +#ifdef DEBUG_GLX + printf_dbgf("(): %f, %f, %f, %f -> %d, %d, %d, %d\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); +#endif + + glTexCoord2f(rx, ry); + glVertex3f(rdx, rdy, z); + + glTexCoord2f(rxe, ry); + glVertex3f(rdxe, rdy, z); + + glTexCoord2f(rxe, rye); + glVertex3f(rdxe, rdye, z); + + glTexCoord2f(rx, rye); + glVertex3f(rdx, rdye, z); + } + + glEnd(); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glBindTexture(tex_tgt, 0); + glDeleteTextures(1, &tex_scr); + glDisable(tex_tgt); + + return true; +} + /** * @brief Render a region with texture data. */ @@ -557,6 +668,8 @@ bool 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) { + bool blur_background = false; + if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; @@ -565,12 +678,15 @@ glx_render(session_t *ps, const glx_texture_t *ptex, // Enable blending if needed if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT == ps->glx_fbconfigs[ptex->depth]->texture_fmt) { + if (!ps->o.glx_no_stencil && blur_background) + glx_blur_dst(ps, dx, dy, width, height, z - 0.5); + glEnable(GL_BLEND); // Needed for handling opacity of ARGB texture glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - // This is all weird, but X Render is using a strange ARGB format, and + // This is all weird, but X Render is using premulitplied ARGB format, and // we need to use those things to correct it. Thanks to derhass for help. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glColor4f(opacity, opacity, opacity, opacity); @@ -618,8 +734,8 @@ glx_render(session_t *ps, const glx_texture_t *ptex, rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); } - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, ptex->texture); + glEnable(ptex->target); + glBindTexture(ptex->target, ptex->texture); glBegin(GL_QUADS); @@ -629,9 +745,9 @@ glx_render(session_t *ps, const glx_texture_t *ptex, GLfloat rxe = rx + (double) rects[i].width / ptex->width; GLfloat rye = ry + (double) rects[i].height / ptex->height; GLint rdx = rects[i].x; - GLint rdy = rects[i].y; + GLint rdy = ps->root_height - rects[i].y; GLint rdxe = rdx + rects[i].width; - GLint rdye = rdy + rects[i].height; + GLint rdye = rdy - rects[i].height; // Invert Y if needed, this may not work as expected, though. I don't // have such a FBConfig to test with. @@ -665,11 +781,12 @@ glx_render(session_t *ps, const glx_texture_t *ptex, } // Cleanup - glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(ptex->target, 0); glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); glDisable(GL_COLOR_LOGIC_OP); + glDisable(ptex->target); return true; }