mirror of
https://github.com/yshui/picom.git
synced 2024-11-11 13:51:02 -05:00
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:
parent
3c38b36f04
commit
4a619d2404
5 changed files with 766 additions and 9 deletions
654
src/opengl.c
654
src/opengl.c
|
@ -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,
|
||||
|
|
39
src/opengl.h
39
src/opengl.h
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
74
src/render.c
74
src/render.c
|
@ -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(®_tmp)) {
|
||||
set_tgt_clip(ps, ®_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, ®_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, ®ion, NULL);
|
||||
ps->root_height, 0, 1.0, false, false, 0, ®ion, 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue