Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backend: gl: add dither #952

Merged
merged 5 commits into from
Dec 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions man/picom.1.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box
*--window-shader-fg-rule* 'SHADER':'CONDITION'::
Specify GLSL fragment shader path for rendering window contents using patterns. Similar to *--opacity-rule*, arguments should be in the format of 'SHADER:CONDITION', e.g. "shader.frag:name = \'window\'". Leading and trailing whitespaces in 'SHADER' will be trimmed. If 'SHADER' is "default", then the default shader will be used for the matching windows. (This also unfortunately means you can't use a shader file named "default"). Does not work when *--legacy-backends* is enabled.

*--dithered-present*
Use higher precision during rendering, and apply dither when presenting the rendered screen. Reduces banding artifacts, but might cause performance degradation. Only works with OpenGL.

FORMAT OF CONDITIONS
--------------------
Some options accept a condition string to match certain windows. A condition string is formed by one or more conditions, joined by logical operators.
Expand Down
5 changes: 5 additions & 0 deletions picom.sample.conf
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ blur-background-exclude = [
# backend = "glx"
backend = "xrender";

# Use higher precision during rendering, and apply dither when presenting the
# rendered screen. Reduces banding artifacts, but might cause performance
# degradation. Only works with OpenGL.
dithered-present = false;

# Enable/disable VSync.
# vsync = false
vsync = true;
Expand Down
16 changes: 10 additions & 6 deletions src/backend/gl/blur.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,10 @@ bool gl_dual_kawase_blur(double opacity, struct gl_blur_context *bctx, const rec
return true;
}

bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask,
coord_t mask_dst, const region_t *reg_blur,
const region_t *reg_visible attr_unused, GLuint source_texture,
geometry_t source_size, GLuint target_fbo, GLuint default_mask) {
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coord_t mask_dst,
const region_t *reg_blur, const region_t *reg_visible attr_unused,
GLuint source_texture, geometry_t source_size, GLuint target_fbo,
GLuint default_mask, bool high_precision) {
bool ret = false;

if (source_size.width != bctx->fb_width || source_size.height != bctx->fb_height) {
Expand All @@ -284,7 +284,11 @@ bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask,
}

glBindTexture(GL_TEXTURE_2D, bctx->blur_textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_size->width,
GLint format = GL_RGBA8;
if (high_precision) {
format = GL_RGBA16;
}
glTexImage2D(GL_TEXTURE_2D, 0, format, tex_size->width,
tex_size->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);

if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
Expand Down Expand Up @@ -406,7 +410,7 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mas
return gl_blur_impl(opacity, bctx, mask, mask_dst, reg_blur, reg_visible,
gd->back_texture,
(geometry_t){.width = gd->width, .height = gd->height},
gd->back_fbo, gd->default_mask_texture);
gd->back_fbo, gd->default_mask_texture, gd->dithered_present);
}

static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
Expand Down
36 changes: 27 additions & 9 deletions src/backend/gl/gl_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ void gl_resize(struct gl_data *gd, int width, int height) {
assert(viewport_dimensions[1] >= gd->height);

glBindTexture(GL_TEXTURE_2D, gd->back_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_BGR,
glTexImage2D(GL_TEXTURE_2D, 0, gd->back_format, width, height, 0, GL_BGR,
GL_UNSIGNED_BYTE, NULL);

gl_check_err();
Expand Down Expand Up @@ -873,7 +873,16 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
glUseProgram(0);

gd->present_prog = gl_create_program_from_str(present_vertex_shader, dummy_frag);
gd->dithered_present = ps->o.dithered_present;
if (gd->dithered_present) {
gd->present_prog = gl_create_program_from_strv(
(const char *[]){present_vertex_shader, NULL},
(const char *[]){present_frag, dither_glsl, NULL});
} else {
gd->present_prog = gl_create_program_from_strv(
(const char *[]){present_vertex_shader, NULL},
(const char *[]){dummy_frag, NULL});
}
if (!gd->present_prog) {
log_error("Failed to create the present shader");
return false;
Expand Down Expand Up @@ -907,14 +916,23 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
glUseProgram(0);

// Set up the size of the back texture
gl_resize(gd, ps->root_width, ps->root_height);

// Set up the size and format of the back texture
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->back_fbo);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
gd->back_texture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
const GLint *format = gd->dithered_present ? (const GLint[]){GL_RGB16, GL_RGBA16}
: (const GLint[]){GL_RGB8, GL_RGBA8};
for (int i = 0; i < 2; i++) {
gd->back_format = format[i];
gl_resize(gd, ps->root_width, ps->root_height);

glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, gd->back_texture, 0);
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
log_info("Using back buffer format %#x", gd->back_format);
break;
}
}
if (!gl_check_fb_complete(GL_DRAW_FRAMEBUFFER)) {
return false;
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
Expand Down Expand Up @@ -1279,7 +1297,7 @@ void *gl_shadow_from_mask(backend_t *base, void *mask,
1.0, gsctx->blur_context, NULL, (coord_t){0}, &reg_blur, NULL,
source_texture,
(geometry_t){.width = new_inner->width, .height = new_inner->height},
fbo, gd->default_mask_texture);
fbo, gd->default_mask_texture, gd->dithered_present);
pixman_region32_fini(&reg_blur);
}

