diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index e1653a00..96ec047d 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 7d1d1b01..52a64b10 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 00000000..df9b32cd --- /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 f1926b94..0657686d 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 1c98de5e..d5abd80b 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 dde8ed35..70942af1 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 6dc4e844..c7b35ecd 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 adc9d413..12b03b1d 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 34ff6403..db7a538d 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 d1273f4a..cba6a29d 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 1684449c..ce5a8f75 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 a2714d75..42102043 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,