aboutsummaryrefslogtreecommitdiff
path: root/src/backend/gl
diff options
context:
space:
mode:
authorallusive-dev <[email protected]>2023-09-19 17:46:20 +1000
committerallusive-dev <[email protected]>2023-09-19 17:46:20 +1000
commit5650d887357bf2a3fac8c5fd4f467bf8795b5fc4 (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/backend/gl
parentUpdate picom.sample.conf (diff)
downloadcompfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.tar.xz
compfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.zip
reset
Diffstat (limited to 'src/backend/gl')
-rw-r--r--src/backend/gl/gl_common.c1922
-rw-r--r--src/backend/gl/gl_common.h220
-rw-r--r--src/backend/gl/glx.c648
-rw-r--r--src/backend/gl/glx.h77
4 files changed, 0 insertions, 2867 deletions
diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c
deleted file mode 100644
index 8cc5a05..0000000
--- a/src/backend/gl/gl_common.c
+++ /dev/null
@@ -1,1922 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// 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 <xcb/render.h> // for xcb_render_fixed_t, XXX
-
-#include "backend/backend.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "kernel.h"
-#include "log.h"
-#include "region.h"
-#include "string_utils.h"
-#include "types.h"
-#include "utils.h"
-
-#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);
-
- bool success = false;
- GLuint shader = glCreateShader(shader_type);
- if (!shader) {
- log_error("Failed to create shader with type %#x.", shader_type);
- goto end;
- }
- glShaderSource(shader, 1, &shader_str, NULL);
- glCompileShader(shader);
-
- // Get shader status
- {
- GLint status = GL_FALSE;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
- if (GL_FALSE == status) {
- GLint log_len = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
- if (log_len) {
- char log[log_len + 1];
- glGetShaderInfoLog(shader, log_len, NULL, log);
- log_error("Failed to compile shader with type %d: %s",
- shader_type, log);
- }
- goto end;
- }
- }
-
- success = true;
-
-end:
- if (shader && !success) {
- glDeleteShader(shader);
- shader = 0;
- }
-
- return shader;
-}
-
-GLuint gl_create_program(const GLuint *const shaders, int nshaders) {
- bool success = false;
- GLuint program = glCreateProgram();
- if (!program) {
- log_error("Failed to create program.");
- goto end;
- }
-
- 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) {
- GLint log_len = 0;
- glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len);
- if (log_len) {
- char log[log_len + 1];
- glGetProgramInfoLog(program, log_len, NULL, log);
- log_error("Failed to link program: %s", log);
- }
- goto end;
- }
- }
- success = true;
-
-end:
- if (program) {
- for (int i = 0; i < nshaders; ++i)
- glDetachShader(program, shaders[i]);
- }
- if (program && !success) {
- glDeleteProgram(program);
- program = 0;
- }
-
- return program;
-}
-
-/**
- * @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) {
- 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 shaders[2];
- int count = 0;
- if (vert_shader) {
- shaders[count++] = vert_shader;
- }
- if (frag_shader) {
- shaders[count++] = frag_shader;
- }
- if (count) {
- prog = gl_create_program(shaders, count);
- }
- }
-
- if (vert_shader)
- glDeleteShader(vert_shader);
- if (frag_shader)
- glDeleteShader(frag_shader);
-
- return prog;
-}
-
-static void gl_free_prog_main(gl_win_shader_t *pprogram) {
- if (!pprogram)
- return;
- if (pprogram->prog) {
- glDeleteProgram(pprogram->prog);
- pprogram->prog = 0;
- }
-}
-
-/*
- * @brief Implements recursive part of gl_average_texture_color.
- *
- * @note In order to reduce number of textures which needs to be
- * allocated and deleted during this recursive render
- * we reuse the same two textures for render source and
- * destination simply by alterating between them.
- * Unfortunately on first iteration source_texture might
- * be read-only. In this case we will select auxiliary_texture as
- * destination_texture in order not to touch that read-only source
- * texture in following render iteration.
- * Otherwise we simply will switch source and destination textures
- * between each other on each render iteration.
- */
-static GLuint
-_gl_average_texture_color(backend_t *base, GLuint source_texture, GLuint destination_texture,
- GLuint auxiliary_texture, GLuint fbo, int width, int height) {
- const int max_width = 1;
- const int max_height = 1;
- const int from_width = next_power_of_two(width);
- const int from_height = next_power_of_two(height);
- const int to_width = from_width > max_width ? from_width / 2 : from_width;
- const int to_height = from_height > max_height ? from_height / 2 : from_height;
-
- // Prepare coordinates
- GLint coord[] = {
- // top left
- 0, 0, // vertex coord
- 0, 0, // texture coord
-
- // top right
- to_width, 0, // vertex coord
- width, 0, // texture coord
-
- // bottom right
- to_width, to_height, // vertex coord
- width, height, // texture coord
-
- // bottom left
- 0, to_height, // vertex coord
- 0, height, // texture coord
- };
- glBufferSubData(GL_ARRAY_BUFFER, 0, (long)sizeof(*coord) * 16, coord);
-
- // Prepare framebuffer for new render iteration
- glBindTexture(GL_TEXTURE_2D, destination_texture);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- destination_texture, 0);
- gl_check_fb_complete(GL_FRAMEBUFFER);
-
- // Bind source texture as downscaling shader uniform input
- glBindTexture(GL_TEXTURE_2D, source_texture);
-
- // Render into framebuffer
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
-
- // Have we downscaled enough?
- GLuint result;
- if (to_width > max_width || to_height > max_height) {
- GLuint new_source_texture = destination_texture;
- GLuint new_destination_texture =
- auxiliary_texture != 0 ? auxiliary_texture : source_texture;
- result = _gl_average_texture_color(base, new_source_texture,
- new_destination_texture, 0, fbo,
- to_width, to_height);
- } else {
- result = destination_texture;
- }
-
- return result;
-}
-
-/*
- * @brief Builds a 1x1 texture which has color corresponding to the average of all
- * pixels of img by recursively rendering into texture of quorter the size (half
- * width and half height).
- * Returned texture must not be deleted, since it's owned by the gl_image. It will be
- * deleted when the gl_image is released.
- */
-static GLuint gl_average_texture_color(backend_t *base, struct backend_image *img) {
- auto gd = (struct gl_data *)base;
- auto inner = (struct gl_texture *)img->inner;
-
- // Prepare textures which will be used for destination and source of rendering
- // during downscaling.
- const int texture_count = ARR_SIZE(inner->auxiliary_texture);
- if (!inner->auxiliary_texture[0]) {
- assert(!inner->auxiliary_texture[1]);
- glGenTextures(texture_count, inner->auxiliary_texture);
- glActiveTexture(GL_TEXTURE0);
- for (int i = 0; i < texture_count; i++) {
- glBindTexture(GL_TEXTURE_2D, inner->auxiliary_texture[i]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
- (GLint[]){0, 0, 0, 0});
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, inner->width,
- inner->height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);
- }
- }
-
- // Prepare framebuffer used for rendering and bind it
- GLuint fbo;
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
-
- // Enable shaders
- glUseProgram(gd->brightness_shader.prog);
- glUniform2f(glGetUniformLocationChecked(gd->brightness_shader.prog, "texsize"),
- (GLfloat)inner->width, (GLfloat)inner->height);
-
- // Prepare vertex attributes
- 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]);
- 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));
-
- // Allocate buffers for render input
- GLint coord[16] = {0};
- GLuint indices[] = {0, 1, 2, 2, 3, 0};
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_DYNAMIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
- GL_STATIC_DRAW);
-
- // Do actual recursive render to 1x1 texture
- GLuint result_texture = _gl_average_texture_color(
- base, inner->texture, inner->auxiliary_texture[0],
- inner->auxiliary_texture[1], fbo, inner->width, inner->height);
-
- // Cleanup vertex attributes
- glDisableVertexAttribArray(vert_coord_loc);
- glDisableVertexAttribArray(vert_in_texcoord_loc);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDeleteBuffers(2, bo);
- glBindVertexArray(0);
- glDeleteVertexArrays(1, &vao);
-
- // Cleanup shaders
- glUseProgram(0);
-
- // Cleanup framebuffers
- glDeleteFramebuffers(1, &fbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glDrawBuffer(GL_BACK);
-
- // Cleanup render textures
- glBindTexture(GL_TEXTURE_2D, 0);
-
- gl_check_err();
-
- return result_texture;
-}
-
-/**
- * Render a region with texture data.
- *
- * @param ptex the texture
- * @param target the framebuffer to render into
- * @param dst_x,dst_y the top left corner of region where this texture
- * should go. In OpenGL coordinate system (important!).
- * @param reg_tgt the clip region, in Xorg coordinate system
- * @param reg_visible ignored
- */
-static void _gl_compose(backend_t *base, struct backend_image *img, GLuint target,
- GLint *coord, GLuint *indices, int nrects) {
- auto gd = (struct gl_data *)base;
- auto inner = (struct gl_texture *)img->inner;
- if (!img || !inner->texture) {
- log_error("Missing texture.");
- return;
- }
-
- GLuint brightness = 0;
- if (img->max_brightness < 1.0) {
- 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);
- }
- if (gd->win_shader.unifm_invert_color >= 0) {
- glUniform1i(gd->win_shader.unifm_invert_color, img->color_inverted);
- }
- if (gd->win_shader.unifm_tex >= 0) {
- glUniform1i(gd->win_shader.unifm_tex, 0);
- }
- if (gd->win_shader.unifm_dim >= 0) {
- glUniform1f(gd->win_shader.unifm_dim, (float)img->dim);
- }
- if (gd->win_shader.unifm_brightness >= 0) {
- glUniform1i(gd->win_shader.unifm_brightness, 1);
- }
- if (gd->win_shader.unifm_max_brightness >= 0) {
- glUniform1f(gd->win_shader.unifm_max_brightness, (float)img->max_brightness);
- }
-
- // 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);
-
- // Bind texture
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, brightness);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, inner->texture);
-
- 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) * 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));
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target);
- glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
-
- glDisableVertexAttribArray(vert_coord_loc);
- glDisableVertexAttribArray(vert_in_texcoord_loc);
- glBindVertexArray(0);
- glDeleteVertexArrays(1, &vao);
-
- // Cleanup
- glBindTexture(GL_TEXTURE_2D, 0);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glDrawBuffer(GL_BACK);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDeleteBuffers(2, bo);
-
- glUseProgram(0);
-
- gl_check_err();
-
- return;
-}
-
-/// 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
-/// coordinates
-/// @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;
- }
-
- for (int i = 0; i < nrects; i++) {
- // Y-flip. Note after this, crect.y1 > crect.y2
- rect_t crect = rects[i];
- crect.y1 = root_height - crect.y1;
- crect.y2 = root_height - crect.y2;
-
- // 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,
- texture_x2 = texture_x1 + (crect.x2 - crect.x1),
- texture_y2 = texture_y1 + (crect.y1 - crect.y2);
-
- // X pixmaps might be Y inverted, invert the texture coordinates
- if (y_inverted) {
- texture_y1 = texture_height - texture_y1;
- texture_y2 = texture_height - texture_y2;
- }
-
- // Vertex coordinates
- auto vx1 = crect.x1;
- auto vy1 = crect.y2;
- auto vx2 = crect.x2;
- auto vy2 = crect.y1;
-
- // log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d",
- // ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
-
- memcpy(&coord[i * 16],
- ((GLint[][2]){
- {vx1, vy1},
- {texture_x1, texture_y1},
- {vx2, vy1},
- {texture_x2, texture_y1},
- {vx2, vy2},
- {texture_x2, texture_y2},
- {vx1, vy2},
- {texture_x1, texture_y2},
- }),
- sizeof(GLint[2]) * 8);
-
- GLuint u = (GLuint)(i * 4);
- memcpy(&indices[i * 6],
- ((GLuint[]){u + 0, u + 1, u + 2, u + 2, u + 3, u + 0}),
- sizeof(GLuint) * 6);
- }
-}
-
-// 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) {
- auto gd = (struct gl_data *)base;
- struct backend_image *img = image_data;
- auto inner = (struct gl_texture *)img->inner;
-
- // Painting
- int nrects;
- const rect_t *rects;
- rects = pixman_region32_rectangles((region_t *)reg_tgt, &nrects);
- if (!nrects) {
- // Nothing to paint
- return;
- }
-
- // Until we start to use glClipControl, reg_tgt, dst_x and dst_y and
- // in a different coordinate system than the one OpenGL uses.
- // OpenGL window coordinate (or NDC) has the origin at the lower left of the
- // screen, with y axis pointing up; Xorg has the origin at the upper left of the
- // screen, with y axis pointing down. We have to do some coordinate conversion in
- // this function
-
- 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);
-
- 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) {
- // Build program
- ret->prog = gl_create_program_from_str(vshader_str, fshader_str);
- if (!ret->prog) {
- log_error("Failed to create GLSL program.");
- return -1;
- }
-
- // 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");
-
- gl_check_err();
-
- return true;
-}
-
-/**
- * Callback to run on root window size change.
- */
-void gl_resize(struct gl_data *gd, int width, int height) {
- GLint viewport_dimensions[2];
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
-
- gd->height = height;
- gd->width = width;
-
- assert(viewport_dimensions[0] >= gd->width);
- assert(viewport_dimensions[1] >= gd->height);
-
- glBindTexture(GL_TEXTURE_2D, gd->back_texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_BGR,
- GL_UNSIGNED_BYTE, NULL);
-
- 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,
- int height, bool y_inverted) {
- static const GLuint fill_vert_in_coord_loc = 0;
- int nrects;
- const rect_t *rect = pixman_region32_rectangles((region_t *)clip, &nrects);
- auto gd = (struct gl_data *)base;
-
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
-
- GLuint bo[2];
- glGenBuffers(2, bo);
- glUseProgram(gd->fill_shader.prog);
- glUniform4f(gd->fill_shader.color_loc, (GLfloat)c.red, (GLfloat)c.green,
- (GLfloat)c.blue, (GLfloat)c.alpha);
- glEnableVertexAttribArray(fill_vert_in_coord_loc);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
-
- auto coord = ccalloc(nrects * 8, GLint);
- auto indices = ccalloc(nrects * 6, GLuint);
- for (int i = 0; i < nrects; i++) {
- GLint y1 = y_inverted ? height - rect[i].y2 : rect[i].y1,
- y2 = y_inverted ? height - rect[i].y1 : rect[i].y2;
- // clang-format off
- memcpy(&coord[i * 8],
- ((GLint[][2]){
- {rect[i].x1, y1}, {rect[i].x2, y1},
- {rect[i].x2, y2}, {rect[i].x1, y2}}),
- sizeof(GLint[2]) * 4);
- // clang-format on
- indices[i * 6 + 0] = (GLuint)i * 4 + 0;
- indices[i * 6 + 1] = (GLuint)i * 4 + 1;
- indices[i * 6 + 2] = (GLuint)i * 4 + 2;
- indices[i * 6 + 3] = (GLuint)i * 4 + 2;
- indices[i * 6 + 4] = (GLuint)i * 4 + 3;
- indices[i * 6 + 5] = (GLuint)i * 4 + 0;
- }
- glBufferData(GL_ARRAY_BUFFER, nrects * 8 * (long)sizeof(*coord), coord, GL_STREAM_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, nrects * 6 * (long)sizeof(*indices),
- indices, GL_STREAM_DRAW);
-
- glVertexAttribPointer(fill_vert_in_coord_loc, 2, GL_INT, GL_FALSE,
- sizeof(*coord) * 2, (void *)0);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target);
- glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
-
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDisableVertexAttribArray(fill_vert_in_coord_loc);
- glBindVertexArray(0);
- glDeleteVertexArrays(1, &vao);
-
- glDeleteBuffers(2, bo);
- free(indices);
- free(coord);
-
- gl_check_err();
-}
-
-void gl_fill(backend_t *base, struct color c, const region_t *clip) {
- auto gd = (struct gl_data *)base;
- return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
-}
-
-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);
- assert(inner->user_data == NULL);
-
- glDeleteTextures(1, &inner->texture);
- glDeleteTextures(2, inner->auxiliary_texture);
- free(inner);
- gl_check_err();
-}
-
-void gl_release_image(backend_t *base, void *image_data) {
- struct backend_image *wd = image_data;
- auto inner = (struct gl_texture *)wd->inner;
- inner->refcount--;
- assert(inner->refcount >= 0);
- if (inner->refcount == 0) {
- gl_release_image_inner(base, inner);
- }
- free(wd);
-}
-
-static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
- if (shader->prog) {
- glDeleteProgram(shader->prog);
- }
-
- shader->prog = 0;
-}
-
-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]);
- }
- 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;
- }
-
- // 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},
- {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;
-}
-
-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;
-}
-
-// 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;
- }
-);
-
-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
-
-bool gl_init(struct gl_data *gd, session_t *ps) {
- // Initialize GLX data structure
- glDisable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
-
- glEnable(GL_BLEND);
- // X pixmap is in premultiplied alpha, so we might just as well use it too.
- // Thanks to derhass for help.
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-
- // Initialize stencil buffer
- glDisable(GL_STENCIL_TEST);
- glStencilMask(0x1);
- glStencilFunc(GL_EQUAL, 0x1, 0x1);
-
- // Set gl viewport to the maximum supported size so we won't have to worry about
- // it later on when the screen is resized. The corresponding projection matrix can
- // be set now and won't have to be updated. Since fragments outside the target
- // buffer are skipped anyways, this should have no impact on performance.
- GLint viewport_dimensions[2];
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
- glViewport(0, 0, viewport_dimensions[0], viewport_dimensions[1]);
-
- // Clear screen
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-
- glGenFramebuffers(1, &gd->back_fbo);
- glGenTextures(1, &gd->back_texture);
- if (!gd->back_fbo || !gd->back_texture) {
- log_error("Failed to generate a framebuffer object");
- return false;
- }
-
- glBindTexture(GL_TEXTURE_2D, gd->back_texture);
- 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);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- // 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},
- {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");
- glUseProgram(gd->fill_shader.prog);
- glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
- glUseProgram(0);
-
- gd->present_prog = gl_create_program_from_str(present_vertex_shader, dummy_frag);
- if (!gd->present_prog) {
- log_error("Failed to create the present shader");
- return false;
- }
- pml = glGetUniformLocationChecked(gd->present_prog, "projection");
- glUseProgram(gd->present_prog);
- glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0);
- glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
- glUseProgram(0);
-
- gd->brightness_shader.prog =
- gl_create_program_from_str(interpolating_vert, interpolating_frag);
- if (!gd->brightness_shader.prog) {
- log_error("Failed to create the brightness shader");
- return false;
- }
- pml = glGetUniformLocationChecked(gd->brightness_shader.prog, "projection");
- glUseProgram(gd->brightness_shader.prog);
- glUniform1i(glGetUniformLocationChecked(gd->brightness_shader.prog, "tex"), 0);
- glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
- glUseProgram(0);
-
- // Set up the size of the back texture
- gl_resize(gd, ps->root_width, ps->root_height);
-
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->back_fbo);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- gd->back_texture, 0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
- return false;
- }
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-
- gd->logger = gl_string_marker_logger_new();
- if (gd->logger) {
- log_add_target_tls(gd->logger);
- }
-
- const char *vendor = (const char *)glGetString(GL_VENDOR);
- log_debug("GL_VENDOR = %s", vendor);
- if (strcmp(vendor, "NVIDIA Corporation") == 0) {
- log_info("GL vendor is NVIDIA, don't use glFinish");
- gd->is_nvidia = true;
- } else {
- gd->is_nvidia = false;
- }
-
- 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;
- }
-
- gl_check_err();
-}
-
-GLuint gl_new_texture(GLenum target) {
- GLuint texture;
- glGenTextures(1, &texture);
- if (!texture) {
- log_error("Failed to generate texture");
- return 0;
- }
-
- glBindTexture(target, texture);
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glBindTexture(target, 0);
-
- return texture;
-}
-
-/// Actually duplicate a texture into a new one, if this texture is shared
-static inline void gl_image_decouple(backend_t *base, struct backend_image *img) {
- if (img->inner->refcount == 1) {
- return;
- }
- auto gd = (struct gl_data *)base;
- auto inner = (struct gl_texture *)img->inner;
- auto new_tex = ccalloc(1, struct gl_texture);
-
- new_tex->texture = gl_new_texture(GL_TEXTURE_2D);
- new_tex->y_inverted = true;
- new_tex->height = inner->height;
- new_tex->width = inner->width;
- new_tex->refcount = 1;
- new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data);
-
- glBindTexture(GL_TEXTURE_2D, new_tex->texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, new_tex->width, new_tex->height, 0,
- GL_BGRA, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- assert(gd->present_prog);
- glUseProgram(gd->present_prog);
- glBindTexture(GL_TEXTURE_2D, inner->texture);
-
- GLuint fbo;
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- new_tex->texture, 0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- gl_check_fb_complete(GL_DRAW_FRAMEBUFFER);
-
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- // clang-format off
- GLint coord[] = {
- // top left
- 0, 0, // vertex coord
- 0, 0, // texture coord
-
- // top right
- new_tex->width, 0, // vertex coord
- new_tex->width, 0, // texture coord
-
- // bottom right
- new_tex->width, new_tex->height,
- new_tex->width, new_tex->height,
-
- // bottom left
- 0, new_tex->height,
- 0, new_tex->height,
- };
- // clang-format on
- GLuint indices[] = {0, 1, 2, 2, 3, 0};
-
- 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) * 16, coord, GL_STATIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 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));
-
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
-
- glDisableVertexAttribArray(vert_coord_loc);
- glDisableVertexAttribArray(vert_in_texcoord_loc);
- glBindVertexArray(0);
- glDeleteVertexArrays(1, &vao);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDeleteBuffers(2, bo);
-
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glDeleteFramebuffers(1, &fbo);
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glUseProgram(0);
-
- gl_check_err();
-
- img->inner = (struct backend_image_inner_base *)new_tex;
- inner->refcount--;
-}
-
-static void gl_image_apply_alpha(backend_t *base, struct backend_image *img,
- const region_t *reg_op, double alpha) {
- // Result color = 0 (GL_ZERO) + alpha (GL_CONSTANT_ALPHA) * original color
- auto inner = (struct gl_texture *)img->inner;
- glBlendFunc(GL_ZERO, GL_CONSTANT_ALPHA);
- glBlendColor(0, 0, 0, (GLclampf)alpha);
- GLuint fbo;
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- 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);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glDeleteFramebuffers(1, &fbo);
-}
-
-void gl_present(backend_t *base, const region_t *region) {
- auto gd = (struct gl_data *)base;
-
- int nrects;
- const rect_t *rect = pixman_region32_rectangles((region_t *)region, &nrects);
- auto coord = ccalloc(nrects * 8, GLint);
- auto indices = ccalloc(nrects * 6, GLuint);
- for (int i = 0; i < nrects; i++) {
- // clang-format off
- memcpy(&coord[i * 8],
- ((GLint[]){rect[i].x1, gd->height - rect[i].y2,
- rect[i].x2, gd->height - rect[i].y2,
- rect[i].x2, gd->height - rect[i].y1,
- rect[i].x1, gd->height - rect[i].y1}),
- sizeof(GLint) * 8);
- // clang-format on
-
- GLuint u = (GLuint)(i * 4);
- memcpy(&indices[i * 6],
- ((GLuint[]){u + 0, u + 1, u + 2, u + 2, u + 3, u + 0}),
- sizeof(GLuint) * 6);
- }
-
- glUseProgram(gd->present_prog);
- glBindTexture(GL_TEXTURE_2D, gd->back_texture);
-
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
-
- GLuint bo[2];
- glGenBuffers(2, bo);
- glEnableVertexAttribArray(vert_coord_loc);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(GLint) * nrects * 8, coord, GL_STREAM_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(GLuint) * nrects * 6, indices,
- GL_STREAM_DRAW);
-
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 2, NULL);
- glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
- glDeleteBuffers(2, bo);
- glDeleteVertexArrays(1, &vao);
-
- free(coord);
- free(indices);
-}
-
-bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
- const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
- struct backend_image *tex = image_data;
- switch (op) {
- case IMAGE_OP_APPLY_ALPHA:
- gl_image_decouple(base, tex);
- assert(tex->inner->refcount == 1);
- gl_image_apply_alpha(base, tex, reg_op, *(double *)arg);
- break;
- }
-
- 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;
-}
diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h
deleted file mode 100644
index b1d93b0..0000000
--- a/src/backend/gl/gl_common.h
+++ /dev/null
@@ -1,220 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#pragma once
-#include <GL/gl.h>
-#include <GL/glext.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include "backend/backend.h"
-#include "log.h"
-#include "region.h"
-
-#define CASESTRRET(s) \
- case s: return #s
-
-// Program and uniforms for window shader
-typedef struct {
- GLuint prog;
- GLint unifm_opacity;
- GLint unifm_invert_color;
- GLint unifm_tex;
- GLint unifm_dim;
- GLint unifm_brightness;
- GLint unifm_max_brightness;
-} gl_win_shader_t;
-
-// Program and uniforms for brightness shader
-typedef struct {
- GLuint prog;
-} gl_brightness_shader_t;
-
-// Program and uniforms for blur shader
-typedef struct {
- GLuint prog;
- GLint unifm_pixel_norm;
- GLint unifm_opacity;
- GLint texorig_loc;
- GLint scale_loc;
-} gl_blur_shader_t;
-
-typedef struct {
- GLuint prog;
- GLint color_loc;
-} gl_fill_shader_t;
-
-/// @brief Wrapper of a binded GLX texture.
-struct gl_texture {
- int refcount;
- bool has_alpha;
- GLuint texture;
- int width, height;
- bool y_inverted;
-
- // Textures for auxiliary uses.
- GLuint auxiliary_texture[2];
- void *user_data;
-};
-
-struct gl_data {
- backend_t base;
- // If we are using proprietary NVIDIA driver
- bool is_nvidia;
- // Height and width of the root window
- int height, width;
- gl_win_shader_t win_shader;
- gl_brightness_shader_t brightness_shader;
- gl_fill_shader_t fill_shader;
- GLuint back_texture, back_fbo;
- GLuint present_prog;
-
- /// Called when an gl_texture is decoupled from the texture it refers. Returns
- /// the decoupled user_data
- void *(*decouple_texture_user_data)(backend_t *base, void *user_data);
-
- /// Release the user data attached to a gl_texture
- void (*release_user_data)(backend_t *base, struct gl_texture *);
-
- struct log_target *logger;
-};
-
-typedef struct session session_t;
-
-#define GL_PROG_MAIN_INIT \
- { .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, .unifm_tex = -1, }
-
-GLuint gl_create_shader(GLenum shader_type, const char *shader_str);
-GLuint gl_create_program(const GLuint *const shaders, int nshaders);
-GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str);
-
-/**
- * @brief Render a region with texture data.
- */
-void gl_compose(backend_t *, void *ptex,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
- const region_t *reg_tgt, const region_t *reg_visible);
-
-void gl_resize(struct gl_data *, int width, int height);
-
-bool gl_init(struct gl_data *gd, session_t *);
-void gl_deinit(struct gl_data *gd);
-
-GLuint gl_new_texture(GLenum target);
-
-bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
- const region_t *reg_op, const region_t *reg_visible, void *arg);
-
-void gl_release_image(backend_t *base, void *image_data);
-
-void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visible);
-
-bool gl_blur(backend_t *base, double opacity, void *, const region_t *reg_blur,
- const region_t *reg_visible);
-void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
-void gl_destroy_blur_context(backend_t *base, void *ctx);
-void gl_get_blur_size(void *blur_context, int *width, int *height);
-
-void gl_fill(backend_t *base, struct color, const region_t *clip);
-
-void gl_present(backend_t *base, const region_t *);
-bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color *output);
-
-static inline void gl_delete_texture(GLuint texture) {
- glDeleteTextures(1, &texture);
-}
-
-/**
- * Get a textual representation of an OpenGL error.
- */
-static inline const char *gl_get_err_str(GLenum err) {
- switch (err) {
- CASESTRRET(GL_NO_ERROR);
- CASESTRRET(GL_INVALID_ENUM);
- CASESTRRET(GL_INVALID_VALUE);
- CASESTRRET(GL_INVALID_OPERATION);
- CASESTRRET(GL_INVALID_FRAMEBUFFER_OPERATION);
- CASESTRRET(GL_OUT_OF_MEMORY);
- CASESTRRET(GL_STACK_UNDERFLOW);
- CASESTRRET(GL_STACK_OVERFLOW);
- CASESTRRET(GL_FRAMEBUFFER_UNDEFINED);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER);
- CASESTRRET(GL_FRAMEBUFFER_UNSUPPORTED);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS);
- }
- return NULL;
-}
-
-/**
- * Check for GLX error.
- *
- * http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/
- */
-static inline void gl_check_err_(const char *func, int line) {
- GLenum err = GL_NO_ERROR;
-
- while (GL_NO_ERROR != (err = glGetError())) {
- const char *errtext = gl_get_err_str(err);
- if (errtext) {
- log_printf(tls_logger, LOG_LEVEL_ERROR, func,
- "GLX error at line %d: %s", line, errtext);
- } else {
- log_printf(tls_logger, LOG_LEVEL_ERROR, func,
- "GLX error at line %d: %d", line, err);
- }
- }
-}
-
-static inline void gl_clear_err(void) {
- while (glGetError() != GL_NO_ERROR)
- ;
-}
-
-#define gl_check_err() gl_check_err_(__func__, __LINE__)
-
-/**
- * Check for GL framebuffer completeness.
- */
-static inline bool gl_check_fb_complete_(const char *func, int line, GLenum fb) {
- GLenum status = glCheckFramebufferStatus(fb);
-
- if (status == GL_FRAMEBUFFER_COMPLETE) {
- return true;
- }
-
- const char *stattext = gl_get_err_str(status);
- if (stattext) {
- log_printf(tls_logger, LOG_LEVEL_ERROR, func,
- "Framebuffer attachment failed at line %d: %s", line, stattext);
- } else {
- log_printf(tls_logger, LOG_LEVEL_ERROR, func,
- "Framebuffer attachment failed at line %d: %d", line, status);
- }
-
- return false;
-}
-
-#define gl_check_fb_complete(fb) gl_check_fb_complete_(__func__, __LINE__, (fb))
-
-/**
- * Check if a GLX extension exists.
- */
-static inline bool gl_has_extension(const char *ext) {
- int nexts = 0;
- glGetIntegerv(GL_NUM_EXTENSIONS, &nexts);
- for (int i = 0; i < nexts || !nexts; i++) {
- const char *exti = (const char *)glGetStringi(GL_EXTENSIONS, (GLuint)i);
- if (exti == NULL) {
- break;
- }
- if (strcmp(ext, exti) == 0) {
- return true;
- }
- }
- gl_clear_err();
- log_info("Missing GL extension %s.", ext);
- return false;
-}
diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c
deleted file mode 100644
index 1397d19..0000000
--- a/src/backend/gl/glx.c
+++ /dev/null
@@ -1,648 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Compton - a compositor for X11
- *
- * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
- *
- * Copyright (c) 2011-2013, Christopher Jeffrey
- * Copyright (c) 2019 Yuxuan Shui <[email protected]>
- * See LICENSE-mit for more information.
- *
- */
-
-#include <X11/Xlib-xcb.h>
-#include <assert.h>
-#include <limits.h>
-#include <pixman.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <xcb/composite.h>
-#include <xcb/xcb.h>
-
-#include "backend/backend.h"
-#include "backend/backend_common.h"
-#include "backend/gl/gl_common.h"
-#include "backend/gl/glx.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "log.h"
-#include "picom.h"
-#include "region.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-
-struct _glx_pixmap {
- GLXPixmap glpixmap;
- xcb_pixmap_t pixmap;
- bool owned;
-};
-
-struct _glx_data {
- struct gl_data gl;
- Display *display;
- int screen;
- xcb_window_t target_win;
- GLXContext ctx;
-};
-
-#define glXGetFBConfigAttribChecked(a, b, attr, c) \
- do { \
- if (glXGetFBConfigAttrib(a, b, attr, c)) { \
- log_info("Cannot get FBConfig attribute " #attr); \
- continue; \
- } \
- } while (0)
-
-struct glx_fbconfig_info *glx_find_fbconfig(Display *dpy, int screen, struct xvisual_info m) {
- log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", m.red_size,
- m.blue_size, m.green_size, m.alpha_size, m.visual_depth);
-
- int ncfg;
- // clang-format off
- GLXFBConfig *cfg =
- glXChooseFBConfig(dpy, screen, (int[]){
- GLX_RENDER_TYPE, GLX_RGBA_BIT,
- GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
- GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
- GLX_X_RENDERABLE, true,
- GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, (GLint)GLX_DONT_CARE,
- GLX_BUFFER_SIZE, m.red_size + m.green_size +
- m.blue_size + m.alpha_size,
- GLX_RED_SIZE, m.red_size,
- GLX_BLUE_SIZE, m.blue_size,
- GLX_GREEN_SIZE, m.green_size,
- GLX_ALPHA_SIZE, m.alpha_size,
- GLX_STENCIL_SIZE, 0,
- GLX_DEPTH_SIZE, 0,
- 0
- }, &ncfg);
- // clang-format on
-
- int texture_tgts, y_inverted, texture_fmt;
- bool found = false;
- int min_cost = INT_MAX;
- GLXFBConfig ret;
- for (int i = 0; i < ncfg; i++) {
- int depthbuf, stencil, doublebuf, bufsize;
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BUFFER_SIZE, &bufsize);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_DEPTH_SIZE, &depthbuf);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_STENCIL_SIZE, &stencil);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_DOUBLEBUFFER, &doublebuf);
- if (depthbuf + stencil + bufsize * (doublebuf + 1) >= min_cost) {
- continue;
- }
- int red, green, blue;
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_RED_SIZE, &red);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BLUE_SIZE, &blue);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_GREEN_SIZE, &green);
- if (red != m.red_size || green != m.green_size || blue != m.blue_size) {
- // Color size doesn't match, this cannot work
- continue;
- }
-
- int rgb, rgba;
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &rgb);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &rgba);
- if (!rgb && !rgba) {
- log_info("FBConfig is neither RGBA nor RGB, we cannot "
- "handle this setup.");
- continue;
- }
-
- int visual;
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_VISUAL_ID, &visual);
- if (m.visual_depth != -1 &&
- x_get_visual_depth(XGetXCBConnection(dpy), (xcb_visualid_t)visual) !=
- m.visual_depth) {
- // FBConfig and the correspondent X Visual might not have the same
- // depth. (e.g. 32 bit FBConfig with a 24 bit Visual). This is
- // quite common, seen in both open source and proprietary drivers.
- //
- // If the FBConfig has a matching depth but its visual doesn't, we
- // still cannot use it.
- continue;
- }
-
- // All check passed, we are using this one.
- found = true;
- ret = cfg[i];
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT,
- &texture_tgts);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_Y_INVERTED_EXT, &y_inverted);
-
- // Prefer the texture format with matching alpha, with the other one as
- // fallback
- if (m.alpha_size) {
- texture_fmt = rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT
- : GLX_TEXTURE_FORMAT_RGB_EXT;
- } else {
- texture_fmt =
- rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
- }
- min_cost = depthbuf + stencil + bufsize * (doublebuf + 1);
- }
- free(cfg);
- if (!found) {
- return NULL;
- }
-
- auto info = cmalloc(struct glx_fbconfig_info);
- info->cfg = ret;
- info->texture_tgts = texture_tgts;
- info->texture_fmt = texture_fmt;
- info->y_inverted = y_inverted;
- return info;
-}
-
-/**
- * Free a glx_texture_t.
- */
-static void glx_release_image(backend_t *base, struct gl_texture *tex) {
- struct _glx_data *gd = (void *)base;
-
- struct _glx_pixmap *p = tex->user_data;
- // Release binding
- if (p->glpixmap && tex->texture) {
- glBindTexture(GL_TEXTURE_2D, tex->texture);
- glXReleaseTexImageEXT(gd->display, p->glpixmap, GLX_FRONT_LEFT_EXT);
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- // Free GLX Pixmap
- if (p->glpixmap) {
- glXDestroyPixmap(gd->display, p->glpixmap);
- p->glpixmap = 0;
- }
-
- if (p->owned) {
- xcb_free_pixmap(base->c, p->pixmap);
- p->pixmap = XCB_NONE;
- }
-
- free(p);
- tex->user_data = NULL;
-}
-
-/**
- * Destroy GLX related resources.
- */
-void glx_deinit(backend_t *base) {
- struct _glx_data *gd = (void *)base;
-
- gl_deinit(&gd->gl);
-
- // Destroy GLX context
- if (gd->ctx) {
- glXMakeCurrent(gd->display, None, NULL);
- glXDestroyContext(gd->display, gd->ctx);
- gd->ctx = 0;
- }
-
- free(gd);
-}
-
-static void *glx_decouple_user_data(backend_t *base attr_unused, void *ud attr_unused) {
- auto ret = cmalloc(struct _glx_pixmap);
- ret->owned = false;
- ret->glpixmap = 0;
- ret->pixmap = 0;
- return ret;
-}
-
-static bool glx_set_swap_interval(int interval, Display *dpy, GLXDrawable drawable) {
- bool vsync_enabled = false;
- if (glxext.has_GLX_MESA_swap_control) {
- vsync_enabled = (glXSwapIntervalMESA((uint)interval) == 0);
- }
- if (!vsync_enabled && glxext.has_GLX_SGI_swap_control) {
- vsync_enabled = (glXSwapIntervalSGI(interval) == 0);
- }
- if (!vsync_enabled && glxext.has_GLX_EXT_swap_control) {
- // glXSwapIntervalEXT doesn't return if it's successful
- glXSwapIntervalEXT(dpy, drawable, interval);
- vsync_enabled = true;
- }
- return vsync_enabled;
-}
-
-/**
- * Initialize OpenGL.
- */
-static backend_t *glx_init(session_t *ps) {
- bool success = false;
- glxext_init(ps->dpy, ps->scr);
- auto gd = ccalloc(1, struct _glx_data);
- init_backend_base(&gd->gl.base, ps);
-
- gd->display = ps->dpy;
- gd->screen = ps->scr;
- gd->target_win = session_get_target_window(ps);
-
- XVisualInfo *pvis = NULL;
-
- // Check for GLX extension
- if (!ps->glx_exists) {
- log_error("No GLX extension.");
- goto end;
- }
-
- // Get XVisualInfo
- int nitems = 0;
- XVisualInfo vreq = {.visualid = ps->vis};
- pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
- if (!pvis) {
- log_error("Failed to acquire XVisualInfo for current visual.");
- goto end;
- }
-
- // Ensure the visual is double-buffered
- int value = 0;
- if (glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
- log_error("Root visual is not a GL visual.");
- goto end;
- }
-
- if (glXGetConfig(ps->dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) {
- log_error("Root visual lacks stencil buffer.");
- goto end;
- }
-
- if (glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
- log_error("Root visual is not a double buffered GL visual.");
- goto end;
- }
-
- if (glXGetConfig(ps->dpy, pvis, GLX_RGBA, &value) || !value) {
- log_error("Root visual is a color index visual, not supported");
- goto end;
- }
-
- if (!glxext.has_GLX_EXT_texture_from_pixmap) {
- log_error("GLX_EXT_texture_from_pixmap is not supported by your driver");
- goto end;
- }
-
- if (!glxext.has_GLX_ARB_create_context) {
- log_error("GLX_ARB_create_context is not supported by your driver");
- goto end;
- }
-
- // Find a fbconfig with visualid matching the one from the target win, so we can
- // be sure that the fbconfig is compatible with our target window.
- int ncfgs;
- GLXFBConfig *cfg = glXGetFBConfigs(gd->display, gd->screen, &ncfgs);
- bool found = false;
- for (int i = 0; i < ncfgs; i++) {
- int visualid;
- glXGetFBConfigAttribChecked(gd->display, cfg[i], GLX_VISUAL_ID, &visualid);
- if ((VisualID)visualid != pvis->visualid) {
- continue;
- }
-
- gd->ctx = glXCreateContextAttribsARB(ps->dpy, cfg[i], 0, true,
- (int[]){
- GLX_CONTEXT_MAJOR_VERSION_ARB,
- 3,
- GLX_CONTEXT_MINOR_VERSION_ARB,
- 3,
- GLX_CONTEXT_PROFILE_MASK_ARB,
- GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
- 0,
- });
- free(cfg);
-
- if (!gd->ctx) {
- log_error("Failed to get GLX context.");
- goto end;
- }
- found = true;
- break;
- }
-
- if (!found) {
- log_error("Couldn't find a suitable fbconfig for the target window");
- goto end;
- }
-
- // Attach GLX context
- GLXDrawable tgt = gd->target_win;
- if (!glXMakeCurrent(ps->dpy, tgt, gd->ctx)) {
- log_error("Failed to attach GLX context.");
- goto end;
- }
-
- if (!gl_init(&gd->gl, ps)) {
- log_error("Failed to setup OpenGL");
- goto end;
- }
-
- gd->gl.decouple_texture_user_data = glx_decouple_user_data;
- gd->gl.release_user_data = glx_release_image;
-
- if (ps->o.vsync) {
- if (!glx_set_swap_interval(1, ps->dpy, tgt)) {
- log_error("Failed to enable vsync.");
- }
- } else {
- glx_set_swap_interval(0, ps->dpy, tgt);
- }
-
- success = true;
-
-end:
- if (pvis) {
- XFree(pvis);
- }
-
- if (!success) {
- glx_deinit(&gd->gl.base);
- return NULL;
- }
-
- return &gd->gl.base;
-}
-
-static void *
-glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
- struct _glx_data *gd = (void *)base;
- struct _glx_pixmap *glxpixmap = NULL;
- // Retrieve pixmap parameters, if they aren't provided
- if (fmt.visual_depth > OPENGL_MAX_DEPTH) {
- log_error("Requested depth %d higher than max possible depth %d.",
- fmt.visual_depth, OPENGL_MAX_DEPTH);
- return false;
- }
-
- if (fmt.visual_depth < 0) {
- log_error("Pixmap %#010x with invalid depth %d", pixmap, fmt.visual_depth);
- return false;
- }
-
- auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), NULL);
- if (!r) {
- log_error("Invalid pixmap %#010x", pixmap);
- return NULL;
- }
-
- log_trace("Binding pixmap %#010x", pixmap);
- auto wd = ccalloc(1, struct backend_image);
- wd->max_brightness = 1;
- auto inner = ccalloc(1, struct gl_texture);
- inner->width = wd->ewidth = r->width;
- inner->height = wd->eheight = r->height;
- wd->inner = (struct backend_image_inner_base *)inner;
- free(r);
-
- auto fbcfg = glx_find_fbconfig(gd->display, gd->screen, fmt);
- if (!fbcfg) {
- log_error("Couldn't find FBConfig with requested visual %x", fmt.visual);
- goto err;
- }
-
- // Choose a suitable texture target for our pixmap.
- // Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean
- // of the bits in texture_tgts
- if (!(fbcfg->texture_tgts & GLX_TEXTURE_2D_BIT_EXT)) {
- log_error("Cannot bind pixmap to GL_TEXTURE_2D, giving up");
- goto err;
- }
-
- log_debug("depth %d, rgba %d", fmt.visual_depth,
- (fbcfg->texture_fmt == GLX_TEXTURE_FORMAT_RGBA_EXT));
-
- GLint attrs[] = {
- GLX_TEXTURE_FORMAT_EXT,
- fbcfg->texture_fmt,
- GLX_TEXTURE_TARGET_EXT,
- GLX_TEXTURE_2D_EXT,
- 0,
- };
-
- inner->y_inverted = fbcfg->y_inverted;
-
- glxpixmap = cmalloc(struct _glx_pixmap);
- glxpixmap->pixmap = pixmap;
- glxpixmap->glpixmap = glXCreatePixmap(gd->display, fbcfg->cfg, pixmap, attrs);
- glxpixmap->owned = owned;
- free(fbcfg);
-
- if (!glxpixmap->glpixmap) {
- log_error("Failed to create glpixmap for pixmap %#010x", pixmap);
- goto err;
- }
-
- log_trace("GLXPixmap %#010lx", glxpixmap->glpixmap);
-
- // Create texture
- inner->user_data = glxpixmap;
- inner->texture = gl_new_texture(GL_TEXTURE_2D);
- inner->has_alpha = fmt.alpha_size != 0;
- wd->opacity = 1;
- wd->color_inverted = false;
- wd->dim = 0;
- wd->inner->refcount = 1;
- glBindTexture(GL_TEXTURE_2D, inner->texture);
- glXBindTexImageEXT(gd->display, glxpixmap->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- gl_check_err();
- return wd;
-err:
- if (glxpixmap && glxpixmap->glpixmap) {
- glXDestroyPixmap(gd->display, glxpixmap->glpixmap);
- }
- free(glxpixmap);
-
- if (owned) {
- xcb_free_pixmap(base->c, pixmap);
- }
- free(wd);
- return NULL;
-}
-
-static void glx_present(backend_t *base, const region_t *region attr_unused) {
- struct _glx_data *gd = (void *)base;
- gl_present(base, region);
- glXSwapBuffers(gd->display, gd->target_win);
- if (!gd->gl.is_nvidia) {
- glFinish();
- }
-}
-
-static int glx_buffer_age(backend_t *base) {
- if (!glxext.has_GLX_EXT_buffer_age) {
- return -1;
- }
-
- struct _glx_data *gd = (void *)base;
- unsigned int val;
- glXQueryDrawable(gd->display, gd->target_win, GLX_BACK_BUFFER_AGE_EXT, &val);
- return (int)val ?: -1;
-}
-
-static void glx_diagnostics(backend_t *base) {
- struct _glx_data *gd = (void *)base;
- bool warn_software_rendering = false;
- const char *software_renderer_names[] = {"llvmpipe", "SWR", "softpipe"};
- auto glx_vendor = glXGetClientString(gd->display, GLX_VENDOR);
- printf("* Driver vendors:\n");
- printf(" * GLX: %s\n", glx_vendor);
- printf(" * GL: %s\n", glGetString(GL_VENDOR));
-
- auto gl_renderer = (const char *)glGetString(GL_RENDERER);
- printf("* GL renderer: %s\n", gl_renderer);
- if (strcmp(glx_vendor, "Mesa Project and SGI")) {
- for (size_t i = 0; i < ARR_SIZE(software_renderer_names); i++) {
- if (strstr(gl_renderer, software_renderer_names[i]) != NULL) {
- warn_software_rendering = true;
- break;
- }
- }
- }
-
-#ifdef GLX_MESA_query_renderer
- if (glxext.has_GLX_MESA_query_renderer) {
- unsigned int accelerated = 0;
- glXQueryCurrentRendererIntegerMESA(GLX_RENDERER_ACCELERATED_MESA, &accelerated);
- printf("* Accelerated: %d\n", accelerated);
-
- // Trust GLX_MESA_query_renderer when it's available
- warn_software_rendering = (accelerated == 0);
- }
-#endif
-
- if (warn_software_rendering) {
- printf("\n(You are using a software renderer. Unless you are doing this\n"
- "intentionally, this means you don't have a graphics driver\n"
- "properly installed. Performance will suffer. Please fix this\n"
- "before reporting your issue.)\n");
- }
-}
-
-struct backend_operations glx_ops = {
- .init = glx_init,
- .deinit = glx_deinit,
- .bind_pixmap = glx_bind_pixmap,
- .release_image = gl_release_image,
- .compose = gl_compose,
- .image_op = gl_image_op,
- .set_image_property = default_set_image_property,
- .read_pixel = gl_read_pixel,
- .clone_image = default_clone_image,
- .blur = gl_blur,
- .is_image_transparent = default_is_image_transparent,
- .present = glx_present,
- .buffer_age = glx_buffer_age,
- .render_shadow = default_backend_render_shadow,
- .fill = gl_fill,
- .create_blur_context = gl_create_blur_context,
- .destroy_blur_context = gl_destroy_blur_context,
- .get_blur_size = gl_get_blur_size,
- .diagnostics = glx_diagnostics,
- .max_buffer_age = 5, // Why?
-};
-
-/**
- * Check if a GLX extension exists.
- */
-static inline bool glx_has_extension(Display *dpy, int screen, const char *ext) {
- const char *glx_exts = glXQueryExtensionsString(dpy, screen);
- if (!glx_exts) {
- log_error("Failed get GLX extension list.");
- return false;
- }
-
- auto inlen = strlen(ext);
- const char *curr = glx_exts;
- bool match = false;
- while (curr && !match) {
- const char *end = strchr(curr, ' ');
- if (!end) {
- // Last extension string
- match = strcmp(ext, curr) == 0;
- } else if (curr + inlen == end) {
- // Length match, do match string
- match = strncmp(ext, curr, (unsigned long)(end - curr)) == 0;
- }
- curr = end ? end + 1 : NULL;
- }
-
- if (!match) {
- log_info("Missing GLX extension %s.", ext);
- } else {
- log_info("Found GLX extension %s.", ext);
- }
-
- return match;
-}
-
-struct glxext_info glxext = {0};
-PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
-PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
-PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
-PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML;
-PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
-PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
-PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA;
-PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
-PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
-PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
-
-#ifdef GLX_MESA_query_renderer
-PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA;
-#endif
-
-void glxext_init(Display *dpy, int screen) {
- if (glxext.initialized) {
- return;
- }
- glxext.initialized = true;
-#define check_ext(name) glxext.has_##name = glx_has_extension(dpy, screen, #name)
- check_ext(GLX_SGI_video_sync);
- check_ext(GLX_SGI_swap_control);
- check_ext(GLX_OML_sync_control);
- check_ext(GLX_MESA_swap_control);
- check_ext(GLX_EXT_swap_control);
- check_ext(GLX_EXT_texture_from_pixmap);
- check_ext(GLX_ARB_create_context);
- check_ext(GLX_EXT_buffer_age);
-#ifdef GLX_MESA_query_renderer
- check_ext(GLX_MESA_query_renderer);
-#endif
-#undef check_ext
-
-#define lookup(name) (name = (__typeof__(name))glXGetProcAddress((GLubyte *)#name))
- // Checking if the returned function pointer is NULL is not really necessary,
- // or maybe not even useful, since glXGetProcAddress might always return
- // something. We are doing it just for completeness' sake.
- if (!lookup(glXGetVideoSyncSGI) || !lookup(glXWaitVideoSyncSGI)) {
- glxext.has_GLX_SGI_video_sync = false;
- }
- if (!lookup(glXSwapIntervalEXT)) {
- glxext.has_GLX_EXT_swap_control = false;
- }
- if (!lookup(glXSwapIntervalMESA)) {
- glxext.has_GLX_MESA_swap_control = false;
- }
- if (!lookup(glXSwapIntervalSGI)) {
- glxext.has_GLX_SGI_swap_control = false;
- }
- if (!lookup(glXWaitForMscOML) || !lookup(glXGetSyncValuesOML)) {
- glxext.has_GLX_OML_sync_control = false;
- }
- if (!lookup(glXBindTexImageEXT) || !lookup(glXReleaseTexImageEXT)) {
- glxext.has_GLX_EXT_texture_from_pixmap = false;
- }
- if (!lookup(glXCreateContextAttribsARB)) {
- glxext.has_GLX_ARB_create_context = false;
- }
-#ifdef GLX_MESA_query_renderer
- if (!lookup(glXQueryCurrentRendererIntegerMESA)) {
- glxext.has_GLX_MESA_query_renderer = false;
- }
-#endif
-#undef lookup
-}
diff --git a/src/backend/gl/glx.h b/src/backend/gl/glx.h
deleted file mode 100644
index 1061f0b..0000000
--- a/src/backend/gl/glx.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#pragma once
-#include <stdbool.h>
-// Older version of glx.h defines function prototypes for these extensions...
-// Rename them to avoid conflicts
-#define glXSwapIntervalMESA glXSwapIntervalMESA_
-#define glXBindTexImageEXT glXBindTexImageEXT_
-#define glXReleaseTexImageEXT glXReleaseTexImageEXT
-#include <GL/glx.h>
-#undef glXSwapIntervalMESA
-#undef glXBindTexImageEXT
-#undef glXReleaseTexImageEXT
-#include <X11/Xlib.h>
-#include <xcb/xcb.h>
-#include <xcb/render.h>
-
-#include "log.h"
-#include "compiler.h"
-#include "utils.h"
-#include "x.h"
-
-struct glx_fbconfig_info {
- GLXFBConfig cfg;
- int texture_tgts;
- int texture_fmt;
- int y_inverted;
-};
-
-/// The search criteria for glx_find_fbconfig
-struct glx_fbconfig_criteria {
- /// Bit width of the red component
- int red_size;
- /// Bit width of the green component
- int green_size;
- /// Bit width of the blue component
- int blue_size;
- /// Bit width of the alpha component
- int alpha_size;
- /// The depth of X visual
- int visual_depth;
-};
-
-struct glx_fbconfig_info *glx_find_fbconfig(Display *, int screen, struct xvisual_info);
-
-
-struct glxext_info {
- bool initialized;
- bool has_GLX_SGI_video_sync;
- bool has_GLX_SGI_swap_control;
- bool has_GLX_OML_sync_control;
- bool has_GLX_MESA_swap_control;
- bool has_GLX_EXT_swap_control;
- bool has_GLX_EXT_texture_from_pixmap;
- bool has_GLX_ARB_create_context;
- bool has_GLX_EXT_buffer_age;
- bool has_GLX_MESA_query_renderer;
-};
-
-extern struct glxext_info glxext;
-
-extern PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
-extern PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
-extern PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
-extern PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML;
-extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
-extern PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
-extern PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA;
-extern PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
-extern PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
-extern PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
-
-#ifdef GLX_MESA_query_renderer
-extern PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA;
-#endif
-
-void glxext_init(Display *, int screen);