Expand Down
16 changes: 10 additions & 6 deletions src/backend/gl/gl_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ struct gl_data {
gl_fill_shader_t fill_shader;
gl_shadow_shader_t shadow_shader;
GLuint back_texture, back_fbo;
GLint back_format;
GLuint present_prog;

bool dithered_present;

GLuint default_mask_texture;

/// Called when an gl_texture is decoupled from the texture it refers. Returns
Expand Down Expand Up @@ -163,10 +166,10 @@ void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visi

bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mask_dst,
const region_t *reg_blur, const region_t *reg_visible);
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask,
coord_t mask_dst, const region_t *reg_blur,
const region_t *reg_visible attr_unused, GLuint source_texture,
geometry_t source_size, GLuint target_fbo, GLuint default_mask);
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coord_t mask_dst,
const region_t *reg_blur, const region_t *reg_visible attr_unused,
GLuint source_texture, geometry_t source_size, GLuint target_fbo,
GLuint default_mask, bool high_precision);
void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
void gl_destroy_blur_context(backend_t *base, void *ctx);
struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius);
Expand Down Expand Up @@ -288,5 +291,6 @@ static const GLuint vert_in_texcoord_loc = 1;
#define QUOTE(...) #__VA_ARGS__

extern const char vertex_shader[], copy_with_mask_frag[], masking_glsl[], dummy_frag[],
fill_frag[], fill_vert[], interpolating_frag[], interpolating_vert[], win_shader_glsl[],
win_shader_default[], present_vertex_shader[], shadow_colorization_frag[];
present_frag[], fill_frag[], fill_vert[], interpolating_frag[], interpolating_vert[],
win_shader_glsl[], win_shader_default[], present_vertex_shader[], dither_glsl[],
shadow_colorization_frag[];
32 changes: 32 additions & 0 deletions src/backend/gl/shaders.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ const char dummy_frag[] = GLSL(330,
}
);

const char present_frag[] = GLSL(330,
uniform sampler2D tex;
in vec2 texcoord;
vec4 dither(vec4, vec2);
void main() {
gl_FragColor = dither(texelFetch(tex, ivec2(texcoord.xy), 0), texcoord);
}
);

const char copy_with_mask_frag[] = GLSL(330,
uniform sampler2D tex;
in vec2 texcoord;
Expand Down Expand Up @@ -174,6 +183,29 @@ const char vertex_shader[] = GLSL(330,
texcoord = in_texcoord + texorig;
}
);
const char dither_glsl[] = GLSL(330,
// Stolen from: https://www.shadertoy.com/view/7sfXDn
float bayer2(vec2 a) {
a = floor(a);
return fract(a.x / 2. + a.y * a.y * .75);
}
// 16 * 16 is 2^8, so in total we have equivalent of 16-bit
// color depth, should be enough?
float bayer(vec2 a16) {
vec2 a8 = a16 * .5;
vec2 a4 = a8 * .5;
vec2 a2 = a4 * .5;
float bayer32 = ((bayer2(a2) * .25 + bayer2( a4))
* .25 + bayer2( a8))
* .25 + bayer2(a16);
return bayer32;
}
vec4 dither(vec4 c, vec2 coord) {
vec4 residual = mod(c, 1.0 / 255.0);
vec4 dithered = vec4(greaterThan(residual, vec4(1e-4)));
return vec4(c + dithered * bayer(coord) / 255.0);
}
);
const char shadow_colorization_frag[] = GLSL(330,
uniform vec4 color;
uniform sampler2D tex;
Expand Down
4 changes: 4 additions & 0 deletions src/backend/xrender/xrender.c
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,10 @@ static void get_blur_size(void *blur_context, int *width, int *height) {
}

static backend_t *backend_xrender_init(session_t *ps) {
if (ps->o.dithered_present) {
log_warn("\"dithered-present\" is not supported by the xrender backend.");
}

auto xd = ccalloc(1, struct _xrender_data);
init_backend_base(&xd->base, ps);

Expand Down
2 changes: 2 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ typedef struct options {
/// A list of conditions of windows to which transparent clipping
/// should not apply
c2_lptr_t *transparent_clipping_blacklist;

bool dithered_present;
} options_t;

extern const char *const BACKEND_STRS[NUM_BKEND + 1];
Expand Down
2 changes: 2 additions & 0 deletions src/config_libconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
lcfg_lookup_bool(&cfg, "no-ewmh-fullscreen", &opt->no_ewmh_fullscreen);
// --transparent-clipping
lcfg_lookup_bool(&cfg, "transparent-clipping", &opt->transparent_clipping);
// --dithered_present
lcfg_lookup_bool(&cfg, "dithered-present", &opt->dithered_present);
// --transparent-clipping-exclude
parse_cfg_condlst(&cfg, &opt->transparent_clipping_blacklist,
"transparent-clipping-exclude");
Expand Down
8 changes: 8 additions & 0 deletions src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ static const struct picom_option picom_options[] = {
"similar to --opacity-rule. SHADER_PATH can be \"default\", in which case "
"the default shader will be used. Does not work when --legacy-backends is "
"enabled. See man page for more details"},
// 338 is transparent-clipping-exclude
{"dithered-present" , no_argument , 339, NULL , "Use higher precision during rendering, and apply dither when presenting the "
"rendered screen. Reduces banding artifacts, but might cause performance "
"degradation. Only works with OpenGL."},
{"legacy-backends" , no_argument , 733, NULL , "Use deprecated version of the backends."},
{"monitor-repaint" , no_argument , 800, NULL , "Highlight the updated area of the screen. For debugging."},
{"diagnostics" , no_argument , 801, NULL , "Print diagnostic information"},
Expand Down Expand Up @@ -716,6 +720,10 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
// --clip-shadow-above
condlst_add(&opt->shadow_clip_list, optarg);
break;
case 339:
// --dithered-present
opt->dithered_present = true;
break;
P_CASEBOOL(733, legacy_backends);
P_CASEBOOL(800, monitor_repaint);
case 801:
Expand Down