Feature #69: GLX: Blur background

- GLX backend: Add blur background support using a GLSL shader. Only
  tested with nvidia-drivers-313.26. Known to cause quite some decrease
  in performance (~10%?).

- Detach shaders in glx_create_program(). Misc changes.
This commit is contained in:
Richard Grenville 2013-03-20 17:29:45 +08:00
parent 725bc40a65
commit 8208ec6dc8
4 changed files with 174 additions and 69 deletions

View File

@ -49,9 +49,9 @@ ifeq "$(NO_VSYNC_OPENGL)" ""
# -lGL must precede some other libraries, or it segfaults on FreeBSD (#74) # -lGL must precede some other libraries, or it segfaults on FreeBSD (#74)
LIBS := -lGL $(LIBS) LIBS := -lGL $(LIBS)
OBJS += opengl.o OBJS += opengl.o
# ifeq "$(NO_VSYNC_OPENGL_GLSL)" "" ifeq "$(NO_VSYNC_OPENGL_GLSL)" ""
# CFG += -DCONFIG_VSYNC_OPENGL_GLSL CFG += -DCONFIG_VSYNC_OPENGL_GLSL
# endif endif
endif endif
# ==== D-Bus ==== # ==== D-Bus ====

View File

@ -626,6 +626,18 @@ typedef struct {
f_ReleaseTexImageEXT glXReleaseTexImageProc; f_ReleaseTexImageEXT glXReleaseTexImageProc;
/// FBConfig-s for GLX pixmap of different depths. /// FBConfig-s for GLX pixmap of different depths.
glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1];
#ifdef CONFIG_VSYNC_OPENGL_GLSL
/// Fragment shader for blur.
GLuint glx_frag_shader_blur;
/// GLSL program for blur.
GLuint glx_prog_blur;
/// Location of uniform "offset_x" in blur GLSL program.
GLint glx_prog_blur_unifm_offset_x;
/// Location of uniform "offset_y" in blur GLSL program.
GLint glx_prog_blur_unifm_offset_y;
/// Location of uniform "factor_center" in blur GLSL program.
GLint glx_prog_blur_unifm_factor_center;
#endif
#endif #endif
// === X extension related === // === X extension related ===
@ -1600,10 +1612,22 @@ glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) {
void void
glx_set_clip(session_t *ps, XserverRegion reg); glx_set_clip(session_t *ps, XserverRegion reg);
bool
glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor_center);
bool bool
glx_render(session_t *ps, const glx_texture_t *ptex, glx_render(session_t *ps, const glx_texture_t *ptex,
int x, int y, int dx, int dy, int width, int height, int z, int x, int y, int dx, int dy, int width, int height, int z,
double opacity, bool neg, XserverRegion reg_tgt); double opacity, bool neg, XserverRegion reg_tgt);
#ifdef CONFIG_VSYNC_OPENGL_GLSL
GLuint
glx_create_shader(GLenum shader_type, const char *shader_str);
GLuint
glx_create_program(const GLuint * const shaders, int nshaders);
#endif
#endif #endif
static inline void static inline void

View File

@ -1329,6 +1329,22 @@ win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) {
static inline void static inline void
win_blur_background(session_t *ps, win *w, Picture tgt_buffer, win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
XserverRegion reg_paint) { XserverRegion reg_paint) {
const int x = w->a.x;
const int y = w->a.y;
const int wid = w->widthb;
const int hei = w->heightb;
double factor_center = 1.0;
// Adjust blur strength according to window opacity, to make it appear
// better during fading
if (!ps->o.blur_background_fixed) {
double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0);
factor_center = pct * 8.0 / (1.1 - pct);
}
switch (ps->o.backend) {
case BKEND_XRENDER:
{
const static int convolution_blur_size = 3; const static int convolution_blur_size = 3;
// Convolution filter parameter (box blur) // Convolution filter parameter (box blur)
// gaussian or binomial filters are definitely superior, yet looks // gaussian or binomial filters are definitely superior, yet looks
@ -1344,11 +1360,6 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
}; };
const int x = w->a.x;
const int y = w->a.y;
const int wid = w->widthb;
const int hei = w->heightb;
// Directly copying from tgt_buffer does not work, so we create a // Directly copying from tgt_buffer does not work, so we create a
// Picture in the middle. // Picture in the middle.
Picture tmp_picture = win_build_picture(ps, w, NULL); Picture tmp_picture = win_build_picture(ps, w, NULL);
@ -1356,12 +1367,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
if (!tmp_picture) if (!tmp_picture)
return; return;
// Adjust blur strength according to window opacity, to make it appear convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(factor_center);
// better during fading
if (!ps->o.blur_background_fixed) {
double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0);
convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(pct * 8.0 / (1.1 - pct));
}
// Minimize the region we try to blur, if the window itself is not // Minimize the region we try to blur, if the window itself is not
// opaque, only the frame is. // opaque, only the frame is.
@ -1384,6 +1390,16 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
free_picture(ps, &tmp_picture); free_picture(ps, &tmp_picture);
} }
break;
#ifdef CONFIG_VSYNC_OPENGL
case BKEND_GLX:
glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center);
break;
#endif
default:
assert(0);
}
}
static void static void
render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,

View File

