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 <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-02-03 16:41:04 +00:00
parent 7ac351e5b5
commit 8922312e42
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
12 changed files with 271 additions and 514 deletions

View File

@ -4,7 +4,6 @@
#include <GL/gl.h>
#include <GL/glext.h>
#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, }

View File

@ -10,6 +10,7 @@
*/
#include <GL/glx.h>
#include <X11/Xlib-xcb.h>
#include <assert.h>
#include <pixman.h>
#include <stdbool.h>
@ -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;

15
src/backend/gl/glx.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <xcb/render.h>
#include <xcb/xcb.h>
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);

View File

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

View File

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

View File

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

View File

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

View File

@ -19,20 +19,13 @@
#include "log.h"
#include <xcb/xcb.h>
#include <xcb/render.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <locale.h>
#include <GL/glx.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;
/**
* 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
}

View File

@ -3,10 +3,10 @@
#include <stdlib.h>
#include <string.h>
#include <xcb/xcb_image.h>
#include <xcb/composite.h>
#include <xcb/sync.h>
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xcb_image.h>
#include <xcb/xcb_renderutil.h>
#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);

View File

@ -5,6 +5,10 @@
#include <xcb/xcb.h>
#include <xcb/render.h>
#include <stdbool.h>
#ifdef CONFIG_OPENGL
#include <GL/glx.h>
#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

View File

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

View File

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