aboutsummaryrefslogtreecommitdiff
path: root/src/backend/gl/gl_common.c
diff options
context:
space:
mode:
authorallusive-dev <[email protected]>2023-10-30 15:12:21 +1100
committerallusive-dev <[email protected]>2023-10-30 15:12:21 +1100
commitac33357e7ce7c474aeaffc92e381020289d767a2 (patch)
tree7f05fa79b3ccd7834f85cc65a07fbd4f8030eb94 /src/backend/gl/gl_common.c
parentCreate FUNDING.yml (diff)
downloadcompfy-1.0.0.tar.xz
compfy-1.0.0.zip
Version 1.01.0.0
Diffstat (limited to 'src/backend/gl/gl_common.c')
-rw-r--r--src/backend/gl/gl_common.c1479
1 files changed, 456 insertions, 1023 deletions
diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c
index 8cc5a05..a7d2aab 100644
--- a/src/backend/gl/gl_common.c
+++ b/src/backend/gl/gl_common.c
@@ -2,10 +2,10 @@
// Copyright (c) Yuxuan Shui <[email protected]>
#include <GL/gl.h>
#include <GL/glext.h>
-#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
+#include <time.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include "backend/backend.h"
@@ -22,51 +22,6 @@
#include "backend/backend_common.h"
#include "backend/gl/gl_common.h"
-#define GLSL(version, ...) "#version " #version "\n" #__VA_ARGS__
-#define QUOTE(...) #__VA_ARGS__
-
-static const GLuint vert_coord_loc = 0;
-static const GLuint vert_in_texcoord_loc = 1;
-
-struct gl_blur_context {
- enum blur_method method;
- gl_blur_shader_t *blur_shader;
-
- /// Temporary textures used for blurring
- GLuint *blur_textures;
- int blur_texture_count;
- /// Temporary fbos used for blurring
- GLuint *blur_fbos;
- int blur_fbo_count;
-
- /// Cached dimensions of each blur_texture. They are the same size as the target,
- /// so they are always big enough without resizing.
- /// Turns out calling glTexImage to resize is expensive, so we avoid that.
- struct texture_size {
- int width;
- int height;
- } * texture_sizes;
-
- /// Cached dimensions of the offscreen framebuffer. It's the same size as the
- /// target but is expanded in either direction by resize_width / resize_height.
- int fb_width, fb_height;
-
- /// How much do we need to resize the damaged region for blurring.
- int resize_width, resize_height;
-
- int npasses;
-};
-
-static GLint glGetUniformLocationChecked(GLuint p, const char *name) {
- auto ret = glGetUniformLocation(p, name);
- if (ret < 0) {
- log_info("Failed to get location of uniform '%s'. This is normal when "
- "using custom shaders.",
- name);
- }
- return ret;
-}
-
GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
log_trace("===\n%s\n===", shader_str);
@@ -83,7 +38,7 @@ GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
{
GLint status = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
- if (GL_FALSE == status) {
+ if (status == GL_FALSE) {
GLint log_len = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
if (log_len) {
@@ -103,6 +58,7 @@ end:
glDeleteShader(shader);
shader = 0;
}
+ gl_check_err();
return shader;
}
@@ -115,15 +71,16 @@ GLuint gl_create_program(const GLuint *const shaders, int nshaders) {
goto end;
}
- for (int i = 0; i < nshaders; ++i)
+ for (int i = 0; i < nshaders; ++i) {
glAttachShader(program, shaders[i]);
+ }
glLinkProgram(program);
// Get program status
{
GLint status = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &status);
- if (GL_FALSE == status) {
+ if (status == GL_FALSE) {
GLint log_len = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len);
if (log_len) {
@@ -138,59 +95,83 @@ GLuint gl_create_program(const GLuint *const shaders, int nshaders) {
end:
if (program) {
- for (int i = 0; i < nshaders; ++i)
+ for (int i = 0; i < nshaders; ++i) {
glDetachShader(program, shaders[i]);
+ }
}
if (program && !success) {
glDeleteProgram(program);
program = 0;
}
+ gl_check_err();
return program;
}
/**
- * @brief Create a program from vertex and fragment shader strings.
+ * @brief Create a program from NULL-terminated arrays of vertex and fragment shader
+ * strings.
*/
-GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str) {
- GLuint vert_shader = 0;
- GLuint frag_shader = 0;
- GLuint prog = 0;
-
- if (vert_shader_str)
- vert_shader = gl_create_shader(GL_VERTEX_SHADER, vert_shader_str);
- if (frag_shader_str)
- frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, frag_shader_str);
+GLuint gl_create_program_from_strv(const char **vert_shaders, const char **frag_shaders) {
+ int vert_count, frag_count;
+ for (vert_count = 0; vert_shaders && vert_shaders[vert_count]; ++vert_count) {
+ }
+ for (frag_count = 0; frag_shaders && frag_shaders[frag_count]; ++frag_count) {
+ }
- {
- GLuint shaders[2];
- int count = 0;
- if (vert_shader) {
- shaders[count++] = vert_shader;
- }
- if (frag_shader) {
- shaders[count++] = frag_shader;
+ GLuint prog = 0;
+ auto shaders = (GLuint *)ccalloc(vert_count + frag_count, GLuint);
+ for (int i = 0; i < vert_count; ++i) {
+ shaders[i] = gl_create_shader(GL_VERTEX_SHADER, vert_shaders[i]);
+ if (shaders[i] == 0) {
+ goto out;
}
- if (count) {
- prog = gl_create_program(shaders, count);
+ }
+ for (int i = 0; i < frag_count; ++i) {
+ shaders[vert_count + i] =
+ gl_create_shader(GL_FRAGMENT_SHADER, frag_shaders[i]);
+ if (shaders[vert_count + i] == 0) {
+ goto out;
}
}
- if (vert_shader)
- glDeleteShader(vert_shader);
- if (frag_shader)
- glDeleteShader(frag_shader);
+ prog = gl_create_program(shaders, vert_count + frag_count);
+
+out:
+ for (int i = 0; i < vert_count + frag_count; ++i) {
+ if (shaders[i] != 0) {
+ glDeleteShader(shaders[i]);
+ }
+ }
+ free(shaders);
+ gl_check_err();
return prog;
}
-static void gl_free_prog_main(gl_win_shader_t *pprogram) {
- if (!pprogram)
+/**
+ * @brief Create a program from vertex and fragment shader strings.
+ */
+GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str) {
+ const char *vert_shaders[2] = {vert_shader_str, NULL};
+ const char *frag_shaders[2] = {frag_shader_str, NULL};
+
+ return gl_create_program_from_strv(vert_shaders, frag_shaders);
+}
+
+void gl_destroy_window_shader(backend_t *backend_data attr_unused, void *shader) {
+ if (!shader) {
return;
+ }
+
+ auto pprogram = (gl_win_shader_t *)shader;
if (pprogram->prog) {
glDeleteProgram(pprogram->prog);
pprogram->prog = 0;
}
+ gl_check_err();
+
+ free(shader);
}
/*
@@ -368,9 +349,15 @@ static GLuint gl_average_texture_color(backend_t *base, struct backend_image *im
* @param reg_visible ignored
*/
static void _gl_compose(backend_t *base, struct backend_image *img, GLuint target,
- GLint *coord, GLuint *indices, int nrects) {
+ struct backend_image *mask, coord_t mask_offset, GLint *coord,
+ GLuint *indices, int nrects) {
+ // FIXME(yshui) breaks when `mask` and `img` doesn't have the same y_inverted
+ // value. but we don't ever hit this problem because all of our
+ // images and masks are y_inverted.
auto gd = (struct gl_data *)base;
auto inner = (struct gl_texture *)img->inner;
+ auto mask_texture =
+ mask ? ((struct gl_texture *)mask->inner)->texture : gd->default_mask_texture;
if (!img || !inner->texture) {
log_error("Missing texture.");
return;
@@ -381,27 +368,64 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
brightness = gl_average_texture_color(base, img);
}
- assert(gd->win_shader.prog);
- glUseProgram(gd->win_shader.prog);
- if (gd->win_shader.unifm_opacity >= 0) {
- glUniform1f(gd->win_shader.unifm_opacity, (float)img->opacity);
+ auto win_shader = inner->shader;
+ if (!win_shader) {
+ win_shader = gd->default_shader;
}
- if (gd->win_shader.unifm_invert_color >= 0) {
- glUniform1i(gd->win_shader.unifm_invert_color, img->color_inverted);
+
+ assert(win_shader);
+ assert(win_shader->prog);
+ glUseProgram(win_shader->prog);
+ if (win_shader->uniform_opacity >= 0) {
+ glUniform1f(win_shader->uniform_opacity, (float)img->opacity);
+ }
+ if (win_shader->uniform_invert_color >= 0) {
+ glUniform1i(win_shader->uniform_invert_color, img->color_inverted);
+ }
+ if (win_shader->uniform_tex >= 0) {
+ glUniform1i(win_shader->uniform_tex, 0);
}
- if (gd->win_shader.unifm_tex >= 0) {
- glUniform1i(gd->win_shader.unifm_tex, 0);
+ if (win_shader->uniform_dim >= 0) {
+ glUniform1f(win_shader->uniform_dim, (float)img->dim);
}
- if (gd->win_shader.unifm_dim >= 0) {
- glUniform1f(gd->win_shader.unifm_dim, (float)img->dim);
+ if (win_shader->uniform_brightness >= 0) {
+ glUniform1i(win_shader->uniform_brightness, 1);
}
- if (gd->win_shader.unifm_brightness >= 0) {
- glUniform1i(gd->win_shader.unifm_brightness, 1);
+ if (win_shader->uniform_max_brightness >= 0) {
+ glUniform1f(win_shader->uniform_max_brightness, (float)img->max_brightness);
}
- if (gd->win_shader.unifm_max_brightness >= 0) {
- glUniform1f(gd->win_shader.unifm_max_brightness, (float)img->max_brightness);
+ if (win_shader->uniform_corner_radius >= 0) {
+ glUniform1f(win_shader->uniform_corner_radius, (float)img->corner_radius);
+ }
+ if (win_shader->uniform_border_width >= 0) {
+ auto border_width = img->border_width;
+ if (border_width > img->corner_radius) {
+ border_width = 0;
+ }
+ glUniform1f(win_shader->uniform_border_width, (float)border_width);
+ }
+ if (win_shader->uniform_time >= 0) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ glUniform1f(win_shader->uniform_time,
+ (float)ts.tv_sec * 1000.0F + (float)ts.tv_nsec / 1.0e6F);
+ }
+
+ glUniform1i(win_shader->uniform_mask_tex, 2);
+ glUniform2f(win_shader->uniform_mask_offset, (float)mask_offset.x,
+ (float)mask_offset.y);
+ if (mask != NULL) {
+ glUniform1i(win_shader->uniform_mask_inverted, mask->color_inverted);
+ glUniform1f(win_shader->uniform_mask_corner_radius,
+ (GLfloat)mask->corner_radius);
+ } else {
+ glUniform1i(win_shader->uniform_mask_inverted, 0);
+ glUniform1f(win_shader->uniform_mask_corner_radius, 0);
}
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, mask_texture);
+
// log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n",
// x, y, width, height, dx, dy, ptex->width, ptex->height, z);
@@ -437,6 +461,11 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
glDeleteVertexArrays(1, &vao);
// Cleanup
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
@@ -454,19 +483,18 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
/// Convert rectangles in X coordinates to OpenGL vertex and texture coordinates
/// @param[in] nrects, rects rectangles
-/// @param[in] dst_x, dst_y origin of the OpenGL texture, affect the calculated texture
+/// @param[in] image_dst origin of the OpenGL texture, affect the calculated texture
/// coordinates
+/// @param[in] extend_height height of the drawing extent
/// @param[in] texture_height height of the OpenGL texture
/// @param[in] root_height height of the back buffer
/// @param[in] y_inverted whether the texture is y inverted
/// @param[out] coord, indices output
-static void
-x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int texture_height,
- int root_height, bool y_inverted, GLint *coord, GLuint *indices) {
- dst_y = root_height - dst_y;
- if (y_inverted) {
- dst_y -= texture_height;
- }
+void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
+ int extent_height, int texture_height, int root_height,
+ bool y_inverted, GLint *coord, GLuint *indices) {
+ image_dst.y = root_height - image_dst.y;
+ image_dst.y -= extent_height;
for (int i = 0; i < nrects; i++) {
// Y-flip. Note after this, crect.y1 > crect.y2
@@ -476,7 +504,8 @@ x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int text
// Calculate texture coordinates
// (texture_x1, texture_y1), texture coord for the _bottom left_ corner
- GLint texture_x1 = crect.x1 - dst_x, texture_y1 = crect.y2 - dst_y,
+ GLint texture_x1 = crect.x1 - image_dst.x,
+ texture_y1 = crect.y2 - image_dst.y,
texture_x2 = texture_x1 + (crect.x2 - crect.x1),
texture_y2 = texture_y1 + (crect.y1 - crect.y2);
@@ -516,9 +545,9 @@ x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int text
}
// TODO(yshui) make use of reg_visible
-void gl_compose(backend_t *base, void *image_data,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
- const region_t *reg_tgt, const region_t *reg_visible attr_unused) {
+void gl_compose(backend_t *base, void *image_data, coord_t image_dst, void *mask,
+ coord_t mask_dst, const region_t *reg_tgt,
+ const region_t *reg_visible attr_unused) {
auto gd = (struct gl_data *)base;
struct backend_image *img = image_data;
auto inner = (struct gl_texture *)img->inner;
@@ -541,373 +570,43 @@ void gl_compose(backend_t *base, void *image_data,
auto coord = ccalloc(nrects * 16, GLint);
auto indices = ccalloc(nrects * 6, GLuint);
- x_rect_to_coords(nrects, rects, dst_x1, dst_y1, inner->height, gd->height,
- inner->y_inverted, coord, indices);
-
- // Interpolate the texture coordinates into the specified range
- for (unsigned int i = 2; i < 16; i+=4) {
- coord[i+0] = lerp_range(0, dst_x2 - dst_x1, 0, inner->width, coord[i+0]);
- coord[i+1] = lerp_range(0, dst_y2 - dst_y1, 0, inner->height, coord[i+1]);
- }
-
- _gl_compose(base, img, gd->back_fbo, coord, indices, nrects);
+ coord_t mask_offset = {.x = mask_dst.x - image_dst.x, .y = mask_dst.y - image_dst.y};
+ x_rect_to_coords(nrects, rects, image_dst, inner->height, inner->height,
+ gd->height, inner->y_inverted, coord, indices);
+ _gl_compose(base, img, gd->back_fbo, mask, mask_offset, coord, indices, nrects);
free(indices);
free(coord);
}
/**
- * Blur contents in a particular region.
- */
-bool gl_kernel_blur(backend_t *base, double opacity, void *ctx, const rect_t *extent,
- const GLuint vao[2], const int vao_nelems[2]) {
- auto bctx = (struct gl_blur_context *)ctx;
- auto gd = (struct gl_data *)base;
-
- int dst_y_fb_coord = bctx->fb_height - extent->y2;
-
- int curr = 0;
- for (int i = 0; i < bctx->npasses; ++i) {
- const gl_blur_shader_t *p = &bctx->blur_shader[i];
- assert(p->prog);
-
- assert(bctx->blur_textures[curr]);
-
- // The origin to use when sampling from the source texture
- GLint texorig_x = extent->x1, texorig_y = dst_y_fb_coord;
- GLint tex_width, tex_height;
- GLuint src_texture;
-
- if (i == 0) {
- src_texture = gd->back_texture;
- tex_width = gd->width;
- tex_height = gd->height;
- } else {
- src_texture = bctx->blur_textures[curr];
- auto src_size = bctx->texture_sizes[curr];
- tex_width = src_size.width;
- tex_height = src_size.height;
- }
-
- glBindTexture(GL_TEXTURE_2D, src_texture);
- glUseProgram(p->prog);
- glUniform2f(p->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
- 1.0f / (GLfloat)tex_height);
-
- // The number of indices in the selected vertex array
- GLsizei nelems;
-
- if (i < bctx->npasses - 1) {
- assert(bctx->blur_fbos[0]);
- assert(bctx->blur_textures[!curr]);
-
- // not last pass, draw into framebuffer, with resized regions
- glBindVertexArray(vao[1]);
- nelems = vao_nelems[1];
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[0]);
-
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, bctx->blur_textures[!curr], 0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
- return false;
- }
-
- glUniform1f(p->unifm_opacity, 1.0);
- } else {
- // last pass, draw directly into the back buffer, with origin
- // regions
- glBindVertexArray(vao[0]);
- nelems = vao_nelems[0];
- glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
-
- glUniform1f(p->unifm_opacity, (float)opacity);
- }
-
- glUniform2f(p->texorig_loc, (GLfloat)texorig_x, (GLfloat)texorig_y);
- glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
-
- // XXX use multiple draw calls is probably going to be slow than
- // just simply blur the whole area.
-
- curr = !curr;
- }
-
- return true;
-}
-
-bool gl_dual_kawase_blur(backend_t *base, double opacity, void *ctx, const rect_t *extent,
- const GLuint vao[2], const int vao_nelems[2]) {
- auto bctx = (struct gl_blur_context *)ctx;
- auto gd = (struct gl_data *)base;
-
- int dst_y_fb_coord = bctx->fb_height - extent->y2;
-
- int iterations = bctx->blur_texture_count;
- int scale_factor = 1;
-
- // Kawase downsample pass
- const gl_blur_shader_t *down_pass = &bctx->blur_shader[0];
- assert(down_pass->prog);
- glUseProgram(down_pass->prog);
-
- glUniform2f(down_pass->texorig_loc, (GLfloat)extent->x1, (GLfloat)dst_y_fb_coord);
-
- for (int i = 0; i < iterations; ++i) {
- // Scale output width / height by half in each iteration
- scale_factor <<= 1;
-
- GLuint src_texture;
- int tex_width, tex_height;
-
- if (i == 0) {
- // first pass: copy from back buffer
- src_texture = gd->back_texture;
- tex_width = gd->width;
- tex_height = gd->height;
- } else {
- // copy from previous pass
- src_texture = bctx->blur_textures[i - 1];
- auto src_size = bctx->texture_sizes[i - 1];
- tex_width = src_size.width;
- tex_height = src_size.height;
- }
-
- assert(src_texture);
- assert(bctx->blur_fbos[i]);
-
- glBindTexture(GL_TEXTURE_2D, src_texture);
- glBindVertexArray(vao[1]);
- auto nelems = vao_nelems[1];
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i]);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
-
- glUniform1f(down_pass->scale_loc, (GLfloat)scale_factor);
-
- glUniform2f(down_pass->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
- 1.0f / (GLfloat)tex_height);
-
- glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
- }
-
- // Kawase upsample pass
- const gl_blur_shader_t *up_pass = &bctx->blur_shader[1];
- assert(up_pass->prog);
- glUseProgram(up_pass->prog);
-
- glUniform2f(up_pass->texorig_loc, (GLfloat)extent->x1, (GLfloat)dst_y_fb_coord);
-
- for (int i = iterations - 1; i >= 0; --i) {
- // Scale output width / height back by two in each iteration
- scale_factor >>= 1;
-
- const GLuint src_texture = bctx->blur_textures[i];
- assert(src_texture);
-
- // Calculate normalized half-width/-height of a src pixel
- auto src_size = bctx->texture_sizes[i];
- int tex_width = src_size.width;
- int tex_height = src_size.height;
-
- // The number of indices in the selected vertex array
- GLsizei nelems;
-
- glBindTexture(GL_TEXTURE_2D, src_texture);
- if (i > 0) {
- assert(bctx->blur_fbos[i - 1]);
-
- // not last pass, draw into next framebuffer
- glBindVertexArray(vao[1]);
- nelems = vao_nelems[1];
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i - 1]);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
-
- glUniform1f(up_pass->unifm_opacity, (GLfloat)1);
- } else {
- // last pass, draw directly into the back buffer
- glBindVertexArray(vao[0]);
- nelems = vao_nelems[0];
- glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
-
- glUniform1f(up_pass->unifm_opacity, (GLfloat)opacity);
- }
-
- glUniform1f(up_pass->scale_loc, (GLfloat)scale_factor);
- glUniform2f(up_pass->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
- 1.0f / (GLfloat)tex_height);
-
- glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
- }
-
- return true;
-}
-
-bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blur,
- const region_t *reg_visible attr_unused) {
- auto bctx = (struct gl_blur_context *)ctx;
- auto gd = (struct gl_data *)base;
-
- bool ret = false;
-
- if (gd->width != bctx->fb_width || gd->height != bctx->fb_height) {
- // Resize the temporary textures used for blur in case the root
- // size changed
- bctx->fb_width = gd->width;
- bctx->fb_height = gd->height;
-
- for (int i = 0; i < bctx->blur_texture_count; ++i) {
- auto tex_size = bctx->texture_sizes + i;
- if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
- // Use smaller textures for each iteration (quarter of the
- // previous texture)
- tex_size->width = 1 + ((bctx->fb_width - 1) >> (i + 1));
- tex_size->height = 1 + ((bctx->fb_height - 1) >> (i + 1));
- } else {
- tex_size->width = bctx->fb_width;
- tex_size->height = bctx->fb_height;
- }
-
- glBindTexture(GL_TEXTURE_2D, bctx->blur_textures[i]);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_size->width,
- tex_size->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
-
- if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
- // Attach texture to FBO target
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i]);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
- GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- bctx->blur_textures[i], 0);
- if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- return false;
- }
- }
- }
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- }
-
- // Remainder: regions are in Xorg coordinates
- auto reg_blur_resized =
- resize_region(reg_blur, bctx->resize_width, bctx->resize_height);
- const rect_t *extent = pixman_region32_extents((region_t *)reg_blur),
- *extent_resized = pixman_region32_extents(&reg_blur_resized);
- int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1;
- if (width == 0 || height == 0) {
- return true;
- }
-
- int nrects, nrects_resized;
- const rect_t *rects = pixman_region32_rectangles((region_t *)reg_blur, &nrects),
- *rects_resized =
- pixman_region32_rectangles(&reg_blur_resized, &nrects_resized);
- if (!nrects || !nrects_resized) {
- return true;
- }
-
- auto coord = ccalloc(nrects * 16, GLint);
- auto indices = ccalloc(nrects * 6, GLuint);
- x_rect_to_coords(nrects, rects, extent_resized->x1, extent_resized->y2,
- bctx->fb_height, gd->height, false, coord, indices);
-
- auto coord_resized = ccalloc(nrects_resized * 16, GLint);
- auto indices_resized = ccalloc(nrects_resized * 6, GLuint);
- x_rect_to_coords(nrects_resized, rects_resized, extent_resized->x1,
- extent_resized->y2, bctx->fb_height, bctx->fb_height, false,
- coord_resized, indices_resized);
- pixman_region32_fini(&reg_blur_resized);
-
- GLuint vao[2];
- glGenVertexArrays(2, vao);
- GLuint bo[4];
- glGenBuffers(4, bo);
-
- glBindVertexArray(vao[0]);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STATIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * nrects * 6,
- indices, GL_STATIC_DRAW);
- glEnableVertexAttribArray(vert_coord_loc);
- glEnableVertexAttribArray(vert_in_texcoord_loc);
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
- glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
- sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
-
- glBindVertexArray(vao[1]);
- glBindBuffer(GL_ARRAY_BUFFER, bo[2]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[3]);
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord_resized) * nrects_resized * 16,
- coord_resized, GL_STATIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,
- (long)sizeof(*indices_resized) * nrects_resized * 6, indices_resized,
- GL_STATIC_DRAW);
- glEnableVertexAttribArray(vert_coord_loc);
- glEnableVertexAttribArray(vert_in_texcoord_loc);
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
- glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
- sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
-
- int vao_nelems[2] = {nrects * 6, nrects_resized * 6};
-
- if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
- ret = gl_dual_kawase_blur(base, opacity, ctx, extent_resized, vao, vao_nelems);
- } else {
- ret = gl_kernel_blur(base, opacity, ctx, extent_resized, vao, vao_nelems);
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glBindTexture(GL_TEXTURE_2D, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDeleteBuffers(4, bo);
- glBindVertexArray(0);
- glDeleteVertexArrays(2, vao);
- glUseProgram(0);
-
- free(indices);
- free(coord);
- free(indices_resized);
- free(coord_resized);
-
- gl_check_err();
- return ret;
-}
-
-// clang-format off
-const char *vertex_shader = GLSL(330,
- uniform mat4 projection;
- uniform float scale = 1.0;
- uniform vec2 texorig;
- layout(location = 0) in vec2 coord;
- layout(location = 1) in vec2 in_texcoord;
- out vec2 texcoord;
- void main() {
- gl_Position = projection * vec4(coord, 0, scale);
- texcoord = in_texcoord + texorig;
- }
-);
-// clang-format on
-
-/**
* Load a GLSL main program from shader strings.
*/
-static int gl_win_shader_from_string(const char *vshader_str, const char *fshader_str,
- gl_win_shader_t *ret) {
+static bool gl_win_shader_from_stringv(const char **vshader_strv,
+ const char **fshader_strv, gl_win_shader_t *ret) {
// Build program
- ret->prog = gl_create_program_from_str(vshader_str, fshader_str);
+ ret->prog = gl_create_program_from_strv(vshader_strv, fshader_strv);
if (!ret->prog) {
log_error("Failed to create GLSL program.");
- return -1;
+ gl_check_err();
+ return false;
}
// Get uniform addresses
- ret->unifm_opacity = glGetUniformLocationChecked(ret->prog, "opacity");
- ret->unifm_invert_color = glGetUniformLocationChecked(ret->prog, "invert_color");
- ret->unifm_tex = glGetUniformLocationChecked(ret->prog, "tex");
- ret->unifm_dim = glGetUniformLocationChecked(ret->prog, "dim");
- ret->unifm_brightness = glGetUniformLocationChecked(ret->prog, "brightness");
- ret->unifm_max_brightness =
- glGetUniformLocationChecked(ret->prog, "max_brightness");
+ bind_uniform(ret, opacity);
+ bind_uniform(ret, invert_color);
+ bind_uniform(ret, tex);
+ bind_uniform(ret, dim);
+ bind_uniform(ret, brightness);
+ bind_uniform(ret, max_brightness);
+ bind_uniform(ret, corner_radius);
+ bind_uniform(ret, border_width);
+ bind_uniform(ret, time);
+
+ bind_uniform(ret, mask_tex);
+ bind_uniform(ret, mask_offset);
+ bind_uniform(ret, mask_inverted);
+ bind_uniform(ret, mask_corner_radius);
gl_check_err();
@@ -934,51 +633,6 @@ void gl_resize(struct gl_data *gd, int width, int height) {
gl_check_err();
}
-// clang-format off
-static const char dummy_frag[] = GLSL(330,
- uniform sampler2D tex;
- in vec2 texcoord;
- void main() {
- gl_FragColor = texelFetch(tex, ivec2(texcoord.xy), 0);
- }
-);
-
-static const char fill_frag[] = GLSL(330,
- uniform vec4 color;
- void main() {
- gl_FragColor = color;
- }
-);
-
-static const char fill_vert[] = GLSL(330,
- layout(location = 0) in vec2 in_coord;
- uniform mat4 projection;
- void main() {
- gl_Position = projection * vec4(in_coord, 0, 1);
- }
-);
-
-static const char interpolating_frag[] = GLSL(330,
- uniform sampler2D tex;
- in vec2 texcoord;
- void main() {
- gl_FragColor = vec4(texture2D(tex, vec2(texcoord.xy), 0).rgb, 1);
- }
-);
-
-static const char interpolating_vert[] = GLSL(330,
- uniform mat4 projection;
- uniform vec2 texsize;
- layout(location = 0) in vec2 in_coord;
- layout(location = 1) in vec2 in_texcoord;
- out vec2 texcoord;
- void main() {
- gl_Position = projection * vec4(in_coord, 0, 1);
- texcoord = in_texcoord / texsize;
- }
-);
-// clang-format on
-
/// Fill a given region in bound framebuffer.
/// @param[in] y_inverted whether the y coordinates in `clip` should be inverted
static void _gl_fill(backend_t *base, struct color c, const region_t *clip, GLuint target,
@@ -1048,9 +702,45 @@ void gl_fill(backend_t *base, struct color c, const region_t *clip) {
return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
}
+void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
+ auto tex = ccalloc(1, struct gl_texture);
+ auto img = default_new_backend_image(size.width, size.height);
+ tex->width = size.width;
+ tex->height = size.height;
+ tex->texture = gl_new_texture(GL_TEXTURE_2D);
+ tex->has_alpha = false;
+ tex->y_inverted = true;
+ img->inner = (struct backend_image_inner_base *)tex;
+ img->inner->refcount = 1;
+
+ glBindTexture(GL_TEXTURE_2D, tex->texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size.width, size.height, 0, GL_RED,
+ GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ GLuint fbo;
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ tex->texture, 0);
+ glDrawBuffer(GL_COLOR_ATTACHMENT0);
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ _gl_fill(base, (struct color){1, 1, 1, 1}, reg, fbo, size.height, false);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glDeleteFramebuffers(1, &fbo);
+ return img;
+}
+
static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
auto gd = (struct gl_data *)base;
- gd->release_user_data(base, inner);
+ if (inner->user_data) {
+ gd->release_user_data(base, inner);
+ }
assert(inner->user_data == NULL);
glDeleteTextures(1, &inner->texture);
@@ -1070,513 +760,44 @@ void gl_release_image(backend_t *base, void *image_data) {
free(wd);
}
-static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
- if (shader->prog) {
- glDeleteProgram(shader->prog);
- }
+void *gl_create_window_shader(backend_t *backend_data attr_unused, const char *source) {
+ auto win_shader = (gl_win_shader_t *)ccalloc(1, gl_win_shader_t);
- shader->prog = 0;
-}
+ const char *vert_shaders[2] = {vertex_shader, NULL};
+ const char *frag_shaders[4] = {win_shader_glsl, masking_glsl, source, NULL};
-void gl_destroy_blur_context(backend_t *base attr_unused, void *ctx) {
- auto bctx = (struct gl_blur_context *)ctx;
- // Free GLSL shaders/programs
- for (int i = 0; i < bctx->npasses; ++i) {
- gl_free_blur_shader(&bctx->blur_shader[i]);
+ if (!gl_win_shader_from_stringv(vert_shaders, frag_shaders, win_shader)) {
+ free(win_shader);
+ return NULL;
}
- free(bctx->blur_shader);
- if (bctx->blur_texture_count && bctx->blur_textures) {
- glDeleteTextures(bctx->blur_texture_count, bctx->blur_textures);
- free(bctx->blur_textures);
- }
- if (bctx->blur_texture_count && bctx->texture_sizes) {
- free(bctx->texture_sizes);
- }
- if (bctx->blur_fbo_count && bctx->blur_fbos) {
- glDeleteFramebuffers(bctx->blur_fbo_count, bctx->blur_fbos);
- free(bctx->blur_fbos);
- }
-
- bctx->blur_texture_count = 0;
- bctx->blur_fbo_count = 0;
-
- free(bctx);
-
- gl_check_err();
-}
-
-/**
- * Initialize GL blur filters.
- */
-bool gl_create_kernel_blur_context(void *blur_context, GLfloat *projection,
- enum blur_method method, void *args) {
- bool success = false;
- auto ctx = (struct gl_blur_context *)blur_context;
-
- struct conv **kernels;
-
- int nkernels;
- ctx->method = BLUR_METHOD_KERNEL;
- if (method == BLUR_METHOD_KERNEL) {
- nkernels = ((struct kernel_blur_args *)args)->kernel_count;
- kernels = ((struct kernel_blur_args *)args)->kernels;
- } else {
- kernels = generate_blur_kernel(method, args, &nkernels);
- }
-
- if (!nkernels) {
- ctx->method = BLUR_METHOD_NONE;
- return true;
- }
-
- // Specify required textures and FBOs
- ctx->blur_texture_count = 2;
- ctx->blur_fbo_count = 1;
-
- ctx->blur_shader = ccalloc(max2(2, nkernels), gl_blur_shader_t);
-
- 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");
-
- // clang-format off
- static const char *FRAG_SHADER_BLUR = GLSL(330,
- %s\n // other extension pragmas
- uniform sampler2D tex_src;
- uniform vec2 pixel_norm;
- uniform float opacity;
- in vec2 texcoord;
- out vec4 out_color;
- void main() {
- vec2 uv = texcoord * pixel_norm;
- vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
- %s //body of the convolution
- out_color = sum / float(%.7g) * opacity;
- }
- );
- static const char *FRAG_SHADER_BLUR_ADD = QUOTE(
- sum += float(%.7g) * texture2D(tex_src, uv + pixel_norm * vec2(%.7g, %.7g));
- );
- // clang-format on
-
- const char *shader_add = FRAG_SHADER_BLUR_ADD;
- char *extension = strdup("");
-
- for (int i = 0; i < nkernels; i++) {
- auto kern = kernels[i];
- // Build shader
- int width = kern->w, height = kern->h;
- int nele = width * height;
- // '%.7g' is at most 14 characters, inserted 3 times
- size_t body_len = (strlen(shader_add) + 42) * (uint)nele;
- char *shader_body = ccalloc(body_len, char);
- char *pc = shader_body;
-
- // Make use of the linear interpolation hardware by sampling 2 pixels with
- // one texture access by sampling between both pixels based on their
- // relative weight. Easiest done in a single dimension as 2D bilinear
- // filtering would raise additional constraints on the kernels. Therefore
- // only use interpolation along the larger dimension.
- double sum = 0.0;
- if (width > height) {
- // use interpolation in x dimension (width)
- for (int j = 0; j < height; ++j) {
- for (int k = 0; k < width; k += 2) {
- double val1, val2;
- val1 = kern->data[j * width + k];
- val2 = (k + 1 < width)
- ? kern->data[j * width + k + 1]
- : 0;
-
- double combined_weight = val1 + val2;
- if (combined_weight == 0) {
- continue;
- }
- sum += combined_weight;
-
- double offset_x =
- k + (val2 / combined_weight) - (width / 2);
- double offset_y = j - (height / 2);
- pc += snprintf(
- pc, body_len - (ulong)(pc - shader_body),
- shader_add, combined_weight, offset_x, offset_y);
- assert(pc < shader_body + body_len);
- }
- }
- } else {
- // use interpolation in y dimension (height)
- for (int j = 0; j < height; j += 2) {
- for (int k = 0; k < width; ++k) {
- double val1, val2;
- val1 = kern->data[j * width + k];
- val2 = (j + 1 < height)
- ? kern->data[(j + 1) * width + k]
- : 0;
-
- double combined_weight = val1 + val2;
- if (combined_weight == 0) {
- continue;
- }
- sum += combined_weight;
-
- double offset_x = k - (width / 2);
- double offset_y =
- j + (val2 / combined_weight) - (height / 2);
- pc += snprintf(
- pc, body_len - (ulong)(pc - shader_body),
- shader_add, combined_weight, offset_x, offset_y);
- assert(pc < shader_body + body_len);
- }
- }
- }
-
- auto pass = ctx->blur_shader + i;
- size_t shader_len = strlen(FRAG_SHADER_BLUR) + strlen(extension) +
- strlen(shader_body) + 10 /* sum */ +
- 1 /* null terminator */;
- char *shader_str = ccalloc(shader_len, char);
- auto real_shader_len = snprintf(shader_str, shader_len, FRAG_SHADER_BLUR,
- extension, shader_body, sum);
- CHECK(real_shader_len >= 0);
- CHECK((size_t)real_shader_len < shader_len);
- free(shader_body);
-
- // Build program
- pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
- free(shader_str);
- if (!pass->prog) {
- log_error("Failed to create GLSL program.");
- success = false;
- goto out;
- }
- glBindFragDataLocation(pass->prog, 0, "out_color");
-
- // Get uniform addresses
- pass->unifm_pixel_norm =
- glGetUniformLocationChecked(pass->prog, "pixel_norm");
- pass->unifm_opacity = glGetUniformLocationChecked(pass->prog, "opacity");
- pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig");
-
- // Setup projection matrix
- glUseProgram(pass->prog);
- int pml = glGetUniformLocationChecked(pass->prog, "projection");
- glUniformMatrix4fv(pml, 1, false, projection);
- glUseProgram(0);
-
- ctx->resize_width += kern->w / 2;
- ctx->resize_height += kern->h / 2;
- }
-
- if (nkernels == 1) {
- // Generate an extra null pass so we don't need special code path for
- // the single pass case
- auto pass = &ctx->blur_shader[1];
- pass->prog = gl_create_program_from_str(vertex_shader, dummy_frag);
- pass->unifm_pixel_norm = -1;
- pass->unifm_opacity = -1;
- pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig");
-
- // Setup projection matrix
- glUseProgram(pass->prog);
- int pml = glGetUniformLocationChecked(pass->prog, "projection");
- glUniformMatrix4fv(pml, 1, false, projection);
- glUseProgram(0);
-
- ctx->npasses = 2;
- } else {
- ctx->npasses = nkernels;
- }
-
- success = true;
-out:
- if (method != BLUR_METHOD_KERNEL) {
- // We generated the blur kernels, so we need to free them
- for (int i = 0; i < nkernels; i++) {
- free(kernels[i]);
- }
- free(kernels);
- }
-
- free(extension);
- // Restore LC_NUMERIC
- setlocale(LC_NUMERIC, lc_numeric_old);
- free(lc_numeric_old);
-
- return success;
-}
-
-bool gl_create_dual_kawase_blur_context(void *blur_context, GLfloat *projection,
- enum blur_method method, void *args) {
- bool success = false;
- auto ctx = (struct gl_blur_context *)blur_context;
-
- ctx->method = method;
-
- auto blur_params = generate_dual_kawase_params(args);
-
- // Specify required textures and FBOs
- ctx->blur_texture_count = blur_params->iterations;
- ctx->blur_fbo_count = blur_params->iterations;
-
- ctx->resize_width += blur_params->expand;
- ctx->resize_height += blur_params->expand;
-
- ctx->npasses = 2;
- ctx->blur_shader = ccalloc(ctx->npasses, gl_blur_shader_t);
-
- 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");
-
- // Dual-kawase downsample shader / program
- auto down_pass = ctx->blur_shader;
- {
- // clang-format off
- static const char *FRAG_SHADER_DOWN = GLSL(330,
- uniform sampler2D tex_src;
- uniform float scale = 1.0;
- uniform vec2 pixel_norm;
- in vec2 texcoord;
- out vec4 out_color;
- void main() {
- vec2 offset = %.7g * pixel_norm;
- vec2 uv = texcoord * pixel_norm * (2.0 / scale);
- vec4 sum = texture2D(tex_src, uv) * 4.0;
- sum += texture2D(tex_src, uv - vec2(0.5, 0.5) * offset);
- sum += texture2D(tex_src, uv + vec2(0.5, 0.5) * offset);
- sum += texture2D(tex_src, uv + vec2(0.5, -0.5) * offset);
- sum += texture2D(tex_src, uv - vec2(0.5, -0.5) * offset);
- out_color = sum / 8.0;
- }
- );
- // clang-format on
-
- // Build shader
- size_t shader_len =
- strlen(FRAG_SHADER_DOWN) + 10 /* offset */ + 1 /* null terminator */;
- char *shader_str = ccalloc(shader_len, char);
- auto real_shader_len =
- snprintf(shader_str, shader_len, FRAG_SHADER_DOWN, blur_params->offset);
- CHECK(real_shader_len >= 0);
- CHECK((size_t)real_shader_len < shader_len);
-
- // Build program
- down_pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
- free(shader_str);
- if (!down_pass->prog) {
- log_error("Failed to create GLSL program.");
- success = false;
- goto out;
- }
- glBindFragDataLocation(down_pass->prog, 0, "out_color");
-
- // Get uniform addresses
- down_pass->unifm_pixel_norm =
- glGetUniformLocationChecked(down_pass->prog, "pixel_norm");
- down_pass->texorig_loc =
- glGetUniformLocationChecked(down_pass->prog, "texorig");
- down_pass->scale_loc =
- glGetUniformLocationChecked(down_pass->prog, "scale");
-
- // Setup projection matrix
- glUseProgram(down_pass->prog);
- int pml = glGetUniformLocationChecked(down_pass->prog, "projection");
- glUniformMatrix4fv(pml, 1, false, projection);
- glUseProgram(0);
- }
-
- // Dual-kawase upsample shader / program
- auto up_pass = ctx->blur_shader + 1;
- {
- // clang-format off
- static const char *FRAG_SHADER_UP = GLSL(330,
- uniform sampler2D tex_src;
- uniform float scale = 1.0;
- uniform vec2 pixel_norm;
- uniform float opacity;
- in vec2 texcoord;
- out vec4 out_color;
- void main() {
- vec2 offset = %.7g * pixel_norm;
- vec2 uv = texcoord * pixel_norm / (2 * scale);
- vec4 sum = texture2D(tex_src, uv + vec2(-1.0, 0.0) * offset);
- sum += texture2D(tex_src, uv + vec2(-0.5, 0.5) * offset) * 2.0;
- sum += texture2D(tex_src, uv + vec2(0.0, 1.0) * offset);
- sum += texture2D(tex_src, uv + vec2(0.5, 0.5) * offset) * 2.0;
- sum += texture2D(tex_src, uv + vec2(1.0, 0.0) * offset);
- sum += texture2D(tex_src, uv + vec2(0.5, -0.5) * offset) * 2.0;
- sum += texture2D(tex_src, uv + vec2(0.0, -1.0) * offset);
- sum += texture2D(tex_src, uv + vec2(-0.5, -0.5) * offset) * 2.0;
- out_color = sum / 12.0 * opacity;
- }
- );
- // clang-format on
-
- // Build shader
- size_t shader_len =
- strlen(FRAG_SHADER_UP) + 10 /* offset */ + 1 /* null terminator */;
- char *shader_str = ccalloc(shader_len, char);
- auto real_shader_len =
- snprintf(shader_str, shader_len, FRAG_SHADER_UP, blur_params->offset);
- CHECK(real_shader_len >= 0);
- CHECK((size_t)real_shader_len < shader_len);
-
- // Build program
- up_pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
- free(shader_str);
- if (!up_pass->prog) {
- log_error("Failed to create GLSL program.");
- success = false;
- goto out;
- }
- glBindFragDataLocation(up_pass->prog, 0, "out_color");
-
- // Get uniform addresses
- up_pass->unifm_pixel_norm =
- glGetUniformLocationChecked(up_pass->prog, "pixel_norm");
- up_pass->unifm_opacity =
- glGetUniformLocationChecked(up_pass->prog, "opacity");
- up_pass->texorig_loc =
- glGetUniformLocationChecked(up_pass->prog, "texorig");
- up_pass->scale_loc = glGetUniformLocationChecked(up_pass->prog, "scale");
-
- // Setup projection matrix
- glUseProgram(up_pass->prog);
- int pml = glGetUniformLocationChecked(up_pass->prog, "projection");
- glUniformMatrix4fv(pml, 1, false, projection);
- glUseProgram(0);
- }
-
- success = true;
-out:
- free(blur_params);
-
- if (!success) {
- ctx = NULL;
- }
-
- // Restore LC_NUMERIC
- setlocale(LC_NUMERIC, lc_numeric_old);
- free(lc_numeric_old);
-
- return success;
-}
-
-void *gl_create_blur_context(backend_t *base, enum blur_method method, void *args) {
- bool success;
- auto gd = (struct gl_data *)base;
-
- auto ctx = ccalloc(1, struct gl_blur_context);
-
- if (!method || method >= BLUR_METHOD_INVALID) {
- ctx->method = BLUR_METHOD_NONE;
- return ctx;
- }
+ GLint viewport_dimensions[2];
+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
// Set projection matrix to gl viewport dimensions so we can use screen
// coordinates for all vertices
// Note: OpenGL matrices are column major
- GLint viewport_dimensions[2];
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
- GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)viewport_dimensions[0], 0, 0, 0},
- {0, 2.0f / (GLfloat)viewport_dimensions[1], 0, 0},
+ GLfloat projection_matrix[4][4] = {{2.0F / (GLfloat)viewport_dimensions[0], 0, 0, 0},
+ {0, 2.0F / (GLfloat)viewport_dimensions[1], 0, 0},
{0, 0, 0, 0},
{-1, -1, 0, 1}};
- if (method == BLUR_METHOD_DUAL_KAWASE) {
- success = gl_create_dual_kawase_blur_context(ctx, projection_matrix[0],
- method, args);
- } else {
- success =
- gl_create_kernel_blur_context(ctx, projection_matrix[0], method, args);
- }
- if (!success || ctx->method == BLUR_METHOD_NONE) {
- goto out;
- }
-
- // Texture size will be defined by gl_blur
- ctx->blur_textures = ccalloc(ctx->blur_texture_count, GLuint);
- ctx->texture_sizes = ccalloc(ctx->blur_texture_count, struct texture_size);
- glGenTextures(ctx->blur_texture_count, ctx->blur_textures);
-
- for (int i = 0; i < ctx->blur_texture_count; ++i) {
- glBindTexture(GL_TEXTURE_2D, ctx->blur_textures[i]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
-
- // Generate FBO and textures when needed
- ctx->blur_fbos = ccalloc(ctx->blur_fbo_count, GLuint);
- glGenFramebuffers(ctx->blur_fbo_count, ctx->blur_fbos);
-
- for (int i = 0; i < ctx->blur_fbo_count; ++i) {
- if (!ctx->blur_fbos[i]) {
- log_error("Failed to generate framebuffer objects for blur");
- success = false;
- goto out;
- }
- }
-
-out:
- if (!success) {
- gl_destroy_blur_context(&gd->base, ctx);
- ctx = NULL;
- }
-
- gl_check_err();
- return ctx;
-}
+ int pml = glGetUniformLocationChecked(win_shader->prog, "projection");
+ glUseProgram(win_shader->prog);
+ glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
+ glUseProgram(0);
-void gl_get_blur_size(void *blur_context, int *width, int *height) {
- auto ctx = (struct gl_blur_context *)blur_context;
- *width = ctx->resize_width;
- *height = ctx->resize_height;
+ return win_shader;
}
-// clang-format off
-const char *win_shader_glsl = GLSL(330,
- uniform float opacity;
- uniform float dim;
- uniform bool invert_color;
- in vec2 texcoord;
- uniform sampler2D tex;
- uniform sampler2D brightness;
- uniform float max_brightness;
-
- void main() {
- vec4 c = texelFetch(tex, ivec2(texcoord), 0);
- if (invert_color) {
- c = vec4(c.aaa - c.rgb, c.a);
- }
- c = vec4(c.rgb * (1.0 - dim), c.a) * opacity;
-
- vec3 rgb_brightness = texelFetch(brightness, ivec2(0, 0), 0).rgb;
- // Ref: https://en.wikipedia.org/wiki/Relative_luminance
- float brightness = rgb_brightness.r * 0.21 +
- rgb_brightness.g * 0.72 +
- rgb_brightness.b * 0.07;
- if (brightness > max_brightness)
- c.rgb = c.rgb * (max_brightness / brightness);
-
- gl_FragColor = c;
+uint64_t gl_get_shader_attributes(backend_t *backend_data attr_unused, void *shader) {
+ auto win_shader = (gl_win_shader_t *)shader;
+ uint64_t ret = 0;
+ if (glGetUniformLocation(win_shader->prog, "time") >= 0) {
+ ret |= SHADER_ATTRIBUTE_ANIMATED;
}
-);
-
-const char *present_vertex_shader = GLSL(330,
- uniform mat4 projection;
- layout(location = 0) in vec2 coord;
- out vec2 texcoord;
- void main() {
- gl_Position = projection * vec4(coord, 0, 1);
- texcoord = coord;
- }
-);
-// clang-format on
+ return ret;
+}
bool gl_init(struct gl_data *gd, session_t *ps) {
// Initialize GLX data structure
@@ -1602,7 +823,7 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glViewport(0, 0, viewport_dimensions[0], viewport_dimensions[1]);
// Clear screen
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glGenFramebuffers(1, &gd->back_fbo);
@@ -1619,24 +840,35 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
+ gd->default_mask_texture = gl_new_texture(GL_TEXTURE_2D);
+ if (!gd->default_mask_texture) {
+ log_error("Failed to generate a default mask texture");
+ return false;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, gd->default_mask_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE,
+ (GLbyte[]){'\xff'});
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Initialize shaders
+ gd->default_shader = gl_create_window_shader(NULL, win_shader_default);
+ if (!gd->default_shader) {
+ log_error("Failed to create window shaders");
+ return false;
+ }
+
// Set projection matrix to gl viewport dimensions so we can use screen
// coordinates for all vertices
// Note: OpenGL matrices are column major
- GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)viewport_dimensions[0], 0, 0, 0},
- {0, 2.0f / (GLfloat)viewport_dimensions[1], 0, 0},
+ GLfloat projection_matrix[4][4] = {{2.0F / (GLfloat)viewport_dimensions[0], 0, 0, 0},
+ {0, 2.0F / (GLfloat)viewport_dimensions[1], 0, 0},
{0, 0, 0, 0},
{-1, -1, 0, 1}};
- // Initialize shaders
- gl_win_shader_from_string(vertex_shader, win_shader_glsl, &gd->win_shader);
- int pml = glGetUniformLocationChecked(gd->win_shader.prog, "projection");
- glUseProgram(gd->win_shader.prog);
- glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
- glUseProgram(0);
-
gd->fill_shader.prog = gl_create_program_from_str(fill_vert, fill_frag);
gd->fill_shader.color_loc = glGetUniformLocation(gd->fill_shader.prog, "color");
- pml = glGetUniformLocationChecked(gd->fill_shader.prog, "projection");
+ int pml = glGetUniformLocationChecked(gd->fill_shader.prog, "projection");
glUseProgram(gd->fill_shader.prog);
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
glUseProgram(0);
@@ -1652,6 +884,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) {
@@ -1689,18 +932,24 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
} else {
gd->is_nvidia = false;
}
+ gd->has_robustness = gl_has_extension("GL_ARB_robustness");
+ gd->has_egl_image_storage = gl_has_extension("GL_EXT_EGL_image_storage");
+ gl_check_err();
return true;
}
void gl_deinit(struct gl_data *gd) {
- gl_free_prog_main(&gd->win_shader);
-
if (gd->logger) {
log_remove_target_tls(gd->logger);
gd->logger = NULL;
}
+ if (gd->default_shader) {
+ gl_destroy_window_shader(&gd->base, gd->default_shader);
+ gd->default_shader = NULL;
+ }
+
gl_check_err();
}
@@ -1832,7 +1081,8 @@ static void gl_image_apply_alpha(backend_t *base, struct backend_image *img,
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
inner->texture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
- _gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, fbo, 0, false);
+ _gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, fbo, inner->height,
+ !inner->y_inverted);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
@@ -1904,19 +1154,202 @@ bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
return true;
}
-bool gl_read_pixel(backend_t *base attr_unused, void *image_data, int x, int y,
- struct color *output) {
- struct backend_image *tex = image_data;
- auto inner = (struct gl_texture *)tex->inner;
- GLfloat color[4];
- glReadPixels(x, inner->y_inverted ? inner->height - y : y, 1, 1, GL_RGBA,
- GL_FLOAT, color);
- output->alpha = color[3];
- output->red = color[0];
- output->green = color[1];
- output->blue = color[2];
-
- bool ret = glGetError() == GL_NO_ERROR;
- gl_clear_err();
- return ret;
+bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
+ void *image_data, void *args) {
+ if (prop != IMAGE_PROPERTY_CUSTOM_SHADER) {
+ return default_set_image_property(backend_data, prop, image_data, args);
+ }
+
+ struct backend_image *img = image_data;
+ auto inner = (struct gl_texture *)img->inner;
+ inner->shader = args;
+ 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;
+ ctx->blur_context = NULL;
+
+ if (radius > 0) {
+ struct gaussian_blur_args args = {
+ .size = (int)radius,
+ .deviation = gaussian_kernel_std_for_size(radius, 0.5 / 256.0),
+ };
+ ctx->blur_context = gl_create_blur_context(base, BLUR_METHOD_GAUSSIAN, &args);
+ if (!ctx->blur_context) {
+ log_error("Failed to create shadow context");
+ free(ctx);
+ return NULL;
+ }
+ }
+ 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;
+ if (ctx_->blur_context) {
+ 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 = true;
+ 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);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ 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();
+
+ auto tmp_texture = source_texture;
+ if (gsctx->blur_context != NULL) {
+ glActiveTexture(GL_TEXTURE0);
+ 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(&reg_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}, &reg_blur, NULL,
+ source_texture,
+ (geometry_t){.width = new_inner->width, .height = new_inner->height},
+ fbo, gd->default_mask_texture);
+ pixman_region32_fini(&reg_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});
+ if (tmp_texture != source_texture) {
+ glDeleteTextures(1, (GLuint[]){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) {
+ return DEVICE_STATUS_NORMAL;
+ }
+ if (glGetGraphicsResetStatusARB() == GL_NO_ERROR) {
+ return DEVICE_STATUS_NORMAL;
+ }
+ return DEVICE_STATUS_RESETTING;
}