@ -140,6 +140,14 @@ glx_init_end:
*/ */
void void
glx_destroy(session_t *ps) { glx_destroy(session_t *ps) {
#ifdef CONFIG_VSYNC_OPENGL_GLSL
// Free GLSL shaders/programs
if (ps->glx_frag_shader_blur)
glDeleteShader(ps->glx_frag_shader_blur);
if (ps->glx_prog_blur)
glDeleteProgram(ps->glx_prog_blur);
#endif
// Free FBConfigs // Free FBConfigs
for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) {
free(ps->glx_fbconfigs[i]); free(ps->glx_fbconfigs[i]);
@ -173,20 +181,66 @@ glx_on_root_change(session_t *ps) {
*/ */
bool bool
glx_init_blur(session_t *ps) { glx_init_blur(session_t *ps) {
printf_errf("(): Blur on GLX backend isn't implemented yet, sorry."); #ifdef CONFIG_VSYNC_OPENGL_GLSL
if (ps->o.glx_no_stencil) {
printf_errf("(): I'm afraid blur background won't work so well without "
"stencil buffer support.");
return false; return false;
}
// #ifdef CONFIG_VSYNC_OPENGL_GLSL // Build shader
// static const char *FRAG_SHADER_BLUR = ""; static const char *FRAG_SHADER_BLUR =
// GLuint frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR); "#version 110\n"
// if (!frag_shader) { "uniform float offset_x;\n"
// printf_errf("(): Failed to create fragment shader for blurring."); "uniform float offset_y;\n"
// return false; "uniform float factor_center;\n"
// } "uniform sampler2D tex_scr;\n"
// #else "\n"
// printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); "void main() {\n"
// return false; " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n"
// #endif " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y - offset_y));\n"
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y));\n"
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y + offset_y));\n"
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - offset_y));\n"
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n"
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + offset_y));\n"
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y - offset_y));\n"
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y));\n"
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y + offset_y));\n"
" gl_FragColor = sum / (factor_center + 8.0);\n"
"}\n"
;
ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR);
if (!ps->glx_frag_shader_blur) {
printf_errf("(): Failed to create fragment shader.");
return false;
}
ps->glx_prog_blur = glx_create_program(&ps->glx_frag_shader_blur, 1);
if (!ps->glx_prog_blur) {
printf_errf("(): Failed to create GLSL program.");
return false;
}
#define P_GET_UNIFM_LOC(name, target) { \
ps->target = glGetUniformLocation(ps->glx_prog_blur, name); \
if (ps->target < 0) { \
printf_errf("(): Failed to get location of uniform '" name "'."); \
return false; \
} \
}
P_GET_UNIFM_LOC("factor_center", glx_prog_blur_unifm_factor_center);
P_GET_UNIFM_LOC("offset_x", glx_prog_blur_unifm_offset_x);
P_GET_UNIFM_LOC("offset_y", glx_prog_blur_unifm_offset_y);
#undef P_GET_UNIFM_LOC
return true;
#else
printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend.");
return false;
#endif
} }
/** /**
@ -590,7 +644,8 @@ glx_set_clip(session_t *ps, XserverRegion reg) {
} }
bool bool
glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor_center) {
// Read destination pixels into a texture // Read destination pixels into a texture
GLuint tex_scr = 0; GLuint tex_scr = 0;
glGenTextures(1, &tex_scr); glGenTextures(1, &tex_scr);
@ -617,10 +672,18 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) {
#endif #endif
// Paint it back // Paint it back
// TODO: Blur function. We are using color negation for testing now. // Color negation for testing...
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); // glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); // glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#ifdef CONFIG_VSYNC_OPENGL_GLSL
glUseProgram(ps->glx_prog_blur);
glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / width);
glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / height);
glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center);
#endif
glBegin(GL_QUADS); glBegin(GL_QUADS);
@ -653,6 +716,9 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) {
glEnd(); glEnd();
#ifdef CONFIG_VSYNC_OPENGL_GLSL
glUseProgram(0);
#endif
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBindTexture(tex_tgt, 0); glBindTexture(tex_tgt, 0);
glDeleteTextures(1, &tex_scr); glDeleteTextures(1, &tex_scr);
@ -668,8 +734,6 @@ bool
glx_render(session_t *ps, const glx_texture_t *ptex, glx_render(session_t *ps, const glx_texture_t *ptex,
int x, int y, int dx, int dy, int width, int height, int z, int x, int y, int dx, int dy, int width, int height, int z,
double opacity, bool neg, XserverRegion reg_tgt) { double opacity, bool neg, XserverRegion reg_tgt) {
bool blur_background = false;
if (!ptex || !ptex->texture) { if (!ptex || !ptex->texture) {
printf_errf("(): Missing texture."); printf_errf("(): Missing texture.");
return false; return false;
@ -678,8 +742,6 @@ glx_render(session_t *ps, const glx_texture_t *ptex,
// Enable blending if needed // Enable blending if needed
if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT == if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT ==
ps->glx_fbconfigs[ptex->depth]->texture_fmt) { 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); glEnable(GL_BLEND);
@ -832,8 +894,7 @@ glx_create_shader_end:
} }
GLuint GLuint
glx_create_program(GLenum shader_type, const GLuint * const shaders, glx_create_program(const GLuint * const shaders, int nshaders) {
int nshaders) {
bool success = false; bool success = false;
GLuint program = glCreateProgram(); GLuint program = glCreateProgram();
if (!program) { if (!program) {
@ -863,6 +924,10 @@ glx_create_program(GLenum shader_type, const GLuint * const shaders,
success = true; success = true;
glx_create_program_end: glx_create_program_end:
if (program) {
for (int i = 0; i < nshaders; ++i)
glDetachShader(program, shaders[i]);
}
if (program && !success) { if (program && !success) {
glDeleteProgram(program); glDeleteProgram(program);
program = 0; program = 0;