From 8922312e42b7ed76c77479efd6f08c872042663d Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 3 Feb 2019 16:41:04 +0000 Subject: [PATCH] Refactor FBConfig lookup Background: To bind a Xorg window content to a OpenGL FBConfig, which has to match the color format the Xorg window is using. Previously, compton just find a FBConfig that has the same depth. This led to chjj/compton#477, which has been fixed by a ugly hack. The commit refactor the lookup mechanism to take as much into consideration as we reasonably can. Hopefully preventing similar breakages in the future. Also, some code sharing between the old and new glx backend. Signed-off-by: Yuxuan Shui --- src/backend/gl/gl_common.h | 3 +- src/backend/gl/glx.c | 395 +++++++++++++++---------------------- src/backend/gl/glx.h | 15 ++ src/backend/meson.build | 8 +- src/common.h | 3 - src/compton.modulemap | 3 + src/opengl.c | 230 ++------------------- src/opengl.h | 12 +- src/render.c | 104 ++++++---- src/render.h | 7 + src/utils.h | 4 + src/x.h | 1 + 12 files changed, 271 insertions(+), 514 deletions(-) create mode 100644 src/backend/gl/glx.h diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index e1653a0..96ec047 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -4,7 +4,6 @@ #include #include -#include "common.h" #include "region.h" #include "log.h" @@ -62,6 +61,8 @@ typedef struct { int height; } gl_blur_cache_t; +typedef struct session session_t; + #define GL_PROG_MAIN_INIT \ { .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, .unifm_tex = -1, } diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index 7d1d1b0..52a64b1 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "backend/backend.h" #include "backend/gl/gl_common.h" +#include "backend/gl/glx.h" #include "common.h" #include "compiler.h" #include "config.h" @@ -27,14 +29,7 @@ #include "region.h" #include "utils.h" #include "win.h" - -/// @brief Wrapper of a GLX FBConfig. -typedef struct glx_fbconfig { - GLXFBConfig cfg; - GLint texture_fmt; - GLint texture_tgts; - bool y_inverted; -} glx_fbconfig_t; +#include "x.h" struct _glx_win_data { gl_texture_t texture; @@ -49,13 +44,117 @@ struct _glx_data { gl_cap_t cap; gl_win_shader_t win_shader; gl_blur_shader_t blur_shader[MAX_BLUR_PASS]; - glx_fbconfig_t *fbconfigs[OPENGL_MAX_DEPTH + 1]; void (*glXBindTexImage)(Display *display, GLXDrawable drawable, int buffer, const int *attrib_list); void (*glXReleaseTexImage)(Display *display, GLXDrawable drawable, int buffer); }; +struct glx_fbconfig_info * +glx_find_fbconfig(Display *dpy, int screen, xcb_render_pictforminfo_t *pictfmt, int depth) { + // Alright, this might not be right, but it might just be enough to just match + // visual against GLX_VISUAL_ID, and use the one that matches + assert(pictfmt); + + if (pictfmt->type != XCB_RENDER_PICT_TYPE_DIRECT) { + log_error("compton cannot handle non-DirectColor visuals. Report an " + "issue if you see this error message."); + return NULL; + } + int red_size = popcountl(pictfmt->direct.red_mask), + blue_size = popcountl(pictfmt->direct.blue_mask), + green_size = popcountl(pictfmt->direct.green_mask), + alpha_size = popcountl(pictfmt->direct.alpha_mask); + log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", red_size, blue_size, + green_size, alpha_size, depth); + + int ncfg; + // clang-format off + GLXFBConfig *cfg = + glXChooseFBConfig(dpy, screen, (int[]){ + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_X_RENDERABLE, true, + GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, GLX_DONT_CARE, + GLX_BUFFER_SIZE, red_size + green_size + blue_size + alpha_size, + GLX_RED_SIZE, red_size, + GLX_BLUE_SIZE, blue_size, + GLX_GREEN_SIZE, green_size, + GLX_ALPHA_SIZE, alpha_size, + GLX_STENCIL_SIZE, 0, + GLX_DEPTH_SIZE, 0, + 0 + }, &ncfg); + // clang-format on + +#define glXGetFBConfigAttribChecked(a, b, attr, c) \ + do { \ + if (glXGetFBConfigAttrib(a, b, attr, c)) { \ + log_info("Cannot get FBConfig attribute " #attr); \ + continue; \ + } \ + } while (0) + int texture_tgts, y_inverted, texture_fmt; + bool found = false; + GLXFBConfig ret; + for (int i = 0; i < ncfg; i++) { + int red, green, blue; + glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_RED_SIZE, &red); + glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BLUE_SIZE, &blue); + glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_GREEN_SIZE, &green); + if (red != red_size || green != green_size || blue != blue_size) { + // Color size doesn't match, this cannot work + continue; + } + + int rgb, rgba; + glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &rgb); + glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &rgba); + if (!rgb && !rgba) { + log_info("FBConfig is neither RGBA nor RGB, compton cannot " + "handle this setup."); + continue; + } + + int visual; + glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_VISUAL_ID, &visual); + if (depth != -1 && x_get_visual_depth(XGetXCBConnection(dpy), visual) != depth) { + // Some driver might attach fbconfig to a GLX visual with a + // different depth. (That totally makes sense. - NVIDIA + // developers) + continue; + } + + // All check passed, we are using this one. + found = true; + ret = cfg[i]; + glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_tgts); + glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_Y_INVERTED_EXT, &y_inverted); + + // Prefer the texture format with matching alpha, with the other one as + // fallback + if (alpha_size) { + texture_fmt = rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT; + } else { + texture_fmt = rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT; + } + break; + } +#undef glXGetFBConfigAttribChecked + free(cfg); + if (!found) { + return NULL; + } + + auto info = cmalloc(struct glx_fbconfig_info); + info->cfg = ret; + info->texture_tgts = texture_tgts; + info->texture_fmt = texture_fmt; + info->y_inverted = y_inverted; + return info; +} + /** * Check if a GLX extension exists. */ @@ -120,199 +219,10 @@ static inline void free_win_res_glx(session_t *ps, win *w) { /*free_glx_bc(ps, &w->glx_blur_cache);*/ } -static inline int glx_cmp_fbconfig_cmpattr(session_t *ps, const glx_fbconfig_t *pfbc_a, - const glx_fbconfig_t *pfbc_b, int attr) { - int attr_a = 0, attr_b = 0; - - // TODO: Error checking - glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a); - glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b); - - return attr_a - attr_b; -} - -/** - * Compare two GLX FBConfig's to find the preferred one. - */ -static int -glx_cmp_fbconfig(session_t *ps, const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b) { - int result = 0; - - if (!pfbc_a) - return -1; - if (!pfbc_b) - return 1; - int tmpattr; - - // Avoid 10-bit colors - glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, GLX_RED_SIZE, &tmpattr); - if (tmpattr != 8) - return -1; - - glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, GLX_RED_SIZE, &tmpattr); - if (tmpattr != 8) - return 1; - -#define P_CMPATTR_LT(attr) \ - { \ - if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) \ - return -result; \ - } -#define P_CMPATTR_GT(attr) \ - { \ - if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) \ - return result; \ - } - - P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT); - P_CMPATTR_LT(GLX_DOUBLEBUFFER); - P_CMPATTR_LT(GLX_STENCIL_SIZE); - P_CMPATTR_LT(GLX_DEPTH_SIZE); - P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT); - - return 0; -} - -/** - * @brief Update the FBConfig of given depth. - */ -static inline void glx_update_fbconfig_bydepth(session_t *ps, struct _glx_data *gd, - int depth, glx_fbconfig_t *pfbcfg) { - // Make sure the depth is sane - if (depth < 0 || depth > OPENGL_MAX_DEPTH) - return; - - // Compare new FBConfig with current one - if (glx_cmp_fbconfig(ps, gd->fbconfigs[depth], pfbcfg) < 0) { - log_debug("(depth %d): %p overrides %p, target %#x.\n", depth, pfbcfg->cfg, - gd->fbconfigs[depth] ? gd->fbconfigs[depth]->cfg : 0, pfbcfg->texture_tgts); - if (!gd->fbconfigs[depth]) { - gd->fbconfigs[depth] = cmalloc(glx_fbconfig_t); - } - (*gd->fbconfigs[depth]) = *pfbcfg; - } -} - -/** - * Get GLX FBConfigs for all depths. - */ -static bool glx_update_fbconfig(struct _glx_data *gd, session_t *ps) { - // Acquire all FBConfigs and loop through them - int nele = 0; - GLXFBConfig *pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele); - - for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) { - glx_fbconfig_t fbinfo = { - .cfg = *pcur, - .texture_fmt = 0, - .texture_tgts = 0, - .y_inverted = false, - }; - int id = (int)(pcur - pfbcfgs); - int depth = 0, depth_alpha = 0, val = 0; - - // Skip over multi-sampled visuals - // http://people.freedesktop.org/~glisse/0001-glx-do-not-use-multisample-visual-config-for-front-o.patch -#ifdef GLX_SAMPLES - if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_SAMPLES, &val) && val > 1) - continue; -#endif - - if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth) || - Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) { - log_error("Failed to retrieve buffer size and alpha size of " - "FBConfig %d.", - id); - continue; - } - if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, - &fbinfo.texture_tgts)) { - log_error("Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of " - "FBConfig %d.", - id); - continue; - } - - int visualdepth = 0; - { - XVisualInfo *pvi = glXGetVisualFromFBConfig(ps->dpy, *pcur); - if (!pvi) { - // On nvidia-drivers-325.08 this happens slightly too often... - // log_error("Failed to retrieve X Visual of FBConfig %d.", id); - continue; - } - visualdepth = pvi->depth; - cxfree(pvi); - } - - bool rgb = false; - bool rgba = false; - - if (depth >= 32 && depth_alpha && - Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) && val) - rgba = true; - - if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGB_EXT, &val) && val) - rgb = true; - - if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val)) - fbinfo.y_inverted = val; - - { - int tgtdpt = depth - depth_alpha; - if (tgtdpt == visualdepth && tgtdpt < 32 && rgb) { - fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT; - glx_update_fbconfig_bydepth(ps, gd, tgtdpt, &fbinfo); - } - } - - if (depth == visualdepth && rgba) { - fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT; - glx_update_fbconfig_bydepth(ps, gd, depth, &fbinfo); - } - } - - cxfree(pfbcfgs); - - // Sanity checks - if (!gd->fbconfigs[ps->depth]) { - log_error("No FBConfig found for default depth %d.", ps->depth); - return false; - } - - if (!gd->fbconfigs[32]) { - log_error("No FBConfig found for depth 32. compton may not work " - "correctly"); - } - - return true; -} - -#ifdef DEBUG_GLX_DEBUG_CONTEXT -static inline GLXFBConfig get_fbconfig_from_visualinfo(session_t *ps, const XVisualInfo *visualinfo) { - int nelements = 0; - GLXFBConfig *fbconfigs = glXGetFBConfigs(ps->dpy, visualinfo->screen, &nelements); - for (int i = 0; i < nelements; ++i) { - int visual_id = 0; - if (Success == glXGetFBConfigAttrib(ps->dpy, fbconfigs[i], GLX_VISUAL_ID, &visual_id) && - visual_id == visualinfo->visualid) - return fbconfigs[i]; - } - - return NULL; -} - -static void glx_debug_msg_callback(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const GLchar *message, GLvoid *userParam) { - log_trace("(): source 0x%04X, type 0x%04X, id %u, severity 0x%0X, \"%s\"", source, - type, id, severity, message); -} -#endif - /** * Destroy GLX related resources. */ -void glx_deinit(void *backend_data, session_t *ps) { +static void glx_deinit(void *backend_data, session_t *ps) { struct _glx_data *gd = backend_data; // Free all GLX resources of windows @@ -328,12 +238,6 @@ void glx_deinit(void *backend_data, session_t *ps) { gl_check_err(); - // Free FBConfigs - for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { - free(gd->fbconfigs[i]); - gd->fbconfigs[i] = NULL; - } - // Destroy GLX context if (gd->ctx) { glXDestroyContext(ps->dpy, gd->ctx); @@ -448,10 +352,6 @@ static void *glx_init(session_t *ps) { goto end; } - // Acquire FBConfigs - if (!glx_update_fbconfig(gd, ps)) - goto end; - // Render preparations gl_resize(ps->root_width, ps->root_height); @@ -490,7 +390,7 @@ end: return gd; } -void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { +static void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { struct _glx_data *gd = backend_data; // Retrieve pixmap parameters, if they aren't provided if (w->g.depth > OPENGL_MAX_DEPTH) { @@ -499,36 +399,7 @@ void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { return false; } - const glx_fbconfig_t *pcfg = gd->fbconfigs[w->g.depth]; - if (!pcfg) { - log_error("Couldn't find FBConfig with requested depth %d", w->g.depth); - return false; - } - - // Choose a suitable texture target for our pixmap. - // Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean - // of the bits in texture_tgts - GLenum tex_tgt = 0; - if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts && gd->cap.non_power_of_two_texture) - tex_tgt = GLX_TEXTURE_2D_EXT; - else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & pcfg->texture_tgts) - tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; - else if (!(GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts)) - tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; - else - tex_tgt = GLX_TEXTURE_2D_EXT; - - log_debug("depth %d, tgt %#x, rgba %d\n", w->g.depth, tex_tgt, - (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt)); - - GLint attrs[] = { - GLX_TEXTURE_FORMAT_EXT, pcfg->texture_fmt, GLX_TEXTURE_TARGET_EXT, tex_tgt, 0, - }; - auto wd = ccalloc(1, struct _glx_win_data); - wd->texture.target = (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE); - wd->texture.y_inverted = pcfg->y_inverted; - if (ps->has_name_pixmap) { wd->pixmap = xcb_generate_id(ps->c); xcb_composite_name_window_pixmap(ps->c, w->id, wd->pixmap); @@ -540,7 +411,48 @@ void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { goto err; } - wd->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, wd->pixmap, attrs); + auto pictfmt = x_get_pictform_for_visual(ps->c, w->a.visual); + if (!pictfmt) { + log_error("Window %#010x has invalid visual %#x", w->id, w->a.visual); + goto err; + } + int depth = x_get_visual_depth(ps->c, w->a.visual); + auto fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, pictfmt, depth); + if (!fbcfg) { + log_error("Couldn't find FBConfig with requested visual %x", w->a.visual); + goto err; + } + + // Choose a suitable texture target for our pixmap. + // Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean + // of the bits in texture_tgts + GLenum tex_tgt = 0; + if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts && gd->cap.non_power_of_two_texture) + tex_tgt = GLX_TEXTURE_2D_EXT; + else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts) + tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; + else if (!(GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts)) + tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; + else + tex_tgt = GLX_TEXTURE_2D_EXT; + + log_debug("depth %d, tgt %#x, rgba %d\n", w->g.depth, tex_tgt, + (GLX_TEXTURE_FORMAT_RGBA_EXT == fbcfg->texture_fmt)); + + GLint attrs[] = { + GLX_TEXTURE_FORMAT_EXT, + fbcfg->texture_fmt, + GLX_TEXTURE_TARGET_EXT, + tex_tgt, + 0, + }; + + wd->texture.target = (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE); + wd->texture.y_inverted = fbcfg->y_inverted; + + wd->glpixmap = glXCreatePixmap(ps->dpy, fbcfg->cfg, wd->pixmap, attrs); + free(fbcfg); + if (!wd->glpixmap) { log_error("Failed to create glpixmap for window %#010x", w->id); goto err; @@ -581,7 +493,8 @@ err: /** * Bind a X pixmap to an OpenGL texture. */ -void glx_render_win(void *backend_data, session_t *ps, win *w, void *win_data, const region_t *reg_paint) { +static void glx_render_win(void *backend_data, session_t *ps, win *w, void *win_data, + const region_t *reg_paint) { struct _glx_data *gd = backend_data; struct _glx_win_data *wd = win_data; diff --git a/src/backend/gl/glx.h b/src/backend/gl/glx.h new file mode 100644 index 0000000..df9b32c --- /dev/null +++ b/src/backend/gl/glx.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include +#include +#include + +struct glx_fbconfig_info { + GLXFBConfig cfg; + int texture_tgts; + int texture_fmt; + int y_inverted; +}; + +struct glx_fbconfig_info * +glx_find_fbconfig(Display *, int screen, xcb_render_pictforminfo_t *, int depth); diff --git a/src/backend/meson.build b/src/backend/meson.build index f1926b9..0657686 100644 --- a/src/backend/meson.build +++ b/src/backend/meson.build @@ -2,13 +2,9 @@ srcs += [ files('backend_common.c') ] if get_option('new_backends') srcs += [ files('xrender.c', 'backend.c') ] - - # enable opengl - if get_option('opengl') - srcs += [ files('gl/gl_common.c', 'gl/glx.c') ] - endif endif +# enable opengl if get_option('opengl') - srcs += [ files('gl/gl_common.c') ] + srcs += [ files('gl/gl_common.c', 'gl/glx.c') ] endif diff --git a/src/common.h b/src/common.h index 1c98de5..d5abd80 100644 --- a/src/common.h +++ b/src/common.h @@ -221,7 +221,6 @@ struct _glx_texture { GLenum target; unsigned width; unsigned height; - unsigned depth; bool y_inverted; }; @@ -310,8 +309,6 @@ typedef struct { f_ImportSyncEXT glImportSyncEXT; /// Current GLX Z value. int z; - /// FBConfig-s for GLX pixmap of different depths. - glx_fbconfig_t *fbconfigs[OPENGL_MAX_DEPTH + 1]; #ifdef CONFIG_OPENGL glx_blur_pass_t blur_passes[MAX_BLUR_PASS]; #endif diff --git a/src/compton.modulemap b/src/compton.modulemap index dde8ed3..70942af 100644 --- a/src/compton.modulemap +++ b/src/compton.modulemap @@ -68,6 +68,9 @@ module backend { module gl_common { header "backend/gl/gl_common.h" } + module glx { + header "backend/gl/glx.h" + } } module backend { header "backend/backend.h" diff --git a/src/opengl.c b/src/opengl.c index 6dc4e84..c7b35ec 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -25,173 +25,10 @@ #include "win.h" #include "region.h" #include "backend/gl/gl_common.h" +#include "backend/gl/glx.h" #include "opengl.h" -static inline int -glx_cmp_fbconfig_cmpattr(session_t *ps, - const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b, - int attr) { - int attr_a = 0, attr_b = 0; - - // TODO: Error checking - glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a); - glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b); - - return attr_a - attr_b; -} - -/** - * Compare two GLX FBConfig's to find the preferred one. - */ -static int -glx_cmp_fbconfig(session_t *ps, - const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b) { - int result = 0; - - if (!pfbc_a) - return -1; - if (!pfbc_b) - return 1; - int tmpattr; - - // Avoid 10-bit colors - glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, GLX_RED_SIZE, &tmpattr); - if (tmpattr != 8) - return -1; - - glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, GLX_RED_SIZE, &tmpattr); - if (tmpattr != 8) - return 1; - -#define P_CMPATTR_LT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return -result; } -#define P_CMPATTR_GT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return result; } - - P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT); - P_CMPATTR_LT(GLX_DOUBLEBUFFER); - P_CMPATTR_LT(GLX_STENCIL_SIZE); - P_CMPATTR_LT(GLX_DEPTH_SIZE); - P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT); - - return 0; -} - -/** - * @brief Update the FBConfig of given depth. - */ -static inline void -glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) { - // Make sure the depth is sane - if (depth < 0 || depth > OPENGL_MAX_DEPTH) - return; - - // Compare new FBConfig with current one - if (glx_cmp_fbconfig(ps, ps->psglx->fbconfigs[depth], pfbcfg) < 0) { - log_trace("(depth %d): %p overrides %p, target %#x.", depth, - pfbcfg->cfg, - ps->psglx->fbconfigs[depth] ? ps->psglx->fbconfigs[depth]->cfg: - 0, - pfbcfg->texture_tgts); - if (!ps->psglx->fbconfigs[depth]) { - ps->psglx->fbconfigs[depth] = cmalloc(glx_fbconfig_t); - } - (*ps->psglx->fbconfigs[depth]) = *pfbcfg; - } -} - -/** - * Get GLX FBConfigs for all depths. - */ -static bool -glx_update_fbconfig(session_t *ps) { - // Acquire all FBConfigs and loop through them - int nele = 0; - GLXFBConfig* pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele); - - for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) { - glx_fbconfig_t fbinfo = { - .cfg = *pcur, - .texture_fmt = 0, - .texture_tgts = 0, - .y_inverted = false, - }; - int id = (int) (pcur - pfbcfgs); - int depth = 0, depth_alpha = 0, val = 0; - - // Skip over multi-sampled visuals - // http://people.freedesktop.org/~glisse/0001-glx-do-not-use-multisample-visual-config-for-front-o.patch -#ifdef GLX_SAMPLES - if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_SAMPLES, &val) - && val > 1) - continue; -#endif - - if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth) - || Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) { - log_error("Failed to retrieve buffer size and alpha size of FBConfig %d.", id); - continue; - } - if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &fbinfo.texture_tgts)) { - log_error("Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", id); - continue; - } - - int visualdepth = 0; - { - XVisualInfo *pvi = glXGetVisualFromFBConfig(ps->dpy, *pcur); - if (!pvi) { - // On nvidia-drivers-325.08 this happens slightly too often... - // log_error("Failed to retrieve X Visual of FBConfig %d.", id); - continue; - } - visualdepth = pvi->depth; - cxfree(pvi); - } - - bool rgb = false; - bool rgba = false; - - if (depth >= 32 && depth_alpha && Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) && val) - rgba = true; - - if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGB_EXT, &val) && val) - rgb = true; - - if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val)) - fbinfo.y_inverted = val; - - { - int tgtdpt = depth - depth_alpha; - if (tgtdpt == visualdepth && tgtdpt < 32 && rgb) { - fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT; - glx_update_fbconfig_bydepth(ps, tgtdpt, &fbinfo); - } - } - - if (depth == visualdepth && rgba) { - fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT; - glx_update_fbconfig_bydepth(ps, depth, &fbinfo); - } - } - - cxfree(pfbcfgs); - - // Sanity checks - if (!ps->psglx->fbconfigs[ps->depth]) { - log_error("No FBConfig found for default depth %d.", ps->depth); - return false; - } - - if (!ps->psglx->fbconfigs[32]) { - log_error("No FBConfig found for depth 32. compton will try to continue."); - } else { - log_trace("%d-bit: %p, 32-bit: %p", ps->depth, - ps->psglx->fbconfigs[ps->depth]->cfg, ps->psglx->fbconfigs[32]->cfg); - } - - return true; -} - static inline XVisualInfo * get_visualinfo_from_visual(session_t *ps, xcb_visualid_t visual) { XVisualInfo vreq = { .visualid = visual }; @@ -200,31 +37,6 @@ get_visualinfo_from_visual(session_t *ps, xcb_visualid_t visual) { return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems); } -#ifdef DEBUG_GLX_DEBUG_CONTEXT -static inline GLXFBConfig -get_fbconfig_from_visualinfo(session_t *ps, const XVisualInfo *visualinfo) { - int nelements = 0; - GLXFBConfig *fbconfigs = glXGetFBConfigs(ps->dpy, visualinfo->screen, - &nelements); - for (int i = 0; i < nelements; ++i) { - int visual_id = 0; - if (Success == glXGetFBConfigAttrib(ps->dpy, fbconfigs[i], GLX_VISUAL_ID, &visual_id) - && visual_id == visualinfo->visualid) - return fbconfigs[i]; - } - - return NULL; -} - -static void -glx_debug_msg_callback(GLenum source, GLenum type, - GLuint id, GLenum severity, GLsizei length, const GLchar *message, - GLvoid *userParam) { - log_trace("source 0x%04X, type 0x%04X, id %u, severity 0x%0X, \"%s\"", - source, type, id, severity, message); -} -#endif - /** * Initialize OpenGL. */ @@ -375,10 +187,6 @@ glx_init(session_t *ps, bool need_render) { } } - // Acquire FBConfigs - if (need_render && !glx_update_fbconfig(ps)) - goto glx_init_end; - // Render preparations if (need_render) { glx_on_root_change(ps); @@ -451,12 +259,6 @@ glx_destroy(session_t *ps) { gl_check_err(); - // Free FBConfigs - for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { - free(ps->psglx->fbconfigs[i]); - ps->psglx->fbconfigs[i] = NULL; - } - // Destroy GLX context if (ps->psglx->context) { glXDestroyContext(ps->dpy, ps->psglx->context); @@ -694,7 +496,7 @@ glx_load_prog_main(session_t *ps, */ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, - unsigned width, unsigned height, unsigned depth) { + unsigned width, unsigned height, const struct glx_fbconfig_info *fbcfg) { if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) return true; @@ -703,6 +505,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, return false; } + assert(fbcfg); glx_texture_t *ptex = *pptex; bool need_release = true; @@ -715,7 +518,6 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, .target = 0, .width = 0, .height = 0, - .depth = 0, .y_inverted = false, }; @@ -731,11 +533,12 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, } // Create GLX pixmap + unsigned depth; if (!ptex->glpixmap) { need_release = false; // Retrieve pixmap parameters, if they aren't provided - if (!(width && height && depth)) { + if (!(width && height)) { Window rroot = None; int rx = 0, ry = 0; unsigned rbdwid = 0; @@ -751,45 +554,38 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, } } - const glx_fbconfig_t *pcfg = ps->psglx->fbconfigs[depth]; - if (!pcfg) { - log_error("Couldn't find FBConfig with requested depth %d.", depth); - return false; - } - // Determine texture target, copied from compiz // 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 + if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts && ps->psglx->has_texture_non_power_of_two) tex_tgt = GLX_TEXTURE_2D_EXT; - else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & pcfg->texture_tgts) + else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts) tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; - else if (!(GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts)) + else if (!(GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts)) tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; else tex_tgt = GLX_TEXTURE_2D_EXT; log_debug("depth %d, tgt %#x, rgba %d", depth, tex_tgt, - (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt)); + (GLX_TEXTURE_FORMAT_RGBA_EXT == fbcfg->texture_fmt)); GLint attrs[] = { GLX_TEXTURE_FORMAT_EXT, - pcfg->texture_fmt, + fbcfg->texture_fmt, GLX_TEXTURE_TARGET_EXT, tex_tgt, 0, }; - ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs); + ptex->glpixmap = glXCreatePixmap(ps->dpy, fbcfg->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; - ptex->y_inverted = pcfg->y_inverted; + ptex->y_inverted = fbcfg->y_inverted; } if (!ptex->glpixmap) { log_error("Failed to allocate GLX pixmap."); @@ -1186,8 +982,6 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return false; } - argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT == - ps->psglx->fbconfigs[ptex->depth]->texture_fmt); const bool has_prog = pprogram && pprogram->prog; bool dual_texture = false; diff --git a/src/opengl.h b/src/opengl.h index adc9d41..12b03b1 100644 --- a/src/opengl.h +++ b/src/opengl.h @@ -19,20 +19,13 @@ #include "log.h" #include +#include #include #include #include #include #include -/// @brief Wrapper of a GLX FBConfig. -typedef struct glx_fbconfig { - GLXFBConfig cfg; - GLint texture_fmt; - GLint texture_tgts; - bool y_inverted; -} glx_fbconfig_t; - /** * Check if a word is in string. */ @@ -108,7 +101,7 @@ glx_load_prog_main(session_t *ps, bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, - unsigned width, unsigned height, unsigned depth); + unsigned width, unsigned height, const struct glx_fbconfig_info *); void glx_release_pixmap(session_t *ps, glx_texture_t *ptex); @@ -249,5 +242,6 @@ free_win_res_glx(session_t *ps, win *w) { free_paint_glx(ps, &w->shadow_paint); #ifdef CONFIG_OPENGL free_glx_bc(ps, &w->glx_blur_cache); + free(w->paint.fbcfg); #endif } diff --git a/src/render.c b/src/render.c index 34ff640..db7a538 100644 --- a/src/render.c +++ b/src/render.c @@ -3,10 +3,10 @@ #include #include -#include #include -#include #include +#include +#include #include #include "common.h" @@ -17,40 +17,69 @@ #include "opengl.h" #endif -#include "vsync.h" -#include "win.h" -#include "kernel.h" #include "compiler.h" -#include "x.h" #include "config.h" -#include "region.h" +#include "kernel.h" #include "log.h" +#include "region.h" #include "types.h" #include "utils.h" +#include "vsync.h" +#include "win.h" +#include "x.h" #include "backend/backend_common.h" +#include "backend/gl/glx.h" #include "render.h" -#ifdef CONFIG_OPENGL /** * Bind texture in paint_t if we are using GLX backend. */ -static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, - unsigned hei, unsigned depth, bool force) { +static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, + unsigned depth, xcb_visualid_t visual, bool force) { +#ifdef CONFIG_OPENGL if (!ppaint->pixmap) return false; - if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) - return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); + xcb_render_pictforminfo_t *pictfmt = NULL; + xcb_render_pictforminfo_t tmp_pictfmt = {.direct = + { + .red_mask = 255, + .blue_mask = 255, + .green_mask = 255, + .alpha_mask = depth == 32 ? 255 : 0, + }, + .type = XCB_RENDER_PICT_TYPE_DIRECT}; - return true; -} -#else -static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, - unsigned hei, unsigned depth, bool force) { - return true; -} + if (!depth) { + assert(visual); + depth = x_get_visual_depth(ps->c, visual); + } + + if (!visual) { + assert(depth); + pictfmt = &tmp_pictfmt; + } else { + pictfmt = x_get_pictform_for_visual(ps->c, visual); + } + + if (!pictfmt) { + return false; + } + + if (!ppaint->fbcfg) { + ppaint->fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, pictfmt, depth); + } + if (!ppaint->fbcfg) { + log_error("Failed to find appropriate FBConfig for X pixmap"); + return false; + } + + if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) + return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, ppaint->fbcfg); #endif + return true; +} /** * Check if current backend uses XRender for rendering. @@ -219,7 +248,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { // Let glx_bind_pixmap() determine pixmap size, because if the user // is resizing windows, the width and height we get may not be up-to-date, // causing the jittering issue M4he reported in #7. - if (!paint_bind_tex(ps, &w->paint, 0, 0, 0, (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) { + if (!paint_bind_tex(ps, &w->paint, 0, 0, 0, w->a.visual, + (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) { log_error("Failed to bind texture for window %#010x.", w->id); } w->pixmap_damaged = false; @@ -445,8 +475,7 @@ static bool get_root_tile(session_t *ps) { ps->root_tile_paint.pixmap = pixmap; #ifdef CONFIG_OPENGL if (BKEND_GLX == ps->o.backend) - return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex, - ps->root_tile_paint.pixmap, 0, 0, 0); + return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, 0, ps->vis, false); #endif return true; @@ -484,7 +513,8 @@ static bool win_build_shadow(session_t *ps, win *w, double opacity) { return XCB_NONE; } - shadow_pixmap = x_create_pixmap(ps->c, 8, ps->root, shadow_image->width, shadow_image->height); + shadow_pixmap = + x_create_pixmap(ps->c, 8, ps->root, shadow_image->width, shadow_image->height); shadow_pixmap_argb = x_create_pixmap(ps->c, 32, ps->root, shadow_image->width, shadow_image->height); @@ -542,7 +572,7 @@ shadow_picture_err: */ static inline void win_paint_shadow(session_t *ps, win *w, region_t *reg_paint) { // Bind shadow pixmap to GLX texture if needed - paint_bind_tex(ps, &w->shadow_paint, 0, 0, 32, false); + paint_bind_tex(ps, &w->shadow_paint, 0, 0, 32, 0, false); if (!paint_isvalid(ps, &w->shadow_paint)) { log_error("Window %#010x is missing shadow data.", w->id); @@ -942,12 +972,14 @@ void paint_all(session_t *ps, win *const t, bool ignore_damage) { switch (ps->o.backend) { case BKEND_XRENDER: if (ps->o.monitor_repaint) { - // Copy the screen content to a new picture, and highlight - // the paint region. This is not very efficient, but since - // it's for debug only, we don't really care + // Copy the screen content to a new picture, and highlight the + // paint region. This is not very efficient, but since it's for + // debug only, we don't really care - // First we create a new picture, and copy content from the buffer to it - xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(ps->c, ps->vis); + // First we create a new picture, and copy content from the buffer + // to it + xcb_render_pictforminfo_t *pictfmt = + x_get_pictform_for_visual(ps->c, ps->vis); xcb_render_picture_t new_pict = x_create_picture_with_pictfmt( ps, ps->root_width, ps->root_height, pictfmt, 0, NULL); xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, @@ -983,7 +1015,7 @@ void paint_all(session_t *ps, win *const t, bool ignore_damage) { glXWaitX(); assert(ps->tgt_buffer.pixmap); paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, - ps->depth, !ps->o.glx_no_rebind_pixmap); + ps->depth, ps->vis, !ps->o.glx_no_rebind_pixmap); if (ps->o.vsync_use_glfinish) glFinish(); else @@ -1200,12 +1232,12 @@ void deinit_render(session_t *ps) { // Free other X resources free_root_tile(ps); - // Free the damage ring - for (int i = 0; i < ps->ndamage; ++i) - pixman_region32_fini(&ps->damage_ring[i]); - ps->ndamage = 0; - free(ps->damage_ring); - ps->damage_ring = ps->damage = NULL; + // Free the damage ring + for (int i = 0; i < ps->ndamage; ++i) + pixman_region32_fini(&ps->damage_ring[i]); + ps->ndamage = 0; + free(ps->damage_ring); + ps->damage_ring = ps->damage = NULL; #ifdef CONFIG_OPENGL glx_destroy(ps); diff --git a/src/render.h b/src/render.h index d1273f4..cba6a29 100644 --- a/src/render.h +++ b/src/render.h @@ -5,6 +5,10 @@ #include #include #include +#ifdef CONFIG_OPENGL +#include +#include "backend/gl/glx.h" +#endif #include "region.h" typedef struct _glx_texture glx_texture_t; @@ -16,6 +20,9 @@ typedef struct paint { xcb_pixmap_t pixmap; xcb_render_picture_t pict; glx_texture_t *ptex; +#ifdef CONFIG_OPENGL + struct glx_fbconfig_info *fbcfg; +#endif } paint_t; void diff --git a/src/utils.h b/src/utils.h index 1684449..ce5a8f7 100644 --- a/src/utils.h +++ b/src/utils.h @@ -73,6 +73,10 @@ static inline long attr_const min_l(long a, long b) { return (a > b ? b : a); } +static inline int attr_const popcountl(unsigned long a) { + return __builtin_popcountl(a); +} + /** * Normalize a double value to a specific range. * diff --git a/src/x.h b/src/x.h index a2714d7..4210204 100644 --- a/src/x.h +++ b/src/x.h @@ -94,6 +94,7 @@ xcb_window_t wid_get_prop_window(session_t *ps, xcb_window_t wid, xcb_atom_t apr bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst, int *pnstr); xcb_render_pictforminfo_t *x_get_pictform_for_visual(xcb_connection_t *, xcb_visualid_t); +int x_get_visual_depth(xcb_connection_t *, xcb_visualid_t); xcb_render_picture_t x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *, xcb_render_pictforminfo_t *pictfmt,