2019-02-03 13:47:36 -05:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
2018-10-03 17:46:18 -04:00
|
|
|
#include <GL/gl.h>
|
|
|
|
#include <GL/glext.h>
|
2018-12-31 02:16:35 -05:00
|
|
|
#include <locale.h>
|
2019-01-20 11:53:39 -05:00
|
|
|
#include <stdbool.h>
|
2019-01-20 16:15:20 -05:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2019-01-27 18:57:43 -05:00
|
|
|
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-08-09 19:56:04 -04:00
|
|
|
#include "backend/backend.h"
|
2018-10-03 17:46:18 -04:00
|
|
|
#include "common.h"
|
2019-01-20 16:15:20 -05:00
|
|
|
#include "compiler.h"
|
|
|
|
#include "config.h"
|
2019-03-08 20:39:18 -05:00
|
|
|
#include "kernel.h"
|
2018-10-03 17:46:18 -04:00
|
|
|
#include "log.h"
|
2019-01-20 11:53:39 -05:00
|
|
|
#include "region.h"
|
2018-12-31 02:16:35 -05:00
|
|
|
#include "string_utils.h"
|
2019-08-09 19:56:04 -04:00
|
|
|
#include "types.h"
|
2019-01-20 11:53:39 -05:00
|
|
|
#include "utils.h"
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-06-08 20:01:53 -04:00
|
|
|
#include "backend/backend_common.h"
|
2019-06-09 14:25:34 -04:00
|
|
|
#include "backend/gl/gl_common.h"
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-09 09:12:53 -05:00
|
|
|
#define GLSL(version, ...) "#version " #version "\n" #__VA_ARGS__
|
2019-03-09 20:22:38 -05:00
|
|
|
#define QUOTE(...) #__VA_ARGS__
|
2019-03-09 09:12:53 -05:00
|
|
|
|
2019-06-23 20:25:21 -04:00
|
|
|
static const GLuint vert_coord_loc = 0;
|
|
|
|
static const GLuint vert_in_texcoord_loc = 1;
|
|
|
|
|
2019-06-09 14:25:34 -04:00
|
|
|
struct gl_blur_context {
|
|
|
|
enum blur_method method;
|
|
|
|
gl_blur_shader_t *blur_shader;
|
|
|
|
|
2019-12-20 16:10:02 -05:00
|
|
|
/// 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.
|
2019-07-02 12:42:23 -04:00
|
|
|
/// Turns out calling glTexImage to resize is expensive, so we avoid that.
|
2019-12-20 16:10:02 -05:00
|
|
|
struct texture_size {
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
} * texture_sizes;
|
2019-06-09 14:25:34 -04:00
|
|
|
|
2019-12-20 16:10:02 -05:00
|
|
|
/// 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;
|
2019-06-09 14:25:34 -04:00
|
|
|
|
2019-07-02 12:42:23 -04:00
|
|
|
/// How much do we need to resize the damaged region for blurring.
|
|
|
|
int resize_width, resize_height;
|
|
|
|
|
2019-06-09 14:25:34 -04:00
|
|
|
int npasses;
|
|
|
|
};
|
|
|
|
|
|
|
|
static GLint glGetUniformLocationChecked(GLuint p, const char *name) {
|
|
|
|
auto ret = glGetUniformLocation(p, name);
|
|
|
|
if (ret < 0) {
|
2020-03-07 12:24:42 -05:00
|
|
|
log_info("Failed to get location of uniform '%s'. This is normal when "
|
|
|
|
"using custom shaders.",
|
|
|
|
name);
|
2019-06-09 14:25:34 -04:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
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.
|
|
|
|
*/
|
2018-12-30 20:32:31 -05:00
|
|
|
GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str) {
|
2018-10-03 17:46:18 -04:00
|
|
|
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];
|
2019-03-30 05:07:21 -04:00
|
|
|
int count = 0;
|
|
|
|
if (vert_shader) {
|
2018-10-03 17:46:18 -04:00
|
|
|
shaders[count++] = vert_shader;
|
2019-03-30 05:07:21 -04:00
|
|
|
}
|
|
|
|
if (frag_shader) {
|
2018-10-03 17:46:18 -04:00
|
|
|
shaders[count++] = frag_shader;
|
2019-03-30 05:07:21 -04:00
|
|
|
}
|
|
|
|
if (count) {
|
2018-10-03 17:46:18 -04:00
|
|
|
prog = gl_create_program(shaders, count);
|
2019-03-30 05:07:21 -04:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vert_shader)
|
|
|
|
glDeleteShader(vert_shader);
|
|
|
|
if (frag_shader)
|
|
|
|
glDeleteShader(frag_shader);
|
|
|
|
|
|
|
|
return prog;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
static void gl_free_prog_main(gl_win_shader_t *pprogram) {
|
2018-12-30 20:32:31 -05:00
|
|
|
if (!pprogram)
|
|
|
|
return;
|
|
|
|
if (pprogram->prog) {
|
|
|
|
glDeleteProgram(pprogram->prog);
|
|
|
|
pprogram->prog = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-05 18:34:57 -05:00
|
|
|
/*
|
|
|
|
* @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
|
2019-11-09 21:47:59 -05:00
|
|
|
* be read-only. In this case we will select auxiliary_texture as
|
2019-11-05 18:34:57 -05:00
|
|
|
* 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.
|
|
|
|
*/
|
2019-11-09 21:47:59 -05:00
|
|
|
static GLuint
|
|
|
|
_gl_average_texture_color(backend_t *base, GLuint source_texture, GLuint destination_texture,
|
|
|
|
GLuint auxiliary_texture, GLuint fbo, int width, int height) {
|
2019-11-05 18:34:57 -05:00
|
|
|
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[] = {
|
2019-11-09 21:47:59 -05:00
|
|
|
// top left
|
|
|
|
0, 0, // vertex coord
|
|
|
|
0, 0, // texture coord
|
2019-11-05 18:34:57 -05:00
|
|
|
|
2019-11-09 21:47:59 -05:00
|
|
|
// top right
|
|
|
|
to_width, 0, // vertex coord
|
|
|
|
width, 0, // texture coord
|
2019-11-05 18:34:57 -05:00
|
|
|
|
2019-11-09 21:47:59 -05:00
|
|
|
// bottom right
|
|
|
|
to_width, to_height, // vertex coord
|
|
|
|
width, height, // texture coord
|
2019-11-05 18:34:57 -05:00
|
|
|
|
2019-11-09 21:47:59 -05:00
|
|
|
// bottom left
|
|
|
|
0, to_height, // vertex coord
|
|
|
|
0, height, // texture coord
|
2019-11-05 18:34:57 -05:00
|
|
|
};
|
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, (long)sizeof(*coord) * 16, coord);
|
|
|
|
|
|
|
|
// Prepare framebuffer for new render iteration
|
|
|
|
glBindTexture(GL_TEXTURE_2D, destination_texture);
|
2019-11-09 21:47:59 -05:00
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
|
|
destination_texture, 0);
|
2021-06-24 12:38:44 -04:00
|
|
|
gl_check_fb_complete(GL_FRAMEBUFFER);
|
2019-11-05 18:34:57 -05:00
|
|
|
|
|
|
|
// 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;
|
2019-11-09 21:47:59 -05:00
|
|
|
GLuint new_destination_texture =
|
|
|
|
auxiliary_texture != 0 ? auxiliary_texture : source_texture;
|
2019-11-05 18:34:57 -05:00
|
|
|
result = _gl_average_texture_color(base, new_source_texture,
|
2019-11-09 21:47:59 -05:00
|
|
|
new_destination_texture, 0, fbo,
|
|
|
|
to_width, to_height);
|
2019-11-05 18:34:57 -05:00
|
|
|
} 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).
|
2019-11-09 21:47:59 -05:00
|
|
|
* Returned texture must not be deleted, since it's owned by the gl_image. It will be
|
|
|
|
* deleted when the gl_image is released.
|
2019-11-05 18:34:57 -05:00
|
|
|
*/
|
2021-06-13 22:22:13 -04:00
|
|
|
static GLuint gl_average_texture_color(backend_t *base, struct backend_image *img) {
|
2020-03-21 11:18:02 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
2021-06-22 15:57:53 -04:00
|
|
|
auto inner = (struct gl_texture *)img->inner;
|
2019-11-05 18:34:57 -05:00
|
|
|
|
|
|
|
// Prepare textures which will be used for destination and source of rendering
|
|
|
|
// during downscaling.
|
2021-06-13 22:22:13 -04:00
|
|
|
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);
|
2019-11-09 21:47:59 -05:00
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
for (int i = 0; i < texture_count; i++) {
|
2021-06-13 22:22:13 -04:00
|
|
|
glBindTexture(GL_TEXTURE_2D, inner->auxiliary_texture[i]);
|
2019-11-09 21:47:59 -05:00
|
|
|
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});
|
2021-06-13 22:22:13 -04:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, inner->width,
|
|
|
|
inner->height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);
|
2019-11-09 21:47:59 -05:00
|
|
|
}
|
2019-11-05 18:34:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2019-11-09 21:47:59 -05:00
|
|
|
glUniform2f(glGetUniformLocationChecked(gd->brightness_shader.prog, "texsize"),
|
2021-06-13 22:22:13 -04:00
|
|
|
(GLfloat)inner->width, (GLfloat)inner->height);
|
2019-11-05 18:34:57 -05:00
|
|
|
|
|
|
|
// 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);
|
2019-11-09 21:47:59 -05:00
|
|
|
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
|
2019-11-05 18:34:57 -05:00
|
|
|
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);
|
2019-11-09 21:47:59 -05:00
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
|
|
|
|
GL_STATIC_DRAW);
|
2019-11-05 18:34:57 -05:00
|
|
|
|
|
|
|
// Do actual recursive render to 1x1 texture
|
2019-11-09 21:47:59 -05:00
|
|
|
GLuint result_texture = _gl_average_texture_color(
|
2021-06-13 22:22:13 -04:00
|
|
|
base, inner->texture, inner->auxiliary_texture[0],
|
|
|
|
inner->auxiliary_texture[1], fbo, inner->width, inner->height);
|
2019-11-05 18:34:57 -05:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
/**
|
2019-03-08 20:39:18 -05:00
|
|
|
* Render a region with texture data.
|
|
|
|
*
|
|
|
|
* @param ptex the texture
|
2019-05-06 17:09:03 -04:00
|
|
|
* @param target the framebuffer to render into
|
2019-03-08 20:39:18 -05:00
|
|
|
* @param dst_x,dst_y the top left corner of region where this texture
|
2019-05-06 17:09:03 -04:00
|
|
|
* should go. In OpenGL coordinate system (important!).
|
|
|
|
* @param reg_tgt the clip region, in Xorg coordinate system
|
2019-03-08 20:39:18 -05:00
|
|
|
* @param reg_visible ignored
|
2018-10-03 17:46:18 -04:00
|
|
|
*/
|
2021-06-13 22:22:13 -04:00
|
|
|
static void _gl_compose(backend_t *base, struct backend_image *img, GLuint target,
|
2019-07-14 11:21:22 -04:00
|
|
|
GLint *coord, GLuint *indices, int nrects) {
|
2020-03-21 11:18:02 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
2021-06-13 22:22:13 -04:00
|
|
|
auto inner = (struct gl_texture *)img->inner;
|
|
|
|
if (!img || !inner->texture) {
|
2018-10-03 17:46:18 -04:00
|
|
|
log_error("Missing texture.");
|
2019-03-08 20:39:18 -05:00
|
|
|
return;
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
2019-11-06 19:19:20 -05:00
|
|
|
GLuint brightness = 0;
|
2019-11-09 21:47:59 -05:00
|
|
|
if (img->max_brightness < 1.0) {
|
2019-11-06 19:19:20 -05:00
|
|
|
brightness = gl_average_texture_color(base, img);
|
2019-11-09 21:47:59 -05:00
|
|
|
}
|
2019-11-05 18:34:57 -05:00
|
|
|
|
2019-04-01 14:55:11 -04:00
|
|
|
assert(gd->win_shader.prog);
|
|
|
|
glUseProgram(gd->win_shader.prog);
|
|
|
|
if (gd->win_shader.unifm_opacity >= 0) {
|
2019-05-06 17:09:03 -04:00
|
|
|
glUniform1f(gd->win_shader.unifm_opacity, (float)img->opacity);
|
2019-04-01 14:55:11 -04:00
|
|
|
}
|
|
|
|
if (gd->win_shader.unifm_invert_color >= 0) {
|
2019-05-06 17:09:03 -04:00
|
|
|
glUniform1i(gd->win_shader.unifm_invert_color, img->color_inverted);
|
2019-04-01 14:55:11 -04:00
|
|
|
}
|
|
|
|
if (gd->win_shader.unifm_tex >= 0) {
|
|
|
|
glUniform1i(gd->win_shader.unifm_tex, 0);
|
|
|
|
}
|
|
|
|
if (gd->win_shader.unifm_dim >= 0) {
|
2019-05-06 17:09:03 -04:00
|
|
|
glUniform1f(gd->win_shader.unifm_dim, (float)img->dim);
|
2019-01-27 19:02:54 -05:00
|
|
|
}
|
2019-11-05 18:34:57 -05:00
|
|
|
if (gd->win_shader.unifm_brightness >= 0) {
|
|
|
|
glUniform1i(gd->win_shader.unifm_brightness, 1);
|
|
|
|
}
|
|
|
|
if (gd->win_shader.unifm_max_brightness >= 0) {
|
2019-11-06 19:19:20 -05:00
|
|
|
glUniform1f(gd->win_shader.unifm_max_brightness, (float)img->max_brightness);
|
2019-11-05 18:34:57 -05:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
// 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
|
2019-11-05 18:34:57 -05:00
|
|
|
glActiveTexture(GL_TEXTURE1);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, brightness);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2021-06-13 22:22:13 -04:00
|
|
|
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
2019-05-06 17:09:03 -04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2019-06-23 20:25:21 -04:00
|
|
|
glEnableVertexAttribArray(vert_coord_loc);
|
|
|
|
glEnableVertexAttribArray(vert_in_texcoord_loc);
|
2019-07-14 11:21:22 -04:00
|
|
|
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));
|
2019-05-06 17:09:03 -04:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target);
|
|
|
|
glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
|
2020-04-24 08:28:10 -04:00
|
|
|
|
2019-06-23 20:25:21 -04:00
|
|
|
glDisableVertexAttribArray(vert_coord_loc);
|
|
|
|
glDisableVertexAttribArray(vert_in_texcoord_loc);
|
2019-05-06 17:09:03 -04:00
|
|
|
glBindVertexArray(0);
|
|
|
|
glDeleteVertexArrays(1, &vao);
|
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2019-07-24 19:37:43 -04:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
glDrawBuffer(GL_BACK);
|
2019-05-06 17:09:03 -04:00
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
glDeleteBuffers(2, bo);
|
|
|
|
|
|
|
|
glUseProgram(0);
|
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-06 19:31:54 -04:00
|
|
|
/// 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
|
2019-07-24 19:37:43 -04:00
|
|
|
/// @param[in] texture_height height of the OpenGL texture
|
2019-05-06 19:31:54 -04:00
|
|
|
/// @param[in] root_height height of the back buffer
|
|
|
|
/// @param[in] y_inverted whether the texture is y inverted
|
|
|
|
/// @param[out] coord, indices output
|
2019-07-24 21:27:02 -04:00
|
|
|
static void
|
2019-07-24 19:37:43 -04:00
|
|
|
x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int texture_height,
|
2019-07-24 21:27:02 -04:00
|
|
|
int root_height, bool y_inverted, GLint *coord, GLuint *indices) {
|
2019-05-06 19:31:54 -04:00
|
|
|
dst_y = root_height - dst_y;
|
|
|
|
if (y_inverted) {
|
2019-07-24 19:37:43 -04:00
|
|
|
dst_y -= texture_height;
|
2019-05-06 17:09:03 -04:00
|
|
|
}
|
|
|
|
|
2019-05-06 19:31:54 -04:00
|
|
|
for (int i = 0; i < nrects; i++) {
|
2019-03-08 20:39:18 -05:00
|
|
|
// Y-flip. Note after this, crect.y1 > crect.y2
|
2019-04-01 14:55:11 -04:00
|
|
|
rect_t crect = rects[i];
|
2019-05-06 19:31:54 -04:00
|
|
|
crect.y1 = root_height - crect.y1;
|
|
|
|
crect.y2 = root_height - crect.y2;
|
2019-03-08 20:39:18 -05:00
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
// Calculate texture coordinates
|
2019-03-08 20:39:18 -05:00
|
|
|
// (texture_x1, texture_y1), texture coord for the _bottom left_ corner
|
2019-07-14 11:21:22 -04:00
|
|
|
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);
|
2019-03-08 20:39:18 -05:00
|
|
|
|
|
|
|
// X pixmaps might be Y inverted, invert the texture coordinates
|
2019-05-06 19:31:54 -04:00
|
|
|
if (y_inverted) {
|
2019-07-24 19:37:43 -04:00
|
|
|
texture_y1 = texture_height - texture_y1;
|
|
|
|
texture_y2 = texture_height - texture_y2;
|
2019-03-08 20:39:18 -05:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
// Vertex coordinates
|
2019-07-14 11:21:22 -04:00
|
|
|
auto vx1 = crect.x1;
|
|
|
|
auto vy1 = crect.y2;
|
|
|
|
auto vx2 = crect.x2;
|
|
|
|
auto vy2 = crect.y1;
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
// log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d",
|
|
|
|
// ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
|
|
|
|
|
2019-04-01 14:55:11 -04:00
|
|
|
memcpy(&coord[i * 16],
|
2020-10-21 21:25:19 -04:00
|
|
|
((GLint[][2]){
|
2019-04-01 14:55:11 -04:00
|
|
|
{vx1, vy1},
|
|
|
|
{texture_x1, texture_y1},
|
|
|
|
{vx2, vy1},
|
|
|
|
{texture_x2, texture_y1},
|
|
|
|
{vx2, vy2},
|
|
|
|
{texture_x2, texture_y2},
|
|
|
|
{vx1, vy2},
|
|
|
|
{texture_x1, texture_y2},
|
2020-10-21 21:25:19 -04:00
|
|
|
}),
|
2019-07-14 11:21:22 -04:00
|
|
|
sizeof(GLint[2]) * 8);
|
2019-04-01 14:55:11 -04:00
|
|
|
|
|
|
|
GLuint u = (GLuint)(i * 4);
|
2020-10-21 21:25:19 -04:00
|
|
|
memcpy(&indices[i * 6],
|
|
|
|
((GLuint[]){u + 0, u + 1, u + 2, u + 2, u + 3, u + 0}),
|
2019-04-01 14:55:11 -04:00
|
|
|
sizeof(GLuint) * 6);
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
2019-05-06 19:31:54 -04:00
|
|
|
}
|
|
|
|
|
2020-08-30 10:25:58 -04:00
|
|
|
// TODO(yshui) make use of reg_visible
|
2019-05-06 19:31:54 -04:00
|
|
|
void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y,
|
2019-07-24 21:27:02 -04:00
|
|
|
const region_t *reg_tgt, const region_t *reg_visible attr_unused) {
|
2020-03-21 11:18:02 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
2021-06-13 22:22:13 -04:00
|
|
|
struct backend_image *img = image_data;
|
|
|
|
auto inner = (struct gl_texture *)img->inner;
|
2019-05-06 19:31:54 -04:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2019-07-14 11:21:22 -04:00
|
|
|
auto coord = ccalloc(nrects * 16, GLint);
|
2019-05-06 19:31:54 -04:00
|
|
|
auto indices = ccalloc(nrects * 6, GLuint);
|
2021-06-13 22:22:13 -04:00
|
|
|
x_rect_to_coords(nrects, rects, dst_x, dst_y, inner->height, gd->height,
|
|
|
|
inner->y_inverted, coord, indices);
|
2019-07-24 19:37:43 -04:00
|
|
|
_gl_compose(base, img, gd->back_fbo, coord, indices, nrects);
|
2019-04-01 14:55:11 -04:00
|
|
|
|
|
|
|
free(indices);
|
|
|
|
free(coord);
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Blur contents in a particular region.
|
|
|
|
*/
|
2019-12-20 14:46:36 -05:00
|
|
|
bool gl_kernel_blur(backend_t *base, double opacity, void *ctx, const rect_t *extent,
|
2020-09-11 14:11:11 -04:00
|
|
|
const GLuint vao[2], const int vao_nelems[2]) {
|
2019-12-20 14:46:36 -05:00
|
|
|
auto bctx = (struct gl_blur_context *)ctx;
|
|
|
|
auto gd = (struct gl_data *)base;
|
|
|
|
|
|
|
|
int dst_y_screen_coord = gd->height - extent->y2,
|
|
|
|
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, texorig_y;
|
|
|
|
GLint tex_width, tex_height;
|
|
|
|
GLuint src_texture;
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
texorig_x = extent->x1;
|
|
|
|
texorig_y = dst_y_screen_coord;
|
|
|
|
src_texture = gd->back_texture;
|
|
|
|
tex_width = gd->width;
|
|
|
|
tex_height = gd->height;
|
|
|
|
} else {
|
|
|
|
texorig_x = extent->x1 + bctx->resize_width;
|
|
|
|
texorig_y = dst_y_fb_coord - bctx->resize_height;
|
|
|
|
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);
|
2020-09-11 14:11:11 -04:00
|
|
|
|
|
|
|
// The number of indices in the selected vertex array
|
|
|
|
GLsizei nelems;
|
|
|
|
|
2019-12-20 14:46:36 -05:00
|
|
|
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]);
|
2020-09-11 14:11:11 -04:00
|
|
|
nelems = vao_nelems[1];
|
2019-12-20 14:46:36 -05:00
|
|
|
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);
|
2021-06-24 12:38:44 -04:00
|
|
|
if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
|
2019-12-20 14:46:36 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
glUniform1f(p->unifm_opacity, 1.0);
|
|
|
|
glUniform2f(p->orig_loc, (GLfloat)bctx->resize_width,
|
|
|
|
-(GLfloat)bctx->resize_height);
|
|
|
|
} else {
|
|
|
|
// last pass, draw directly into the back buffer, with origin
|
|
|
|
// regions
|
|
|
|
glBindVertexArray(vao[0]);
|
2020-09-11 14:11:11 -04:00
|
|
|
nelems = vao_nelems[0];
|
2019-12-20 14:46:36 -05:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
|
|
|
|
|
|
|
|
glUniform1f(p->unifm_opacity, (float)opacity);
|
|
|
|
glUniform2f(p->orig_loc, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
glUniform2f(p->texorig_loc, (GLfloat)texorig_x, (GLfloat)texorig_y);
|
2020-09-11 14:11:11 -04:00
|
|
|
glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
|
2019-12-20 14:46:36 -05:00
|
|
|
|
|
|
|
// XXX use multiple draw calls is probably going to be slow than
|
|
|
|
// just simply blur the whole area.
|
|
|
|
|
|
|
|
curr = !curr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-20 15:33:37 -05:00
|
|
|
bool gl_dual_kawase_blur(backend_t *base, double opacity, void *ctx, const rect_t *extent,
|
2020-09-11 14:11:11 -04:00
|
|
|
const GLuint vao[2], const int vao_nelems[2]) {
|
2019-12-20 15:33:37 -05:00
|
|
|
auto bctx = (struct gl_blur_context *)ctx;
|
|
|
|
auto gd = (struct gl_data *)base;
|
|
|
|
|
|
|
|
int dst_y_screen_coord = gd->height - extent->y2,
|
|
|
|
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);
|
|
|
|
|
|
|
|
// Downsample always renders with resize offset
|
|
|
|
glUniform2f(down_pass->orig_loc, (GLfloat)bctx->resize_width,
|
|
|
|
-(GLfloat)bctx->resize_height);
|
|
|
|
|
|
|
|
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;
|
|
|
|
int texorig_x, texorig_y;
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
// first pass: copy from back buffer
|
|
|
|
src_texture = gd->back_texture;
|
|
|
|
tex_width = gd->width;
|
|
|
|
tex_height = gd->height;
|
2019-12-20 14:46:36 -05:00
|
|
|
|
2019-12-20 15:33:37 -05:00
|
|
|
texorig_x = extent->x1;
|
|
|
|
texorig_y = dst_y_screen_coord;
|
|
|
|
} 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;
|
|
|
|
|
|
|
|
texorig_x = extent->x1 + bctx->resize_width;
|
|
|
|
texorig_y = dst_y_fb_coord - bctx->resize_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(src_texture);
|
|
|
|
assert(bctx->blur_fbos[i]);
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, src_texture);
|
|
|
|
glBindVertexArray(vao[1]);
|
2020-09-11 14:11:11 -04:00
|
|
|
auto nelems = vao_nelems[1];
|
2019-12-20 15:33:37 -05:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i]);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
|
|
|
|
glUniform2f(down_pass->texorig_loc, (GLfloat)texorig_x, (GLfloat)texorig_y);
|
|
|
|
glUniform1f(down_pass->scale_loc, (GLfloat)scale_factor);
|
|
|
|
|
|
|
|
glUniform2f(down_pass->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
|
|
|
|
1.0f / (GLfloat)tex_height);
|
|
|
|
|
2020-09-11 14:11:11 -04:00
|
|
|
glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
|
2019-12-20 15:33:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Kawase upsample pass
|
|
|
|
const gl_blur_shader_t *up_pass = &bctx->blur_shader[1];
|
|
|
|
assert(up_pass->prog);
|
|
|
|
glUseProgram(up_pass->prog);
|
|
|
|
|
|
|
|
// Upsample always samples from textures with resize offset
|
|
|
|
glUniform2f(up_pass->texorig_loc, (GLfloat)(extent->x1 + bctx->resize_width),
|
|
|
|
(GLfloat)(dst_y_fb_coord - bctx->resize_height));
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-09-11 14:11:11 -04:00
|
|
|
// The number of indices in the selected vertex array
|
|
|
|
GLsizei nelems;
|
|
|
|
|
2019-12-20 15:33:37 -05:00
|
|
|
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]);
|
2020-09-11 14:11:11 -04:00
|
|
|
nelems = vao_nelems[1];
|
2019-12-20 15:33:37 -05:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i - 1]);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
|
|
|
|
glUniform2f(up_pass->orig_loc, (GLfloat)bctx->resize_width,
|
|
|
|
-(GLfloat)bctx->resize_height);
|
|
|
|
glUniform1f(up_pass->unifm_opacity, (GLfloat)1);
|
|
|
|
} else {
|
|
|
|
// last pass, draw directly into the back buffer
|
|
|
|
glBindVertexArray(vao[0]);
|
2020-09-11 14:11:11 -04:00
|
|
|
nelems = vao_nelems[0];
|
2019-12-20 15:33:37 -05:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
|
|
|
|
|
|
|
|
glUniform2f(up_pass->orig_loc, (GLfloat)0, (GLfloat)0);
|
|
|
|
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);
|
|
|
|
|
2020-09-11 14:11:11 -04:00
|
|
|
glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
|
2019-12-20 15:33:37 -05:00
|
|
|
}
|
2019-12-20 14:46:36 -05:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-09 14:25:34 -04:00
|
|
|
bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blur,
|
2019-07-24 21:27:02 -04:00
|
|
|
const region_t *reg_visible attr_unused) {
|
2019-12-20 14:46:36 -05:00
|
|
|
auto bctx = (struct gl_blur_context *)ctx;
|
2020-03-21 11:18:02 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
2019-06-09 14:25:34 -04:00
|
|
|
|
2019-12-20 14:46:36 -05:00
|
|
|
bool ret = false;
|
|
|
|
|
2019-12-20 16:10:02 -05:00
|
|
|
if (gd->width + bctx->resize_width * 2 != bctx->fb_width ||
|
|
|
|
gd->height + bctx->resize_height * 2 != bctx->fb_height) {
|
2019-06-09 14:25:34 -04:00
|
|
|
// Resize the temporary textures used for blur in case the root
|
|
|
|
// size changed
|
2019-12-20 16:10:02 -05:00
|
|
|
bctx->fb_width = gd->width + bctx->resize_width * 2;
|
|
|
|
bctx->fb_height = gd->height + bctx->resize_height * 2;
|
|
|
|
|
|
|
|
for (int i = 0; i < bctx->blur_texture_count; ++i) {
|
|
|
|
auto tex_size = bctx->texture_sizes + i;
|
2019-12-20 14:46:36 -05:00
|
|
|
if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
|
2019-12-20 15:33:37 -05:00
|
|
|
// 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));
|
2019-12-20 14:46:36 -05:00
|
|
|
} else {
|
|
|
|
tex_size->width = bctx->fb_width;
|
|
|
|
tex_size->height = bctx->fb_height;
|
|
|
|
}
|
2019-12-20 16:10:02 -05:00
|
|
|
|
|
|
|
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);
|
2019-12-20 15:33:37 -05:00
|
|
|
|
|
|
|
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);
|
2021-06-24 12:38:44 -04:00
|
|
|
if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
|
2019-12-20 15:33:37 -05:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2019-12-20 16:10:02 -05:00
|
|
|
}
|
2019-12-20 15:33:37 -05:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
2019-06-09 14:25:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remainder: regions are in Xorg coordinates
|
2019-07-02 12:42:23 -04:00
|
|
|
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(®_blur_resized);
|
2019-07-24 19:37:43 -04:00
|
|
|
int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1;
|
2019-03-09 20:22:38 -05:00
|
|
|
if (width == 0 || height == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-07-02 12:42:23 -04:00
|
|
|
int nrects, nrects_resized;
|
|
|
|
const rect_t *rects = pixman_region32_rectangles((region_t *)reg_blur, &nrects),
|
|
|
|
*rects_resized =
|
|
|
|
pixman_region32_rectangles(®_blur_resized, &nrects_resized);
|
|
|
|
if (!nrects || !nrects_resized) {
|
2019-04-01 14:43:24 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-14 11:21:22 -04:00
|
|
|
auto coord = ccalloc(nrects * 16, GLint);
|
2019-04-01 14:43:24 -04:00
|
|
|
auto indices = ccalloc(nrects * 6, GLuint);
|
2019-07-02 12:42:23 -04:00
|
|
|
x_rect_to_coords(nrects, rects, extent_resized->x1, extent_resized->y2,
|
2019-12-20 16:10:02 -05:00
|
|
|
bctx->fb_height, gd->height, false, coord, indices);
|
2019-07-02 12:42:23 -04:00
|
|
|
|
2019-07-14 11:21:22 -04:00
|
|
|
auto coord_resized = ccalloc(nrects_resized * 16, GLint);
|
2019-07-02 12:42:23 -04:00
|
|
|
auto indices_resized = ccalloc(nrects_resized * 6, GLuint);
|
|
|
|
x_rect_to_coords(nrects_resized, rects_resized, extent_resized->x1,
|
2019-12-20 16:10:02 -05:00
|
|
|
extent_resized->y2, bctx->fb_height, bctx->fb_height, false,
|
|
|
|
coord_resized, indices_resized);
|
2019-07-07 14:09:15 -04:00
|
|
|
pixman_region32_fini(®_blur_resized);
|
2019-07-02 12:42:23 -04:00
|
|
|
|
|
|
|
GLuint vao[2];
|
|
|
|
glGenVertexArrays(2, vao);
|
|
|
|
GLuint bo[4];
|
|
|
|
glGenBuffers(4, bo);
|
|
|
|
|
|
|
|
glBindVertexArray(vao[0]);
|
2019-04-01 14:43:24 -04:00
|
|
|
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);
|
2019-06-23 20:51:50 -04:00
|
|
|
glEnableVertexAttribArray(vert_coord_loc);
|
|
|
|
glEnableVertexAttribArray(vert_in_texcoord_loc);
|
2019-07-14 11:21:22 -04:00
|
|
|
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
|
|
|
|
glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
|
2019-07-25 20:19:55 -04:00
|
|
|
sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
|
2019-04-01 14:43:24 -04:00
|
|
|
|
2019-07-02 12:42:23 -04:00
|
|
|
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);
|
2019-07-14 11:21:22 -04:00
|
|
|
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
|
|
|
|
glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
|
2019-07-25 20:19:55 -04:00
|
|
|
sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
|
2019-07-02 12:42:23 -04:00
|
|
|
|
2020-09-11 14:11:11 -04:00
|
|
|
int vao_nelems[2] = {nrects * 6, nrects_resized * 6};
|
|
|
|
|
2019-12-20 14:46:36 -05:00
|
|
|
if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
|
2020-09-11 14:11:11 -04:00
|
|
|
ret = gl_dual_kawase_blur(base, opacity, ctx, extent_resized, vao, vao_nelems);
|
2019-12-20 14:46:36 -05:00
|
|
|
} else {
|
2020-09-11 14:11:11 -04:00
|
|
|
ret = gl_kernel_blur(base, opacity, ctx, extent_resized, vao, vao_nelems);
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
2019-03-10 16:38:34 -04:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2019-04-01 14:43:24 -04:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
2019-07-02 12:42:23 -04:00
|
|
|
glDeleteBuffers(4, bo);
|
2019-04-01 14:57:49 -04:00
|
|
|
glBindVertexArray(0);
|
2019-07-02 12:42:23 -04:00
|
|
|
glDeleteVertexArrays(2, vao);
|
2019-12-20 14:46:36 -05:00
|
|
|
glUseProgram(0);
|
2019-04-01 14:43:24 -04:00
|
|
|
|
|
|
|
free(indices);
|
|
|
|
free(coord);
|
2019-07-07 14:09:15 -04:00
|
|
|
free(indices_resized);
|
|
|
|
free(coord_resized);
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-13 19:26:24 -04:00
|
|
|
// clang-format off
|
2019-04-01 14:59:54 -04:00
|
|
|
const char *vertex_shader = GLSL(330,
|
2019-03-13 19:26:24 -04:00
|
|
|
uniform mat4 projection;
|
2019-12-20 15:33:37 -05:00
|
|
|
uniform float scale = 1.0;
|
2019-04-01 14:43:24 -04:00
|
|
|
uniform vec2 orig;
|
2019-07-24 19:37:43 -04:00
|
|
|
uniform vec2 texorig;
|
2019-06-23 20:25:21 -04:00
|
|
|
layout(location = 0) in vec2 coord;
|
|
|
|
layout(location = 1) in vec2 in_texcoord;
|
2019-03-13 19:26:24 -04:00
|
|
|
out vec2 texcoord;
|
|
|
|
void main() {
|
2019-12-20 15:33:37 -05:00
|
|
|
gl_Position = projection * vec4(coord + orig, 0, scale);
|
2019-07-24 19:37:43 -04:00
|
|
|
texcoord = in_texcoord + texorig;
|
2019-03-13 19:26:24 -04:00
|
|
|
}
|
|
|
|
);
|
|
|
|
// clang-format on
|
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
/**
|
|
|
|
* Load a GLSL main program from shader strings.
|
|
|
|
*/
|
2019-03-09 09:12:53 -05:00
|
|
|
static int gl_win_shader_from_string(const char *vshader_str, const char *fshader_str,
|
|
|
|
gl_win_shader_t *ret) {
|
2018-10-03 17:46:18 -04:00
|
|
|
// 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");
|
2019-03-09 21:25:41 -05:00
|
|
|
ret->unifm_dim = glGetUniformLocationChecked(ret->prog, "dim");
|
2019-11-05 18:34:57 -05:00
|
|
|
ret->unifm_brightness = glGetUniformLocationChecked(ret->prog, "brightness");
|
2019-11-09 21:47:59 -05:00
|
|
|
ret->unifm_max_brightness =
|
|
|
|
glGetUniformLocationChecked(ret->prog, "max_brightness");
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-04-01 14:43:24 -04:00
|
|
|
glUseProgram(ret->prog);
|
|
|
|
int orig_loc = glGetUniformLocation(ret->prog, "orig");
|
|
|
|
glUniform2f(orig_loc, 0, 0);
|
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
gl_check_err();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback to run on root window size change.
|
|
|
|
*/
|
2019-03-08 20:39:18 -05:00
|
|
|
void gl_resize(struct gl_data *gd, int width, int height) {
|
2020-04-24 14:14:35 -04:00
|
|
|
GLint viewport_dimensions[2];
|
|
|
|
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
gd->height = height;
|
|
|
|
gd->width = width;
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2020-04-24 14:14:35 -04:00
|
|
|
assert(viewport_dimensions[0] >= gd->width);
|
|
|
|
assert(viewport_dimensions[1] >= gd->height);
|
2019-11-05 18:34:57 -05:00
|
|
|
|
2019-07-24 19:37:43 -04:00
|
|
|
glBindTexture(GL_TEXTURE_2D, gd->back_texture);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_BGR,
|
|
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
|
|
|
2019-03-13 19:26:24 -04:00
|
|
|
gl_check_err();
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
2018-12-31 02:16:35 -05:00
|
|
|
|
2019-04-01 14:34:02 -04:00
|
|
|
// clang-format off
|
2019-07-24 18:55:31 -04:00
|
|
|
static const char dummy_frag[] = GLSL(330,
|
|
|
|
uniform sampler2D tex;
|
|
|
|
in vec2 texcoord;
|
|
|
|
void main() {
|
|
|
|
gl_FragColor = texelFetch(tex, ivec2(texcoord.xy), 0);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2019-04-01 14:59:54 -04:00
|
|
|
static const char fill_frag[] = GLSL(330,
|
2019-04-01 14:34:02 -04:00
|
|
|
uniform vec4 color;
|
|
|
|
void main() {
|
|
|
|
gl_FragColor = color;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2019-04-01 14:59:54 -04:00
|
|
|
static const char fill_vert[] = GLSL(330,
|
2019-06-23 20:25:21 -04:00
|
|
|
layout(location = 0) in vec2 in_coord;
|
2019-04-01 14:34:02 -04:00
|
|
|
uniform mat4 projection;
|
|
|
|
void main() {
|
|
|
|
gl_Position = projection * vec4(in_coord, 0, 1);
|
|
|
|
}
|
|
|
|
);
|
2019-11-05 18:34:57 -05:00
|
|
|
|
|
|
|
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;
|
2019-11-09 21:47:59 -05:00
|
|
|
uniform vec2 texsize;
|
2019-11-05 18:34:57 -05:00
|
|
|
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);
|
2019-11-09 21:47:59 -05:00
|
|
|
texcoord = in_texcoord / texsize;
|
2019-11-05 18:34:57 -05:00
|
|
|
}
|
|
|
|
);
|
2019-04-01 14:34:02 -04:00
|
|
|
// clang-format on
|
|
|
|
|
2019-05-10 03:34:58 -04:00
|
|
|
/// Fill a given region in bound framebuffer.
|
|
|
|
/// @param[in] y_inverted whether the y coordinates in `clip` should be inverted
|
2019-07-24 19:37:43 -04:00
|
|
|
static void _gl_fill(backend_t *base, struct color c, const region_t *clip, GLuint target,
|
|
|
|
int height, bool y_inverted) {
|
2019-06-23 20:25:21 -04:00
|
|
|
static const GLuint fill_vert_in_coord_loc = 0;
|
2019-03-09 20:51:21 -05:00
|
|
|
int nrects;
|
|
|
|
const rect_t *rect = pixman_region32_rectangles((region_t *)clip, &nrects);
|
2020-03-21 11:18:02 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
2019-04-01 14:57:49 -04:00
|
|
|
|
|
|
|
GLuint vao;
|
|
|
|
glGenVertexArrays(1, &vao);
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
|
2019-04-01 14:34:02 -04:00
|
|
|
GLuint bo[2];
|
|
|
|
glGenBuffers(2, bo);
|
|
|
|
glUseProgram(gd->fill_shader.prog);
|
2019-05-10 03:34:58 -04:00
|
|
|
glUniform4f(gd->fill_shader.color_loc, (GLfloat)c.red, (GLfloat)c.green,
|
|
|
|
(GLfloat)c.blue, (GLfloat)c.alpha);
|
2019-06-23 20:25:21 -04:00
|
|
|
glEnableVertexAttribArray(fill_vert_in_coord_loc);
|
2019-04-01 14:34:02 -04:00
|
|
|
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);
|
2019-03-09 20:51:21 -05:00
|
|
|
for (int i = 0; i < nrects; i++) {
|
2019-05-10 03:34:58 -04:00
|
|
|
GLint y1 = y_inverted ? height - rect[i].y2 : rect[i].y1,
|
|
|
|
y2 = y_inverted ? height - rect[i].y1 : rect[i].y2;
|
2020-10-21 21:25:19 -04:00
|
|
|
// clang-format off
|
2019-04-01 14:34:02 -04:00
|
|
|
memcpy(&coord[i * 8],
|
2020-10-21 21:25:19 -04:00
|
|
|
((GLint[][2]){
|
|
|
|
{rect[i].x1, y1}, {rect[i].x2, y1},
|
|
|
|
{rect[i].x2, y2}, {rect[i].x1, y2}}),
|
2019-04-01 14:34:02 -04:00
|
|
|
sizeof(GLint[2]) * 4);
|
2020-10-21 21:25:19 -04:00
|
|
|
// clang-format on
|
2019-04-01 14:34:02 -04:00
|
|
|
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;
|
2019-03-09 20:51:21 -05:00
|
|
|
}
|
2019-04-01 14:34:02 -04:00
|
|
|
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);
|
|
|
|
|
2019-06-23 20:25:21 -04:00
|
|
|
glVertexAttribPointer(fill_vert_in_coord_loc, 2, GL_INT, GL_FALSE,
|
2019-04-01 14:34:02 -04:00
|
|
|
sizeof(*coord) * 2, (void *)0);
|
2019-07-24 19:37:43 -04:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target);
|
2019-04-01 14:34:02 -04:00
|
|
|
glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
|
2020-04-23 08:20:03 -04:00
|
|
|
|
2019-07-24 19:37:43 -04:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
2019-04-01 14:34:02 -04:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
2019-06-23 20:25:21 -04:00
|
|
|
glDisableVertexAttribArray(fill_vert_in_coord_loc);
|
2019-04-01 14:57:49 -04:00
|
|
|
glBindVertexArray(0);
|
|
|
|
glDeleteVertexArrays(1, &vao);
|
2019-04-01 14:34:02 -04:00
|
|
|
|
|
|
|
glDeleteBuffers(2, bo);
|
2019-07-25 22:53:24 -04:00
|
|
|
free(indices);
|
|
|
|
free(coord);
|
2021-06-24 12:38:44 -04:00
|
|
|
|
|
|
|
gl_check_err();
|
2019-03-09 20:51:21 -05:00
|
|
|
}
|
|
|
|
|
2019-05-10 03:34:58 -04:00
|
|
|
void gl_fill(backend_t *base, struct color c, const region_t *clip) {
|
2020-03-21 11:18:02 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
2019-07-24 19:37:43 -04:00
|
|
|
return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
|
2019-05-10 03:34:58 -04:00
|
|
|
}
|
|
|
|
|
2021-06-13 22:22:13 -04:00
|
|
|
static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
|
2020-03-21 11:18:02 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
2021-06-13 22:22:13 -04:00
|
|
|
gd->release_user_data(base, inner);
|
|
|
|
assert(inner->user_data == NULL);
|
2019-05-06 17:09:03 -04:00
|
|
|
|
2021-06-13 22:22:13 -04:00
|
|
|
glDeleteTextures(1, &inner->texture);
|
|
|
|
glDeleteTextures(2, inner->auxiliary_texture);
|
|
|
|
free(inner);
|
2019-05-06 17:09:03 -04:00
|
|
|
gl_check_err();
|
|
|
|
}
|
|
|
|
|
2021-06-13 22:22:13 -04:00
|
|
|
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);
|
2019-05-06 17:09:03 -04:00
|
|
|
}
|
|
|
|
|
2019-06-09 14:25:34 -04:00
|
|
|
static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
|
|
|
|
if (shader->prog) {
|
|
|
|
glDeleteProgram(shader->prog);
|
|
|
|
}
|
|
|
|
|
|
|
|
shader->prog = 0;
|
|
|
|
}
|
|
|
|
|
2019-07-24 21:27:02 -04:00
|
|
|
void gl_destroy_blur_context(backend_t *base attr_unused, void *ctx) {
|
2019-12-20 14:46:36 -05:00
|
|
|
auto bctx = (struct gl_blur_context *)ctx;
|
2019-06-09 14:25:34 -04:00
|
|
|
// Free GLSL shaders/programs
|
|
|
|
for (int i = 0; i < bctx->npasses; ++i) {
|
|
|
|
gl_free_blur_shader(&bctx->blur_shader[i]);
|
|
|
|
}
|
|
|
|
free(bctx->blur_shader);
|
|
|
|
|
2019-12-20 16:10:02 -05:00
|
|
|
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);
|
2019-06-09 14:25:34 -04:00
|
|
|
}
|
2019-12-20 16:10:02 -05:00
|
|
|
|
|
|
|
bctx->blur_texture_count = 0;
|
|
|
|
bctx->blur_fbo_count = 0;
|
|
|
|
|
2019-06-09 14:25:34 -04:00
|
|
|
free(bctx);
|
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
}
|
|
|
|
|
2018-12-31 02:16:35 -05:00
|
|
|
/**
|
|
|
|
* Initialize GL blur filters.
|
|
|
|
*/
|
2019-12-20 14:46:36 -05:00
|
|
|
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;
|
2019-06-09 14:25:34 -04:00
|
|
|
|
|
|
|
struct conv **kernels;
|
2019-12-20 14:31:00 -05:00
|
|
|
|
2019-07-24 18:55:31 -04:00
|
|
|
int nkernels;
|
2019-06-09 14:25:34 -04:00
|
|
|
ctx->method = BLUR_METHOD_KERNEL;
|
|
|
|
if (method == BLUR_METHOD_KERNEL) {
|
2019-07-24 18:55:31 -04:00
|
|
|
nkernels = ((struct kernel_blur_args *)args)->kernel_count;
|
2019-06-09 14:25:34 -04:00
|
|
|
kernels = ((struct kernel_blur_args *)args)->kernels;
|
|
|
|
} else {
|
2019-07-24 18:55:31 -04:00
|
|
|
kernels = generate_blur_kernel(method, args, &nkernels);
|
2019-06-09 14:25:34 -04:00
|
|
|
}
|
|
|
|
|
2019-07-24 18:55:31 -04:00
|
|
|
if (!nkernels) {
|
2019-06-09 14:25:34 -04:00
|
|
|
ctx->method = BLUR_METHOD_NONE;
|
2019-12-20 14:46:36 -05:00
|
|
|
return true;
|
2019-03-09 20:22:38 -05:00
|
|
|
}
|
2018-12-31 02:16:35 -05:00
|
|
|
|
2019-12-20 14:46:36 -05:00
|
|
|
// Specify required textures and FBOs
|
|
|
|
ctx->blur_texture_count = 2;
|
|
|
|
ctx->blur_fbo_count = 1;
|
2020-04-23 08:20:03 -04:00
|
|
|
|
2019-07-24 18:55:31 -04:00
|
|
|
ctx->blur_shader = ccalloc(max2(2, nkernels), gl_blur_shader_t);
|
2019-06-06 02:37:48 -04:00
|
|
|
|
2018-12-31 02:16:35 -05:00
|
|
|
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");
|
|
|
|
|
2019-03-09 20:22:38 -05:00
|
|
|
// clang-format off
|
2019-04-01 14:59:54 -04:00
|
|
|
static const char *FRAG_SHADER_BLUR = GLSL(330,
|
2019-03-09 20:22:38 -05:00
|
|
|
%s\n // other extension pragmas
|
2020-06-06 15:27:25 -04:00
|
|
|
uniform sampler2D tex_src;
|
|
|
|
uniform vec2 pixel_norm;
|
2019-03-09 21:04:14 -05:00
|
|
|
uniform float opacity;
|
2019-03-13 19:26:24 -04:00
|
|
|
in vec2 texcoord;
|
2019-03-09 20:22:38 -05:00
|
|
|
out vec4 out_color;
|
|
|
|
void main() {
|
2020-06-06 15:27:25 -04:00
|
|
|
vec2 uv = texcoord * pixel_norm;
|
2019-03-09 20:22:38 -05:00
|
|
|
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
|
|
|
|
%s //body of the convolution
|
2019-03-09 21:04:14 -05:00
|
|
|
out_color = sum / float(%.7g) * opacity;
|
2019-03-09 20:22:38 -05:00
|
|
|
}
|
|
|
|
);
|
|
|
|
static const char *FRAG_SHADER_BLUR_ADD = QUOTE(
|
2020-06-07 06:41:32 -04:00
|
|
|
sum += float(%.7g) * texture2D(tex_src, uv + pixel_norm * vec2(%.7g, %.7g));
|
2019-03-09 20:22:38 -05:00
|
|
|
);
|
|
|
|
// clang-format on
|
|
|
|
|
2018-12-31 02:16:35 -05:00
|
|
|
const char *shader_add = FRAG_SHADER_BLUR_ADD;
|
|
|
|
char *extension = strdup("");
|
|
|
|
|
2019-07-24 18:55:31 -04:00
|
|
|
for (int i = 0; i < nkernels; i++) {
|
2019-03-09 20:22:38 -05:00
|
|
|
auto kern = kernels[i];
|
2018-12-31 02:16:35 -05:00
|
|
|
// Build shader
|
2019-02-17 18:47:46 -05:00
|
|
|
int width = kern->w, height = kern->h;
|
2019-05-06 19:29:02 -04:00
|
|
|
int nele = width * height;
|
2020-06-07 06:41:32 -04:00
|
|
|
// '%.7g' is at most 14 characters, inserted 3 times
|
2019-03-30 05:07:21 -04:00
|
|
|
size_t body_len = (strlen(shader_add) + 42) * (uint)nele;
|
2019-03-09 20:22:38 -05:00
|
|
|
char *shader_body = ccalloc(body_len, char);
|
|
|
|
char *pc = shader_body;
|
2018-12-31 02:16:35 -05:00
|
|
|
|
2020-06-07 06:41:32 -04:00
|
|
|
// 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.
|
2018-12-31 02:16:35 -05:00
|
|
|
double sum = 0.0;
|
2020-06-07 06:41:32 -04:00
|
|
|
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);
|
2019-02-17 18:47:46 -05:00
|
|
|
}
|
2018-12-31 02:16:35 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-09 14:25:34 -04:00
|
|
|
auto pass = ctx->blur_shader + i;
|
2019-03-09 20:22:38 -05:00
|
|
|
size_t shader_len = strlen(FRAG_SHADER_BLUR) + strlen(extension) +
|
2019-03-10 16:38:34 -04:00
|
|
|
strlen(shader_body) + 10 /* sum */ +
|
|
|
|
1 /* null terminator */;
|
2019-03-09 20:22:38 -05:00
|
|
|
char *shader_str = ccalloc(shader_len, char);
|
2019-03-31 13:15:14 -04:00
|
|
|
auto real_shader_len = snprintf(shader_str, shader_len, FRAG_SHADER_BLUR,
|
|
|
|
extension, shader_body, sum);
|
2019-03-30 18:57:29 -04:00
|
|
|
CHECK(real_shader_len >= 0);
|
|
|
|
CHECK((size_t)real_shader_len < shader_len);
|
2019-03-09 20:22:38 -05:00
|
|
|
free(shader_body);
|
2018-12-31 02:16:35 -05:00
|
|
|
|
|
|
|
// Build program
|
2019-03-13 19:26:24 -04:00
|
|
|
pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
|
2019-03-09 20:22:38 -05:00
|
|
|
free(shader_str);
|
2018-12-31 02:16:35 -05:00
|
|
|
if (!pass->prog) {
|
|
|
|
log_error("Failed to create GLSL program.");
|
2019-06-09 14:25:34 -04:00
|
|
|
success = false;
|
|
|
|
goto out;
|
2018-12-31 02:16:35 -05:00
|
|
|
}
|
2019-03-09 20:22:38 -05:00
|
|
|
glBindFragDataLocation(pass->prog, 0, "out_color");
|
2018-12-31 02:16:35 -05:00
|
|
|
|
|
|
|
// Get uniform addresses
|
2020-06-06 15:27:25 -04:00
|
|
|
pass->unifm_pixel_norm =
|
|
|
|
glGetUniformLocationChecked(pass->prog, "pixel_norm");
|
2019-03-09 21:25:41 -05:00
|
|
|
pass->unifm_opacity = glGetUniformLocationChecked(pass->prog, "opacity");
|
2019-04-01 14:43:24 -04:00
|
|
|
pass->orig_loc = glGetUniformLocationChecked(pass->prog, "orig");
|
2019-07-24 19:37:43 -04:00
|
|
|
pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig");
|
2020-04-23 08:20:03 -04:00
|
|
|
|
|
|
|
// Setup projection matrix
|
|
|
|
glUseProgram(pass->prog);
|
|
|
|
int pml = glGetUniformLocationChecked(pass->prog, "projection");
|
2019-12-20 14:46:36 -05:00
|
|
|
glUniformMatrix4fv(pml, 1, false, projection);
|
2020-04-23 08:20:03 -04:00
|
|
|
glUseProgram(0);
|
|
|
|
|
2019-07-02 12:42:23 -04:00
|
|
|
ctx->resize_width += kern->w / 2;
|
|
|
|
ctx->resize_height += kern->h / 2;
|
2018-12-31 02:16:35 -05:00
|
|
|
}
|
|
|
|
|
2019-07-24 18:55:31 -04:00
|
|
|
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);
|
2020-06-06 15:27:25 -04:00
|
|
|
pass->unifm_pixel_norm = -1;
|
2020-03-07 12:31:46 -05:00
|
|
|
pass->unifm_opacity = -1;
|
2019-07-24 18:55:31 -04:00
|
|
|
pass->orig_loc = glGetUniformLocationChecked(pass->prog, "orig");
|
2019-07-24 19:37:43 -04:00
|
|
|
pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig");
|
2020-04-23 08:20:03 -04:00
|
|
|
|
|
|
|
// Setup projection matrix
|
|
|
|
glUseProgram(pass->prog);
|
|
|
|
int pml = glGetUniformLocationChecked(pass->prog, "projection");
|
2019-12-20 14:46:36 -05:00
|
|
|
glUniformMatrix4fv(pml, 1, false, projection);
|
2020-04-23 08:20:03 -04:00
|
|
|
glUseProgram(0);
|
|
|
|
|
2019-07-24 18:55:31 -04:00
|
|
|
ctx->npasses = 2;
|
|
|
|
} else {
|
|
|
|
ctx->npasses = nkernels;
|
|
|
|
}
|
|
|
|
|
2019-12-20 14:46:36 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-12-20 15:33:37 -05:00
|
|
|
bool gl_create_dual_kawase_blur_context(void *blur_context, GLfloat *projection,
|
|
|
|
enum blur_method method, void *args) {
|
2019-12-20 14:46:36 -05:00
|
|
|
bool success = false;
|
|
|
|
auto ctx = (struct gl_blur_context *)blur_context;
|
|
|
|
|
2019-12-20 15:33:37 -05:00
|
|
|
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->orig_loc =
|
|
|
|
glGetUniformLocationChecked(down_pass->prog, "orig");
|
|
|
|
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->orig_loc = glGetUniformLocationChecked(up_pass->prog, "orig");
|
|
|
|
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);
|
|
|
|
}
|
2019-12-20 14:46:36 -05:00
|
|
|
|
|
|
|
success = true;
|
2019-12-20 15:33:37 -05:00
|
|
|
out:
|
|
|
|
free(blur_params);
|
|
|
|
|
|
|
|
if (!success) {
|
|
|
|
ctx = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore LC_NUMERIC
|
|
|
|
setlocale(LC_NUMERIC, lc_numeric_old);
|
|
|
|
free(lc_numeric_old);
|
|
|
|
|
2019-12-20 14:46:36 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-07-24 18:55:31 -04:00
|
|
|
// Texture size will be defined by gl_blur
|
2019-12-20 16:10:02 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-07-24 18:55:31 -04:00
|
|
|
// Generate FBO and textures when needed
|
2019-12-20 16:10:02 -05:00
|
|
|
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;
|
|
|
|
}
|
2019-03-09 20:22:38 -05:00
|
|
|
}
|
|
|
|
|
2019-06-09 14:25:34 -04:00
|
|
|
out:
|
|
|
|
if (!success) {
|
|
|
|
gl_destroy_blur_context(&gd->base, ctx);
|
|
|
|
ctx = NULL;
|
|
|
|
}
|
2018-12-31 02:16:35 -05:00
|
|
|
|
2019-06-09 14:25:34 -04:00
|
|
|
gl_check_err();
|
|
|
|
return ctx;
|
2018-12-31 02:16:35 -05:00
|
|
|
}
|
2019-03-08 20:39:18 -05:00
|
|
|
|
2019-07-13 11:55:26 -04:00
|
|
|
void gl_get_blur_size(void *blur_context, int *width, int *height) {
|
2019-12-20 14:46:36 -05:00
|
|
|
auto ctx = (struct gl_blur_context *)blur_context;
|
2019-07-13 11:55:26 -04:00
|
|
|
*width = ctx->resize_width;
|
|
|
|
*height = ctx->resize_height;
|
|
|
|
}
|
|
|
|
|
2019-03-09 09:12:53 -05:00
|
|
|
// clang-format off
|
2019-04-01 14:59:54 -04:00
|
|
|
const char *win_shader_glsl = GLSL(330,
|
2019-03-09 09:12:53 -05:00
|
|
|
uniform float opacity;
|
2019-03-09 21:25:41 -05:00
|
|
|
uniform float dim;
|
2019-03-09 09:12:53 -05:00
|
|
|
uniform bool invert_color;
|
2019-03-13 19:26:24 -04:00
|
|
|
in vec2 texcoord;
|
2019-03-09 09:12:53 -05:00
|
|
|
uniform sampler2D tex;
|
2019-11-05 18:34:57 -05:00
|
|
|
uniform sampler2D brightness;
|
|
|
|
uniform float max_brightness;
|
2019-03-09 09:12:53 -05:00
|
|
|
|
|
|
|
void main() {
|
2019-07-14 11:21:22 -04:00
|
|
|
vec4 c = texelFetch(tex, ivec2(texcoord), 0);
|
2019-03-09 21:25:41 -05:00
|
|
|
if (invert_color) {
|
2019-03-09 09:12:53 -05:00
|
|
|
c = vec4(c.aaa - c.rgb, c.a);
|
2019-03-09 21:25:41 -05:00
|
|
|
}
|
|
|
|
c = vec4(c.rgb * (1.0 - dim), c.a) * opacity;
|
2019-11-05 18:34:57 -05:00
|
|
|
|
2019-11-09 21:47:59 -05:00
|
|
|
vec3 rgb_brightness = texelFetch(brightness, ivec2(0, 0), 0).rgb;
|
2019-11-09 22:37:11 -05:00
|
|
|
// 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;
|
2019-11-05 18:34:57 -05:00
|
|
|
if (brightness > max_brightness)
|
|
|
|
c.rgb = c.rgb * (max_brightness / brightness);
|
|
|
|
|
2019-03-09 09:12:53 -05:00
|
|
|
gl_FragColor = c;
|
|
|
|
}
|
|
|
|
);
|
2019-07-24 19:37:43 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
);
|
2019-03-09 09:12:53 -05:00
|
|
|
// clang-format on
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
bool gl_init(struct gl_data *gd, session_t *ps) {
|
|
|
|
// Initialize GLX data structure
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_FALSE);
|
2019-03-09 20:22:38 -05:00
|
|
|
|
2019-03-09 20:51:21 -05:00
|
|
|
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);
|
2019-03-08 20:39:18 -05:00
|
|
|
|
2019-03-09 20:22:38 -05:00
|
|
|
// Initialize stencil buffer
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
glStencilMask(0x1);
|
|
|
|
glStencilFunc(GL_EQUAL, 0x1, 0x1);
|
2019-03-08 20:39:18 -05:00
|
|
|
|
2020-04-23 08:20:03 -04:00
|
|
|
// 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);
|
2020-04-24 14:14:35 -04:00
|
|
|
glViewport(0, 0, viewport_dimensions[0], viewport_dimensions[1]);
|
2020-04-23 08:20:03 -04:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
// Clear screen
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
2019-03-09 20:22:38 -05:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
2019-03-08 20:39:18 -05:00
|
|
|
|
2019-07-24 19:37:43 -04:00
|
|
|
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);
|
2020-06-06 15:27:25 -04:00
|
|
|
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);
|
2019-07-24 19:37:43 -04:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
2020-04-23 08:20:03 -04:00
|
|
|
// Set projection matrix to gl viewport dimensions so we can use screen
|
|
|
|
// coordinates for all vertices
|
|
|
|
// Note: OpenGL matrices are column major
|
2020-04-24 14:14:35 -04:00
|
|
|
GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)viewport_dimensions[0], 0, 0, 0},
|
|
|
|
{0, 2.0f / (GLfloat)viewport_dimensions[1], 0, 0},
|
2020-04-23 08:20:03 -04:00
|
|
|
{0, 0, 0, 0},
|
|
|
|
{-1, -1, 0, 1}};
|
|
|
|
|
|
|
|
// Initialize shaders
|
2019-03-13 19:26:24 -04:00
|
|
|
gl_win_shader_from_string(vertex_shader, win_shader_glsl, &gd->win_shader);
|
2020-04-23 08:20:03 -04:00
|
|
|
int pml = glGetUniformLocationChecked(gd->win_shader.prog, "projection");
|
|
|
|
glUseProgram(gd->win_shader.prog);
|
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
|
|
|
glUseProgram(0);
|
|
|
|
|
2019-04-01 14:34:02 -04:00
|
|
|
gd->fill_shader.prog = gl_create_program_from_str(fill_vert, fill_frag);
|
|
|
|
gd->fill_shader.color_loc = glGetUniformLocation(gd->fill_shader.prog, "color");
|
2020-04-23 08:20:03 -04:00
|
|
|
pml = glGetUniformLocationChecked(gd->fill_shader.prog, "projection");
|
|
|
|
glUseProgram(gd->fill_shader.prog);
|
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
|
|
|
glUseProgram(0);
|
2019-03-09 20:22:38 -05:00
|
|
|
|
2019-07-24 19:37:43 -04:00
|
|
|
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;
|
|
|
|
}
|
2020-04-23 08:20:03 -04:00
|
|
|
pml = glGetUniformLocationChecked(gd->present_prog, "projection");
|
2019-07-24 19:37:43 -04:00
|
|
|
glUseProgram(gd->present_prog);
|
|
|
|
glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0);
|
2020-04-23 08:20:03 -04:00
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
2019-07-24 19:37:43 -04:00
|
|
|
glUseProgram(0);
|
|
|
|
|
2019-11-09 21:47:59 -05:00
|
|
|
gd->brightness_shader.prog =
|
|
|
|
gl_create_program_from_str(interpolating_vert, interpolating_frag);
|
2019-11-05 18:34:57 -05:00
|
|
|
if (!gd->brightness_shader.prog) {
|
|
|
|
log_error("Failed to create the brightness shader");
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-23 08:20:03 -04:00
|
|
|
pml = glGetUniformLocationChecked(gd->brightness_shader.prog, "projection");
|
2019-11-05 18:34:57 -05:00
|
|
|
glUseProgram(gd->brightness_shader.prog);
|
|
|
|
glUniform1i(glGetUniformLocationChecked(gd->brightness_shader.prog, "tex"), 0);
|
2020-04-23 08:20:03 -04:00
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
2019-11-05 18:34:57 -05:00
|
|
|
glUseProgram(0);
|
|
|
|
|
2020-04-23 08:20:03 -04:00
|
|
|
// Set up the size of the back texture
|
2019-03-09 20:22:38 -05:00
|
|
|
gl_resize(gd, ps->root_width, ps->root_height);
|
2019-03-08 20:39:18 -05:00
|
|
|
|
2019-07-24 19:37:43 -04:00
|
|
|
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);
|
2021-06-24 12:38:44 -04:00
|
|
|
if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-24 19:37:43 -04:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
|
2019-03-27 10:51:20 -04:00
|
|
|
gd->logger = gl_string_marker_logger_new();
|
|
|
|
if (gd->logger) {
|
|
|
|
log_add_target_tls(gd->logger);
|
|
|
|
}
|
|
|
|
|
2019-04-19 20:10:04 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_deinit(struct gl_data *gd) {
|
|
|
|
gl_free_prog_main(&gd->win_shader);
|
|
|
|
|
2019-03-27 10:51:20 -04:00
|
|
|
if (gd->logger) {
|
|
|
|
log_remove_target_tls(gd->logger);
|
|
|
|
gd->logger = NULL;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
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);
|
2019-03-09 10:08:21 -05:00
|
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
2019-03-08 20:39:18 -05:00
|
|
|
glBindTexture(target, 0);
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
2021-06-13 22:22:13 -04:00
|
|
|
/// 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) {
|
2019-05-06 12:27:14 -04:00
|
|
|
if (img->inner->refcount == 1) {
|
|
|
|
return;
|
|
|
|
}
|
2021-06-13 22:22:13 -04:00
|
|
|
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);
|
|
|
|
|
|
|
|
GLuint fbo;
|
|
|
|
glGenFramebuffers(1, &fbo);
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
|
|
inner->texture, 0);
|
|
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, new_tex->texture);
|
|
|
|
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, new_tex->width, new_tex->height, 0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
|
|
glDeleteFramebuffers(1, &fbo);
|
|
|
|
|
|
|
|
img->inner = (struct backend_image_inner_base *)new_tex;
|
|
|
|
inner->refcount--;
|
|
|
|
}
|
2019-05-06 17:09:03 -04:00
|
|
|
|
2021-06-13 22:22:13 -04:00
|
|
|
static void gl_image_apply_alpha(backend_t *base, struct backend_image *img,
|
2019-05-10 03:34:58 -04:00
|
|
|
const region_t *reg_op, double alpha) {
|
2020-03-15 16:24:30 -04:00
|
|
|
// Result color = 0 (GL_ZERO) + alpha (GL_CONSTANT_ALPHA) * original color
|
2021-06-13 22:22:13 -04:00
|
|
|
auto inner = (struct gl_texture *)img->inner;
|
2020-03-15 16:24:30 -04:00
|
|
|
glBlendFunc(GL_ZERO, GL_CONSTANT_ALPHA);
|
|
|
|
glBlendColor(0, 0, 0, (GLclampf)alpha);
|
2019-05-10 03:34:58 -04:00
|
|
|
GLuint fbo;
|
|
|
|
glGenFramebuffers(1, &fbo);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
2021-06-13 22:22:13 -04:00
|
|
|
inner->texture, 0);
|
2019-05-10 03:34:58 -04:00
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
2020-03-15 16:08:27 -04:00
|
|
|
_gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, fbo, 0, false);
|
2019-05-10 03:34:58 -04:00
|
|
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
glDeleteFramebuffers(1, &fbo);
|
2019-05-06 19:31:54 -04:00
|
|
|
}
|
|
|
|
|
2019-07-25 21:02:34 -04:00
|
|
|
void gl_present(backend_t *base, const region_t *region) {
|
2019-07-24 19:37:43 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
|
|
|
|
2019-07-25 21:02:34 -04:00
|
|
|
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],
|
2020-10-21 21:25:19 -04:00
|
|
|
((GLint[]){rect[i].x1, gd->height - rect[i].y2,
|
2019-07-25 21:02:34 -04:00
|
|
|
rect[i].x2, gd->height - rect[i].y2,
|
|
|
|
rect[i].x2, gd->height - rect[i].y1,
|
2020-10-21 21:25:19 -04:00
|
|
|
rect[i].x1, gd->height - rect[i].y1}),
|
2019-07-25 21:02:34 -04:00
|
|
|
sizeof(GLint) * 8);
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
GLuint u = (GLuint)(i * 4);
|
2020-10-21 21:25:19 -04:00
|
|
|
memcpy(&indices[i * 6],
|
|
|
|
((GLuint[]){u + 0, u + 1, u + 2, u + 2, u + 3, u + 0}),
|
2019-07-25 21:02:34 -04:00
|
|
|
sizeof(GLuint) * 6);
|
|
|
|
}
|
2019-07-24 19:37:43 -04:00
|
|
|
|
|
|
|
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]);
|
2019-07-25 21:02:34 -04:00
|
|
|
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);
|
2019-07-24 19:37:43 -04:00
|
|
|
|
2019-11-09 21:47:59 -05:00
|
|
|
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 2, NULL);
|
2019-07-25 21:02:34 -04:00
|
|
|
glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
|
2019-07-24 19:37:43 -04:00
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
glBindVertexArray(0);
|
|
|
|
glDeleteBuffers(2, bo);
|
|
|
|
glDeleteVertexArrays(1, &vao);
|
2019-07-25 21:02:34 -04:00
|
|
|
|
|
|
|
free(coord);
|
|
|
|
free(indices);
|
2019-07-24 19:37:43 -04:00
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
|
2019-07-24 21:27:02 -04:00
|
|
|
const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
|
2021-06-13 22:22:13 -04:00
|
|
|
struct backend_image *tex = image_data;
|
2019-03-09 09:12:53 -05:00
|
|
|
switch (op) {
|
|
|
|
case IMAGE_OP_APPLY_ALPHA:
|
2019-05-06 17:09:03 -04:00
|
|
|
gl_image_decouple(base, tex);
|
2019-05-10 03:34:58 -04:00
|
|
|
assert(tex->inner->refcount == 1);
|
|
|
|
gl_image_apply_alpha(base, tex, reg_op, *(double *)arg);
|
2019-03-09 09:12:53 -05:00
|
|
|
break;
|
|
|
|
}
|
2019-03-09 13:01:18 -05:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-06-13 22:22:13 -04:00
|
|
|
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;
|
2021-04-13 09:58:48 -04:00
|
|
|
GLfloat color[4];
|
2021-06-13 22:22:13 -04:00
|
|
|
glReadPixels(x, inner->y_inverted ? inner->height - y : y, 1, 1, GL_RGBA,
|
|
|
|
GL_FLOAT, color);
|
2021-04-13 09:58:48 -04:00
|
|
|
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;
|
|
|
|
}
|