From a29caeaf3d5d8cf7ada1b7c076bff91c830c11dd Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 25 Aug 2022 18:00:25 +0100 Subject: [PATCH] ackend: gl: implement shadow_from_mask Signed-off-by: Yuxuan Shui --- src/backend/gl/blur.c | 4 +- src/backend/gl/gl_common.c | 169 +++++++++++++++++++++++++++++++++++++ src/backend/gl/gl_common.h | 18 +++- src/backend/gl/glx.c | 7 +- src/backend/gl/shaders.c | 10 +++ 5 files changed, 201 insertions(+), 7 deletions(-) diff --git a/src/backend/gl/blur.c b/src/backend/gl/blur.c index 271a17ab..b73aeeeb 100644 --- a/src/backend/gl/blur.c +++ b/src/backend/gl/blur.c @@ -379,10 +379,10 @@ bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, } glBindFramebuffer(GL_FRAMEBUFFER, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDeleteBuffers(4, bo); diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 3e7c5c8a..ac9de95d 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -881,6 +881,17 @@ bool gl_init(struct gl_data *gd, session_t *ps) { glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); glUseProgram(0); + gd->shadow_shader.prog = + gl_create_program_from_str(present_vertex_shader, shadow_colorization_frag); + gd->shadow_shader.uniform_color = + glGetUniformLocationChecked(gd->shadow_shader.prog, "color"); + pml = glGetUniformLocationChecked(gd->shadow_shader.prog, "projection"); + glUseProgram(gd->shadow_shader.prog); + glUniform1i(glGetUniformLocationChecked(gd->shadow_shader.prog, "tex"), 0); + glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); + glUseProgram(0); + glBindFragDataLocation(gd->shadow_shader.prog, 0, "out_color"); + gd->brightness_shader.prog = gl_create_program_from_str(interpolating_vert, interpolating_frag); if (!gd->brightness_shader.prog) { @@ -919,6 +930,7 @@ bool gl_init(struct gl_data *gd, session_t *ps) { gd->is_nvidia = false; } gd->has_robustness = gl_has_extension("GL_ARB_robustness"); + gl_check_err(); return true; } @@ -1150,6 +1162,163 @@ bool gl_set_image_property(backend_t *backend_data, enum image_properties prop, return true; } +struct gl_shadow_context { + double radius; + void *blur_context; +}; + +struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius) { + auto ctx = ccalloc(1, struct gl_shadow_context); + ctx->radius = radius; + + struct dual_kawase_blur_args args = { + .size = (int)radius, + .strength = 0, + }; + ctx->blur_context = gl_create_blur_context(base, BLUR_METHOD_DUAL_KAWASE, &args); + return (struct backend_shadow_context *)ctx; +} + +void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shadow_context *ctx) { + auto ctx_ = (struct gl_shadow_context *)ctx; + gl_destroy_blur_context(base, (struct backend_blur_context *)ctx_->blur_context); + free(ctx_); +} + +void *gl_shadow_from_mask(backend_t *base, void *mask, + struct backend_shadow_context *sctx, struct color color) { + log_debug("Create shadow from mask"); + auto gd = (struct gl_data *)base; + auto img = (struct backend_image *)mask; + auto inner = (struct gl_texture *)img->inner; + auto gsctx = (struct gl_shadow_context *)sctx; + int radius = (int)gsctx->radius; + + auto new_inner = ccalloc(1, struct gl_texture); + new_inner->width = inner->width + radius * 2; + new_inner->height = inner->height + radius * 2; + new_inner->texture = gl_new_texture(GL_TEXTURE_2D); + new_inner->has_alpha = inner->has_alpha; + new_inner->y_inverted = false; + auto new_img = default_new_backend_image(new_inner->width, new_inner->height); + new_img->inner = (struct backend_image_inner_base *)new_inner; + new_img->inner->refcount = 1; + + // Render the mask to a texture, so inversion and corner radius can be + // applied. + auto source_texture = gl_new_texture(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, source_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, new_inner->width, new_inner->height, 0, + GL_RED, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + GLuint fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + source_texture, 0); + glDrawBuffer(GL_COLOR_ATTACHMENT0); + if (img->color_inverted) { + // If the mask is inverted, clear the source_texture to white, so the + // "outside" of the mask would be correct + glClearColor(1, 1, 1, 1); + } else { + glClearColor(0, 0, 0, 1); + } + glClear(GL_COLOR_BUFFER_BIT); + { + // clang-format off + // interleaved vertex coordinates and texture coordinates + GLint coords[] = {radius , radius , 0 , 0, + radius + inner->width, radius , inner->width, 0, + radius + inner->width, radius + inner->height, inner->width, inner->height, + radius , radius + inner->height, 0 , inner->height,}; + // clang-format on + GLuint indices[] = {0, 1, 2, 2, 3, 0}; + _gl_compose(base, mask, fbo, NULL, (coord_t){0}, coords, indices, 1); + } + + gl_check_err(); + + glActiveTexture(GL_TEXTURE0); + auto tmp_texture = gl_new_texture(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, tmp_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, new_inner->width, new_inner->height, 0, + GL_RED, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + tmp_texture, 0); + + region_t reg_blur; + pixman_region32_init_rect(®_blur, 0, 0, (unsigned int)new_inner->width, + (unsigned int)new_inner->height); + // gl_blur expects reg_blur to be in X coordinate system (i.e. y flipped), but we + // are covering the whole texture so we don't need to worry about that. + gl_blur_impl(1.0, gsctx->blur_context, NULL, (coord_t){0}, ®_blur, NULL, + source_texture, + (geometry_t){.width = new_inner->width, .height = new_inner->height}, + fbo, gd->default_mask_texture); + pixman_region32_fini(®_blur); + + // Colorize the shadow with color. + log_debug("Colorize shadow"); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, new_inner->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, new_inner->width, new_inner->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + new_inner->texture, 0); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + glBindTexture(GL_TEXTURE_2D, tmp_texture); + glUseProgram(gd->shadow_shader.prog); + glUniform4f(gd->shadow_shader.uniform_color, (GLfloat)color.red, + (GLfloat)color.green, (GLfloat)color.blue, (GLfloat)color.alpha); + + // clang-format off + GLuint indices[] = {0, 1, 2, 2, 3, 0}; + GLint coord[] = {0 , 0 , + new_inner->width , 0 , + new_inner->width , new_inner->height, + 0 , new_inner->height,}; + // clang-format on + + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + GLuint bo[2]; + glGenBuffers(2, bo); + glBindBuffer(GL_ARRAY_BUFFER, bo[0]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]); + glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 8, coord, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices, + GL_STATIC_DRAW); + + glEnableVertexAttribArray(vert_coord_loc); + glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 2, NULL); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL); + + glDisableVertexAttribArray(vert_coord_loc); + glBindVertexArray(0); + glDeleteVertexArrays(1, &vao); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDeleteBuffers(2, bo); + + glDeleteTextures(1, (GLuint[]){source_texture, tmp_texture}); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + gl_check_err(); + return new_img; +} + enum device_status gl_device_status(backend_t *base) { auto gd = (struct gl_data *)base; if (!gd->has_robustness) { diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index 743d8b51..2c288529 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -53,6 +53,11 @@ typedef struct { GLuint prog; } gl_brightness_shader_t; +typedef struct { + GLuint prog; + GLint uniform_color; +} gl_shadow_shader_t; + // Program and uniforms for blur shader typedef struct { GLuint prog; @@ -98,6 +103,7 @@ struct gl_data { gl_win_shader_t *default_shader; gl_brightness_shader_t brightness_shader; gl_fill_shader_t fill_shader; + gl_shadow_shader_t shadow_shader; GLuint back_texture, back_fbo; GLuint present_prog; @@ -155,8 +161,16 @@ 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); 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); +void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shadow_context *ctx); +void *gl_shadow_from_mask(backend_t *base, void *mask, + struct backend_shadow_context *sctx, struct color color); void gl_get_blur_size(void *blur_context, int *width, int *height); void gl_fill(backend_t *base, struct color, const region_t *clip); @@ -272,5 +286,5 @@ 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[]; + fill_frag[], fill_vert[], interpolating_frag[], interpolating_vert[], win_shader_glsl[], + win_shader_default[], present_vertex_shader[], shadow_colorization_frag[]; diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index 26aaa49b..109bec94 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -536,9 +536,10 @@ struct backend_operations glx_ops = { .is_image_transparent = default_is_image_transparent, .present = glx_present, .buffer_age = glx_buffer_age, - .create_shadow_context = default_create_shadow_context, - .destroy_shadow_context = default_destroy_shadow_context, - .render_shadow = default_backend_render_shadow, + .create_shadow_context = gl_create_shadow_context, + .destroy_shadow_context = gl_destroy_shadow_context, + .render_shadow = backend_render_shadow_from_mask, + .shadow_from_mask = gl_shadow_from_mask, .make_mask = gl_make_mask, .fill = gl_fill, .create_blur_context = gl_create_blur_context, diff --git a/src/backend/gl/shaders.c b/src/backend/gl/shaders.c index 7439cd33..23866894 100644 --- a/src/backend/gl/shaders.c +++ b/src/backend/gl/shaders.c @@ -174,4 +174,14 @@ const char vertex_shader[] = GLSL(330, texcoord = in_texcoord + texorig; } ); +const char shadow_colorization_frag[] = GLSL(330, + uniform vec4 color; + uniform sampler2D tex; + in vec2 texcoord; + out vec4 out_color; + void main() { + vec4 c = texelFetch(tex, ivec2(texcoord), 0); + out_color = c.r * color; + } +); // clang-format on