legacy glx backend rounded corners WIP

WIP: added fragment shader skeleton for rounded corners on glx
WIP: skeleton shader for rounded corners working (green mask)
wip: rounded corners shader test code semi-worikng?
wip: rounded corners frag shader works with green corners
changed some variable names in rounded corners shader
round_corners function moved again
rounded corners with glx: almost there...
rounded corners progress...
temp commit before changing lots of code
need cleanup now, but rounded corners are working with glx
rounded corners code cleanup
init rounded corners shader only if glx backend is used
rewrote shader code for better rounded corner rendering
Smooth glx corners

Authored-by: bhagwan <bhagwan@disroot.org>
Authored-by: Samuel Hand <samuel.d.hand@gmail.com>
This commit is contained in:
Yuxuan Shui 2020-09-30 16:09:27 +01:00
parent 3c38b36f04
commit 4a619d2404
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
5 changed files with 766 additions and 9 deletions

View File

@ -95,6 +95,16 @@ bool glx_init(session_t *ps, bool need_render) {
ppass->unifm_offset_x = -1;
ppass->unifm_offset_y = -1;
}
ps->psglx->round_passes = ccalloc(2, glx_round_pass_t);
for (int i = 0; i < 2; ++i) {
glx_round_pass_t *ppass = &ps->psglx->round_passes[i];
ppass->unifm_radius = -1;
ppass->unifm_texcoord = -1;
ppass->unifm_texsize = -1;
ppass->unifm_borderw = -1;
ppass->unifm_resolution = -1;
}
}
glx_session_t *psglx = ps->psglx;
@ -239,6 +249,15 @@ void glx_destroy(session_t *ps) {
}
free(ps->psglx->blur_passes);
for (int i = 0; i < 2; ++i) {
glx_round_pass_t *ppass = &ps->psglx->round_passes[i];
if (ppass->frag_shader)
glDeleteShader(ppass->frag_shader);
if (ppass->prog)
glDeleteProgram(ppass->prog);
}
free(ps->psglx->round_passes);
glx_free_prog_main(&ps->glx_prog_win);
gl_check_err();
@ -415,6 +434,227 @@ bool glx_init_blur(session_t *ps) {
return true;
}
static inline bool
glx_init_frag_shader_corners(glx_round_pass_t *ppass, const char *PREFIX_STR,
const char *SHADER_STR, const char *extension,
const char *sampler_type, const char *texture_func) {
// Build rounded corners shader
{
auto len = strlen(PREFIX_STR) + strlen(extension) + strlen(sampler_type) +
strlen(texture_func) + strlen(SHADER_STR) + 1;
char *shader_str = calloc(len, sizeof(char));
if (!shader_str) {
log_error("Failed to allocate %zd bytes for shader string.", len);
return false;
}
char *pc = shader_str;
sprintf(pc, PREFIX_STR, extension, sampler_type, texture_func);
pc += strlen(pc);
assert(strlen(shader_str) < len);
sprintf(pc, SHADER_STR);
assert(strlen(shader_str) < len);
#ifdef DEBUG_GLX
log_debug("Generated rounded corners shader:\n%s\n", shader_str);
#endif
ppass->frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, shader_str);
free(shader_str);
if (!ppass->frag_shader) {
log_error("Failed to create rounded corners fragment shader.");
return false;
}
// Build program
ppass->prog = gl_create_program(&ppass->frag_shader, 1);
if (!ppass->prog) {
log_error("Failed to create GLSL program.");
return false;
}
// Get uniform addresses
#define P_GET_UNIFM_LOC(name, target) \
{ \
ppass->target = glGetUniformLocation(ppass->prog, name); \
if (ppass->target < 0) { \
log_debug("Failed to get location of rounded corners uniform " \
"'" name "'. Might be troublesome."); \
} \
}
P_GET_UNIFM_LOC("u_radius", unifm_radius);
P_GET_UNIFM_LOC("u_texcoord", unifm_texcoord);
P_GET_UNIFM_LOC("u_texsize", unifm_texsize);
P_GET_UNIFM_LOC("u_borderw", unifm_borderw);
P_GET_UNIFM_LOC("u_is_focused", unifm_is_focused);
P_GET_UNIFM_LOC("u_resolution", unifm_resolution);
#undef P_GET_UNIFM_LOC
}
return true;
}
/**
* Initialize GLX rounded corners filter.
*/
bool glx_init_rounded_corners(session_t *ps) {
{
char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
// Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane
// Thanks to hiciu for reporting.
setlocale(LC_NUMERIC, "C");
static const char *FRAG_SHADER_PREFIX =
"#version 110\n"
"%s" // extensions
"uniform float u_radius;\n"
"uniform float u_borderw;\n"
"uniform int u_is_focused;\n"
"uniform vec2 u_texcoord;\n"
"uniform vec2 u_texsize;\n"
"uniform vec2 u_resolution;\n"
"uniform %s tex_scr;\n" // sampler2D | sampler2DRect
"\n"
"// https://www.shadertoy.com/view/ltS3zW\n"
"float RectSDF(vec2 p, vec2 b, float r) {\n"
" vec2 d = abs(p) - b + vec2(r);\n"
" return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;\n"
"}\n\n"
"// https://www.shadertoy.com/view/ldfSDj\n"
"float udRoundBox( vec2 p, vec2 b, float r )\n"
"{\n"
" return length(max(abs(p)-b+r,0.0))-r;\n"
"}\n\n"
"void main()\n"
"{\n"
" vec2 coord = vec2(u_texcoord.x, "
"u_resolution.y-u_texsize.y-u_texcoord.y);\n"
" vec4 u_v4SrcColor = %s(tex_scr, vec2(gl_TexCoord[0].st));\n"
"\n";
// Fragment shader (round corners)
// dst0 shader
static const char *FRAG_SHADER_ROUND_CORNERS_0 =
" float u_fRadiusPx = u_radius;\n"
" float u_fHalfBorderThickness = 0.0;\n"
" vec4 u_v4BorderColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
" vec4 u_v4FillColor = vec4(0.0, 0.0, 0.0, 0.0);\n"
" vec4 v4FromColor = u_v4BorderColor; //Always the border "
"color. If no border, this still should be set\n"
" vec4 v4ToColor = u_v4SrcColor; //Outside color is the "
"background texture\n"
"\n"
" vec2 u_v2HalfShapeSizePx = u_texsize/2.0 - "
"vec2(u_fHalfBorderThickness);\n"
" vec2 v_v2CenteredPos = (gl_FragCoord.xy - u_texsize.xy / 2.0 - "
"coord);\n"
"\n"
" float fDist = RectSDF(v_v2CenteredPos, u_v2HalfShapeSizePx, "
"u_fRadiusPx - u_fHalfBorderThickness);\n"
" if (u_fHalfBorderThickness > 0.0) {\n"
" if (fDist < 0.0) {\n"
" v4ToColor = u_v4FillColor;\n"
" }\n"
" fDist = abs(fDist) - u_fHalfBorderThickness;\n"
" } else {\n"
" v4FromColor = u_v4FillColor;\n"
" }\n"
" float fBlendAmount = clamp(fDist + 0.5, 0.0, 1.0);\n"
" vec4 c = mix(v4FromColor, v4ToColor, fBlendAmount);\n"
"\n"
" // final color\n"
" //if ( c == vec4(0.0,0.0,0.0,0.0) ) discard; else\n" // !!!!
" gl_FragColor = c;\n"
"\n"
"}\n";
// Fragment shader (round corners)
// dst1 shader
static const char *FRAG_SHADER_ROUND_CORNERS_1 =
" float u_fRadiusPx = u_radius;\n"
" float u_fHalfBorderThickness = 10.0 /2.0;//0.0;\n"
" vec4 u_v4BorderColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
" vec4 u_v4FillColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
" vec4 v4FromColor = u_v4BorderColor; //Always the border color. If "
"no border, this still should be set\n"
" vec4 v4ToColor = vec4(0.0, 0.0, 1.0, 1.0); //Outside color\n"
"\n"
" if (u_is_focused > 0) u_fHalfBorderThickness = u_borderw / 2.0 * "
"4.02;\n"
" vec2 u_v2HalfShapeSizePx = u_texsize/2.0 - "
"vec2(u_fHalfBorderThickness);\n"
" vec2 v_v2CenteredPos = (gl_FragCoord.xy - u_texsize.xy / 2.0 - "
"coord);\n"
"\n"
" float fDist = RectSDF(v_v2CenteredPos, u_v2HalfShapeSizePx, "
"u_fRadiusPx - u_fHalfBorderThickness);\n"
" if (u_fHalfBorderThickness > 0.0) {\n"
" if (fDist < 0.0) {\n"
" v4ToColor = u_v4FillColor;\n"
" }\n"
" fDist = abs(fDist) - u_fHalfBorderThickness;\n"
" } else {\n"
" v4FromColor = u_v4FillColor;\n"
" }\n"
" float fBlendAmount = clamp(fDist + 0.5, 0.0, 1.0);\n"
" vec4 c = mix(v4FromColor, v4ToColor, fBlendAmount);"
"\n"
" // final color\n"
" if ( c == vec4(0.0,0.0,0.0,0.0) ) discard; else\n"
" gl_FragColor = c;\n"
"}\n";
const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two;
const char *sampler_type = (use_texture_rect ? "sampler2DRect" : "sampler2D");
const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D");
char *extension = NULL;
if (use_texture_rect) {
mstrextend(&extension, "#extension GL_ARB_texture_rectangle : "
"require\n");
}
if (!extension) {
extension = strdup("");
}
if (!glx_init_frag_shader_corners(
&ps->psglx->round_passes[0], FRAG_SHADER_PREFIX,
FRAG_SHADER_ROUND_CORNERS_0, extension, sampler_type, texture_func)) {
log_error("Failed to create rounded corners fragment shader "
"PRE.");
setlocale(LC_NUMERIC, lc_numeric_old);
free(lc_numeric_old);
free(extension);
return false;
}
if (!glx_init_frag_shader_corners(
&ps->psglx->round_passes[1], FRAG_SHADER_PREFIX,
FRAG_SHADER_ROUND_CORNERS_1, extension, sampler_type, texture_func)) {
log_error("Failed to create rounded corners fragment shader "
"POST.");
setlocale(LC_NUMERIC, lc_numeric_old);
free(lc_numeric_old);
free(extension);
return false;
}
free(extension);
// Restore LC_NUMERIC
setlocale(LC_NUMERIC, lc_numeric_old);
free(lc_numeric_old);
}
gl_check_err();
return true;
}
/**
* Load a GLSL main program from shader strings.
*/
@ -449,6 +689,116 @@ bool glx_load_prog_main(const char *vshader_str, const char *fshader_str,
return true;
}
/**
* @brief Release binding of a texture.
*/
void glx_release_texture(session_t *ps attr_unused, glx_texture_t **pptex) {
glx_texture_t *ptex = *pptex;
// Release binding
if (ptex->texture) {
// log_info("Deleting texture wh(%d %d)", ptex->width, ptex->height);
glBindTexture(ptex->target, 0);
glDeleteTextures(1, &ptex->texture);
}
free(ptex);
*pptex = NULL;
gl_check_err();
}
/**
* Bind a X pixmap to an OpenGL texture.
*/
bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, int y,
int width attr_unused, int height attr_unused, bool repeat attr_unused) {
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID)
return true;
glx_texture_t *ptex = *pptex;
// log_trace("Copying xy(%d %d) wh(%d %d)", x, y, width, height);
// Release texture if parameters are inconsistent
if (ptex && ptex->texture && (ptex->width != width || ptex->height != height)) {
glx_release_texture(ps, &ptex);
}
// Allocate structure
if (!ptex) {
static const glx_texture_t GLX_TEX_DEF = {
.texture = 0,
.glpixmap = 0,
.pixmap = 0,
.target = 0,
.width = 0,
.height = 0,
.y_inverted = false,
};
ptex = cmalloc(glx_texture_t);
memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t));
*pptex = ptex;
ptex->width = width;
ptex->height = height;
ptex->target = GL_TEXTURE_RECTANGLE;
if (ps->psglx->has_texture_non_power_of_two)
ptex->target = GL_TEXTURE_2D;
}
// Create texture
if (!ptex->texture) {
// log_info("Generating texture for xy(%d %d) wh(%d %d)", x, y, width,
// height);
GLuint texture = 0;
glGenTextures(1, &texture);
if (texture) {
glEnable(ptex->target);
glBindTexture(ptex->target, texture);
glTexParameteri(ptex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(ptex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (repeat) {
glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_REPEAT);
} else {
glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
}
glTexImage2D(ptex->target, 0, GL_RGB, width, height, 0, GL_RGB,
GL_UNSIGNED_BYTE, NULL);
glBindTexture(ptex->target, 0);
// glDisable(ptex->target);
}
ptex->texture = texture;
}
if (!ptex->texture) {
log_error("Failed to allocate texture.");
return false;
}
// Read destination pixels into a texture
glEnable(ptex->target);
glBindTexture(ptex->target, ptex->texture);
if (width > 0 && height > 0)
glCopyTexSubImage2D(ptex->target, 0, 0, 0, x,
ps->root_height - y - height, width, height);
// Cleanup
glBindTexture(ptex->target, 0);
glDisable(ptex->target);
gl_check_err();
return true;
}
/**
* Bind a X pixmap to an OpenGL texture.
*/
@ -889,6 +1239,303 @@ glx_blur_dst_end:
return ret;
}
static void
bind_sampler_to_unit_with_texture(GLuint prog, GLchar const *const sampler_name,
GLuint texture_unit, GLenum tex_tgt, GLuint texture) {
glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(tex_tgt, texture);
GLint loc_sampler = glGetUniformLocation(prog, sampler_name);
glUniform1i(loc_sampler, (GLint)texture_unit);
}
bool glx_round_corners_dst0(session_t *ps, struct managed_win *w,
const glx_texture_t *ptex attr_unused, int shader_idx, int dx,
int dy, int width, int height, float z, float cr,
const region_t *reg_tgt attr_unused, glx_blur_cache_t *pbc) {
assert(shader_idx >= 0 && shader_idx <= 1);
assert(ps->psglx->round_passes[0].prog);
assert(ps->psglx->round_passes[1].prog);
const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST);
const bool have_stencil = glIsEnabled(GL_STENCIL_TEST);
bool ret = false;
// log_warn("dxy(%d, %d) wh(%d %d) rwh(%d %d) b(%d), f(%d)",
// dx, dy, width, height, ps->root_width, ps->root_height, w->g.border_width,
// w->focused);
// Calculate copy region size
glx_blur_cache_t ibc = {.width = 0, .height = 0};
if (!pbc)
pbc = &ibc;
int mdx = dx, mdy = dy, mwidth = width, mheight = height;
log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight);
GLenum tex_tgt = GL_TEXTURE_RECTANGLE;
if (ps->psglx->has_texture_non_power_of_two)
tex_tgt = GL_TEXTURE_2D;
// Free textures if size inconsistency discovered
if (mwidth != pbc->width || mheight != pbc->height)
free_glx_bc_resize(ps, pbc);
// Generate FBO and textures if needed
if (!pbc->textures[0])
pbc->textures[0] = glx_gen_texture(tex_tgt, mwidth, mheight);
GLuint tex_scr = pbc->textures[0];
pbc->width = mwidth;
pbc->height = mheight;
if (!tex_scr) {
log_error("Failed to allocate texture.");
goto glx_round_corners_dst_end;
}
// Read destination pixels into a texture
glEnable(tex_tgt);
glBindTexture(tex_tgt, tex_scr);
glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, mheight);
// Texture scaling factor
GLfloat texfac_x = 1.0f, texfac_y = 1.0f;
if (tex_tgt == GL_TEXTURE_2D) {
texfac_x /= (GLfloat)mwidth;
texfac_y /= (GLfloat)mheight;
}
// Paint it back
{
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
}
{
const glx_round_pass_t *ppass = &ps->psglx->round_passes[shader_idx];
assert(ppass->prog);
assert(tex_scr);
glBindTexture(tex_tgt, tex_scr);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
if (have_scissors)
glEnable(GL_SCISSOR_TEST);
if (have_stencil)
glEnable(GL_STENCIL_TEST);
// Our shader generates a transparent mid section
// with opaque corners copied from the background texture
// We must use blending to get the window pixels to appear
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glUseProgram(ppass->prog);
// If caller specified a texture use it as source
if (ptex) {
log_debug("ptex: %p wh(%d %d) %d %d", ptex, ptex->width,
ptex->height, ptex->target, ptex->texture);
bind_sampler_to_unit_with_texture(ppass->prog, "tex_scr", 0,
ptex->target, ptex->texture);
} else {
bind_sampler_to_unit_with_texture(ppass->prog, "tex_scr", 0,
tex_tgt, tex_scr);
}
if (ppass->unifm_radius >= 0)
glUniform1f(ppass->unifm_radius, cr);
if (ppass->unifm_texcoord >= 0)
glUniform2f(ppass->unifm_texcoord, (float)dx, (float)dy);
if (ppass->unifm_texsize >= 0)
glUniform2f(ppass->unifm_texsize, (float)mwidth, (float)mheight);
if (ppass->unifm_borderw >= 0)
glUniform1f(ppass->unifm_borderw, w->g.border_width);
if (ppass->unifm_is_focused >= 0)
glUniform1i(ppass->unifm_is_focused, w->focused);
if (ppass->unifm_resolution >= 0)
glUniform2f(ppass->unifm_resolution, (float)ps->root_width,
(float)ps->root_height);
// Painting
{
P_PAINTREG_START(crect) {
// XXX explain these variables
auto rx = (GLfloat)(crect.x1 - dx);
auto ry = (GLfloat)(crect.y1 - dy);
auto rxe = rx + (GLfloat)(crect.x2 - crect.x1);
auto rye = ry + (GLfloat)(crect.y2 - crect.y1);
// Rectangle textures have [0-w] [0-h] while 2D texture
// has [0-1] [0-1] Thanks to amonakov for pointing out!
if (GL_TEXTURE_2D == tex_tgt) {
rx = rx / (GLfloat)width;
ry = ry / (GLfloat)height;
rxe = rxe / (GLfloat)width;
rye = rye / (GLfloat)height;
}
auto rdx = (GLfloat)crect.x1;
auto rdy = (GLfloat)(ps->root_height - crect.y1);
auto rdxe = (GLfloat)rdx + (GLfloat)(crect.x2 - crect.x1);
auto rdye = (GLfloat)rdy - (GLfloat)(crect.y2 - crect.y1);
// Invert Y if needed, this may not work as expected,
// though. I don't have such a FBConfig to test with.
// if (ptex && !ptex->y_inverted) {
if (shader_idx == 0) {
ry = 1.0f - ry;
rye = 1.0f - rye;
}
// log_trace("Rect %d (i:%d): %f, %f, %f, %f -> %f, %f,
// %f, %f", ri ,ptex ? ptex->y_inverted : -1, rx, ry,
// rxe,
// rye, rdx, rdy, rdxe, rdye);
glTexCoord2f(rx, ry);
glVertex3f(rdx, rdy, z);
glTexCoord2f(rxe, ry);
glVertex3f(rdxe, rdy, z);
glTexCoord2f(rxe, rye);
glVertex3f(rdxe, rdye, z);
glTexCoord2f(rx, rye);
glVertex3f(rdx, rdye, z);
}
P_PAINTREG_END();
}
glUseProgram(0);
glDisable(GL_BLEND);
}
ret = true;
glx_round_corners_dst_end:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(tex_tgt, 0);
glDisable(tex_tgt);
if (have_scissors)
glEnable(GL_SCISSOR_TEST);
if (have_stencil)
glEnable(GL_STENCIL_TEST);
if (&ibc == pbc) {
free_glx_bc(ps, pbc);
}
gl_check_err();
return ret;
}
bool glx_round_corners_dst1(session_t *ps, struct managed_win *w, const glx_texture_t *ptex,
int shader_idx, int dx, int dy, int width, int height,
float z, float cr, const region_t *reg_tgt attr_unused,
glx_blur_cache_t *pbc attr_unused) {
assert(shader_idx >= 0 && shader_idx <= 1);
assert(ps->psglx->round_passes[0].prog);
assert(ps->psglx->round_passes[1].prog);
bool ret = false;
{
const glx_round_pass_t *ppass = &ps->psglx->round_passes[shader_idx];
assert(ppass->prog);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(ppass->prog);
// If caller specified a texture use it as source
if (ptex) {
log_debug("ptex: %p wh(%d %d) %d %d", ptex, ptex->width,
ptex->height, ptex->target, ptex->texture);
bind_sampler_to_unit_with_texture(ppass->prog, "tex_scr", 0,
ptex->target, ptex->texture);
}
if (ppass->unifm_radius >= 0)
glUniform1f(ppass->unifm_radius, cr);
if (ppass->unifm_texcoord >= 0)
glUniform2f(ppass->unifm_texcoord, (float)dx, (float)dy);
if (ppass->unifm_texsize >= 0)
glUniform2f(ppass->unifm_texsize, (float)width, (float)height);
if (ppass->unifm_borderw >= 0)
glUniform1f(ppass->unifm_borderw, w->g.border_width);
if (ppass->unifm_is_focused >= 0)
glUniform1i(ppass->unifm_is_focused, w->focused);
if (ppass->unifm_resolution >= 0)
glUniform2f(ppass->unifm_resolution, (float)ps->root_width,
(float)ps->root_height);
// Painting
{
P_PAINTREG_START(crect) {
// XXX explain these variables
auto rx = (GLfloat)(crect.x1 - dx);
auto ry = (GLfloat)(crect.y1 - dy);
auto rxe = rx + (GLfloat)(crect.x2 - crect.x1);
auto rye = ry + (GLfloat)(crect.y2 - crect.y1);
// Rectangle textures have [0-w] [0-h] while 2D texture
// has [0-1] [0-1] Thanks to amonakov for pointing out!
if (GL_TEXTURE_2D == ptex->target) {
rx = rx / (GLfloat)width;
ry = ry / (GLfloat)height;
rxe = rxe / (GLfloat)width;
rye = rye / (GLfloat)height;
}
auto rdx = (GLfloat)crect.x1;
auto rdy = (GLfloat)(ps->root_height - crect.y1);
auto rdxe = (GLfloat)rdx + (GLfloat)(crect.x2 - crect.x1);
auto rdye = (GLfloat)rdy - (GLfloat)(crect.y2 - crect.y1);
// Invert Y if needed, this may not work as expected,
// though. I don't have such a FBConfig to test with.
// if (ptex && !ptex->y_inverted) {
if (shader_idx == 0) {
ry = 1.0f - ry;
rye = 1.0f - rye;
}
// log_trace("Rect %d (i:%d): %f, %f, %f, %f -> %f, %f,
// %f, %f", ri ,ptex ? ptex->y_inverted : -1, rx, ry,
// rxe,
// rye, rdx, rdy, rdxe, rdye);
glTexCoord2f(rx, ry);
glVertex3f(rdx, rdy, z);
glTexCoord2f(rxe, ry);
glVertex3f(rdxe, rdy, z);
glTexCoord2f(rxe, rye);
glVertex3f(rdxe, rdye, z);
glTexCoord2f(rx, rye);
glVertex3f(rdx, rdye, z);
}
P_PAINTREG_END();
}
glUseProgram(0);
glDisable(GL_BLEND);
}
ret = true;
// glBindFramebuffer(GL_FRAMEBUFFER, 0);
gl_check_err();
return ret;
}
bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
GLfloat factor, const region_t *reg_tgt) {
// It's possible to dim in glx_render(), but it would be over-complicated
@ -923,7 +1570,7 @@ bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
* @brief Render a region with texture data.
*/
bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy,
int width, int height, int z, double opacity, bool argb, bool neg,
int width, int height, int z, double opacity, bool argb, bool neg, int cr,
const region_t *reg_tgt, const glx_prog_main_t *pprogram) {
if (!ptex || !ptex->texture) {
log_error("Missing texture.");
@ -938,7 +1585,7 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx,
glEnable(ptex->target);
// Enable blending if needed
if (opacity < 1.0 || argb) {
if (opacity < 1.0 || argb || cr > 0) {
glEnable(GL_BLEND);
@ -1038,7 +1685,8 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx,
if (pprogram->unifm_tex >= 0)
glUniform1i(pprogram->unifm_tex, 0);
if (pprogram->unifm_time >= 0)
glUniform1f(pprogram->unifm_time, (float)ts.tv_sec * 1000.0f + (float)ts.tv_nsec / 1.0e6f);
glUniform1f(pprogram->unifm_time, (float)ts.tv_sec * 1000.0f +
(float)ts.tv_nsec / 1.0e6f);
}
// log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d", x, y, width, height,

View File

@ -40,6 +40,25 @@ typedef struct {
GLint unifm_factor_center;
} glx_blur_pass_t;
typedef struct {
/// Fragment shader for rounded corners.
GLuint frag_shader;
/// GLSL program for rounded corners.
GLuint prog;
/// Location of uniform "radius" in rounded-corners GLSL program.
GLint unifm_radius;
/// Location of uniform "texcoord" in rounded-corners GLSL program.
GLint unifm_texcoord;
/// Location of uniform "texsize" in rounded-corners GLSL program.
GLint unifm_texsize;
/// Location of uniform "borderw" in rounded-corners GLSL program.
GLint unifm_borderw;
/// Location of uniform "is_focused" in rounded-corners GLSL program.
GLint unifm_is_focused;
/// Location of uniform "resolution" in rounded-corners GLSL program.
GLint unifm_resolution;
} glx_round_pass_t;
/// Structure containing GLX-dependent data for a session.
typedef struct glx_session {
// === OpenGL related ===
@ -50,6 +69,7 @@ typedef struct glx_session {
/// Current GLX Z value.
int z;
glx_blur_pass_t *blur_passes;
glx_round_pass_t *round_passes;
} glx_session_t;
/// @brief Wrapper of a binded GLX texture.
@ -70,7 +90,7 @@ bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
GLfloat factor, const region_t *reg_tgt);
bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy,
int width, int height, int z, double opacity, bool argb, bool neg,
int width, int height, int z, double opacity, bool argb, bool neg, int cr,
const region_t *reg_tgt, const glx_prog_main_t *pprogram);
bool glx_init(session_t *ps, bool need_render);
@ -81,6 +101,8 @@ void glx_on_root_change(session_t *ps);
bool glx_init_blur(session_t *ps);
bool glx_init_rounded_corners(session_t *ps);
#ifdef CONFIG_OPENGL
bool glx_load_prog_main(const char *vshader_str, const char *fshader_str,
glx_prog_main_t *pprogram);
@ -91,6 +113,11 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
void glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
bool glx_bind_texture(session_t *ps, glx_texture_t **pptex, int x, int y, int width,
int height, bool repeat);
void glx_release_texture(session_t *ps, glx_texture_t **ptex);
void glx_paint_pre(session_t *ps, region_t *preg) attr_nonnull(1, 2);
/**
@ -105,6 +132,14 @@ void glx_set_clip(session_t *ps, const region_t *reg);
bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc);
bool glx_round_corners_dst0(session_t *ps, struct managed_win *w, const glx_texture_t *ptex,
int shader_idx, int dx, int dy, int width, int height, float z,
float cr, const region_t *reg_tgt, glx_blur_cache_t *pbc);
bool glx_round_corners_dst1(session_t *ps, struct managed_win *w, const glx_texture_t *ptex,
int shader_idx, int dx, int dy, int width, int height, float z,
float cr, const region_t *reg_tgt, glx_blur_cache_t *pbc);
GLuint glx_create_shader(GLenum shader_type, const char *shader_str);
GLuint glx_create_program(const GLuint *const shaders, int nshaders);
@ -210,5 +245,7 @@ static inline void free_win_res_glx(session_t *ps, struct managed_win *w) {
free_paint_glx(ps, &w->shadow_paint);
#ifdef CONFIG_OPENGL
free_glx_bc(ps, &w->glx_blur_cache);
free_glx_bc(ps, &w->glx_round_cache);
free_texture(ps, &w->glx_texture_bg);
#endif
}

View File

@ -1002,9 +1002,8 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
"properly under X Render backend.");
}
if (opt->corner_radius > 0 &&
(opt->backend != BKEND_XRENDER || opt->experimental_backends)) {
log_warn("Rounded corner is only supported on legacy xrender backend, it "
if (opt->corner_radius > 0 && opt->experimental_backends) {
log_warn("Rounded corner is only supported on legacy backends, it "
"will be disabled");
opt->corner_radius = 0;
}

View File

@ -333,7 +333,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
#ifdef CONFIG_OPENGL
case BKEND_GLX:
glx_render(ps, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, argb,
neg, reg_paint, pprogram);
neg, cr, reg_paint, pprogram);
ps->psglx->z += 1;
break;
#endif
@ -389,6 +389,47 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
return true;
}
/**
* Rounde the corners of a window.
* Applies a fragment shader to discard corners
*
*/
static inline void
win_round_corners(session_t *ps, struct managed_win *w, const glx_texture_t *ptex,
int shader_idx, float cr, xcb_render_picture_t tgt_buffer attr_unused,
const region_t *reg_paint) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
// log_debug("x:%d y:%d w:%d h:%d", x, y, wid, hei);
switch (ps->o.backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: {
// XRender method is implemented inside render()
} break;
#ifdef CONFIG_OPENGL
case BKEND_GLX:
if (shader_idx == 1) {
glx_round_corners_dst1(ps, w, ptex, shader_idx, x, y, wid, hei,
(float)ps->psglx->z - 0.5f, cr, reg_paint,
&w->glx_round_cache);
} else {
glx_round_corners_dst0(ps, w, ptex, shader_idx, x, y, wid, hei,
(float)ps->psglx->z - 0.5f, cr, reg_paint,
&w->glx_round_cache);
}
break;
#endif
default: assert(0);
}
#ifndef CONFIG_OPENGL
(void)reg_paint;
#endif
}
/**
* Paint a window itself and dim it if asked.
*/
@ -1112,6 +1153,16 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
if (pixman_region32_not_empty(&reg_tmp)) {
set_tgt_clip(ps, &reg_tmp);
// If rounded corners backup the region first
if (w->corner_radius > 0) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
glx_bind_texture(ps, &w->glx_texture_bg, x, y, wid, hei, false);
}
// Blur window background
if (w->blur_background &&
(w->mode == WMODE_TRANS ||
@ -1121,6 +1172,13 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
// Painting the window
paint_one(ps, w, &reg_tmp);
// Round window corners
if (w->corner_radius > 0) {
win_round_corners(ps, w, w->glx_texture_bg, 0,
(float)w->corner_radius,
ps->tgt_buffer.pict, &bshape_corners);
}
}
}
@ -1210,7 +1268,7 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
glFlush();
glXWaitX();
glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width,
ps->root_height, 0, 1.0, false, false, &region, NULL);
ps->root_height, 0, 1.0, false, false, 0, &region, NULL);
fallthrough();
case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break;
#endif
@ -1374,6 +1432,18 @@ bool init_render(session_t *ps) {
return false;
}
}
// Initialize our rounded corners fragment shader
if (ps->o.corner_radius > 0 && ps->o.backend == BKEND_GLX) {
#ifdef CONFIG_OPENGL
if (!glx_init_rounded_corners(ps)) {
log_error("Failed to init rounded corners shader.");
return false;
}
#else
assert(false);
#endif
}
return true;
}

View File

@ -270,6 +270,9 @@ struct managed_win {
#ifdef CONFIG_OPENGL
/// Textures and FBO background blur use.
glx_blur_cache_t glx_blur_cache;
glx_blur_cache_t glx_round_cache;
/// Background texture of the window
glx_texture_t *glx_texture_bg;
#endif
};