2019-02-03 13:47:36 -05:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
2024-02-10 15:36:02 -05:00
|
|
|
#include <epoxy/gl.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>
|
2020-10-24 06:53:12 -04:00
|
|
|
#include <time.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
|
|
|
|
2022-12-14 03:07:36 -05:00
|
|
|
void gl_prepare(backend_t *base, const region_t *reg attr_unused) {
|
|
|
|
auto gd = (struct gl_data *)base;
|
|
|
|
glBeginQuery(GL_TIME_ELAPSED, gd->frame_timing[gd->current_frame_timing]);
|
|
|
|
}
|
|
|
|
|
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);
|
2022-07-17 16:50:26 -04:00
|
|
|
if (status == GL_FALSE) {
|
2018-10-03 17:46:18 -04:00
|
|
|
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;
|
|
|
|
}
|
2022-07-17 16:50:26 -04:00
|
|
|
gl_check_err();
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-07-17 16:50:26 -04:00
|
|
|
for (int i = 0; i < nshaders; ++i) {
|
2018-10-03 17:46:18 -04:00
|
|
|
glAttachShader(program, shaders[i]);
|
2022-07-17 16:50:26 -04:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
glLinkProgram(program);
|
|
|
|
|
|
|
|
// Get program status
|
|
|
|
{
|
|
|
|
GLint status = GL_FALSE;
|
|
|
|
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
2022-07-17 16:50:26 -04:00
|
|
|
if (status == GL_FALSE) {
|
2018-10-03 17:46:18 -04:00
|
|
|
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) {
|
2022-07-17 16:50:26 -04:00
|
|
|
for (int i = 0; i < nshaders; ++i) {
|
2018-10-03 17:46:18 -04:00
|
|
|
glDetachShader(program, shaders[i]);
|
2022-07-17 16:50:26 -04:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
if (program && !success) {
|
|
|
|
glDeleteProgram(program);
|
|
|
|
program = 0;
|
|
|
|
}
|
2022-07-17 16:50:26 -04:00
|
|
|
gl_check_err();
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-07-17 09:24:46 -04:00
|
|
|
* @brief Create a program from NULL-terminated arrays of vertex and fragment shader
|
|
|
|
* strings.
|
2018-10-03 17:46:18 -04:00
|
|
|
*/
|
2022-07-17 09:24:46 -04:00
|
|
|
GLuint gl_create_program_from_strv(const char **vert_shaders, const char **frag_shaders) {
|
|
|
|
int vert_count, frag_count;
|
2022-07-17 16:50:26 -04:00
|
|
|
for (vert_count = 0; vert_shaders && vert_shaders[vert_count]; ++vert_count) {
|
|
|
|
}
|
|
|
|
for (frag_count = 0; frag_shaders && frag_shaders[frag_count]; ++frag_count) {
|
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2022-07-17 16:50:26 -04:00
|
|
|
GLuint prog = 0;
|
|
|
|
auto shaders = (GLuint *)ccalloc(vert_count + frag_count, GLuint);
|
2022-07-17 09:24:46 -04:00
|
|
|
for (int i = 0; i < vert_count; ++i) {
|
|
|
|
shaders[i] = gl_create_shader(GL_VERTEX_SHADER, vert_shaders[i]);
|
2022-07-17 16:50:26 -04:00
|
|
|
if (shaders[i] == 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
for (int i = 0; i < frag_count; ++i) {
|
|
|
|
shaders[vert_count + i] =
|
|
|
|
gl_create_shader(GL_FRAGMENT_SHADER, frag_shaders[i]);
|
2022-07-17 16:50:26 -04:00
|
|
|
if (shaders[vert_count + i] == 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2022-07-17 16:50:26 -04:00
|
|
|
prog = gl_create_program(shaders, vert_count + frag_count);
|
|
|
|
|
|
|
|
out:
|
2022-07-17 09:24:46 -04:00
|
|
|
for (int i = 0; i < vert_count + frag_count; ++i) {
|
2022-07-17 16:50:26 -04:00
|
|
|
if (shaders[i] != 0) {
|
|
|
|
glDeleteShader(shaders[i]);
|
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
}
|
|
|
|
free(shaders);
|
2022-07-17 16:50:26 -04:00
|
|
|
gl_check_err();
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
return prog;
|
|
|
|
}
|
|
|
|
|
2022-07-17 09:24:46 -04:00
|
|
|
/**
|
|
|
|
* @brief Create a program from vertex and fragment shader strings.
|
|
|
|
*/
|
|
|
|
GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str) {
|
|
|
|
const char *vert_shaders[2] = {vert_shader_str, NULL};
|
|
|
|
const char *frag_shaders[2] = {frag_shader_str, NULL};
|
|
|
|
|
|
|
|
return gl_create_program_from_strv(vert_shaders, frag_shaders);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_destroy_window_shader(backend_t *backend_data attr_unused, void *shader) {
|
|
|
|
if (!shader) {
|
2018-12-30 20:32:31 -05:00
|
|
|
return;
|
2022-07-17 09:24:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
auto pprogram = (gl_win_shader_t *)shader;
|
2018-12-30 20:32:31 -05:00
|
|
|
if (pprogram->prog) {
|
|
|
|
glDeleteProgram(pprogram->prog);
|
|
|
|
pprogram->prog = 0;
|
|
|
|
}
|
2022-07-17 16:50:26 -04:00
|
|
|
gl_check_err();
|
2022-07-17 09:24:46 -04:00
|
|
|
|
|
|
|
free(shader);
|
2018-12-30 20:32:31 -05:00
|
|
|
}
|
|
|
|
|
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
|
2024-01-30 22:09:28 -05:00
|
|
|
* destination simply by alternating between them.
|
2019-11-05 18:34:57 -05:00
|
|
|
* 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
|
2024-01-30 22:09:28 -05:00
|
|
|
* pixels of img by recursively rendering into texture of quarter the size (half
|
2019-11-05 18:34:57 -05:00
|
|
|
* 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};
|
2023-07-04 13:47:56 -04:00
|
|
|
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_STREAM_DRAW);
|
2019-11-09 21:47:59 -05:00
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
|
2023-07-04 13:47:56 -04:00
|
|
|
GL_STREAM_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,
|
2022-08-24 02:35:49 -04:00
|
|
|
struct backend_image *mask, coord_t mask_offset, GLint *coord,
|
|
|
|
GLuint *indices, int nrects) {
|
2022-08-26 00:28:19 -04:00
|
|
|
// FIXME(yshui) breaks when `mask` and `img` doesn't have the same y_inverted
|
|
|
|
// value. but we don't ever hit this problem because all of our
|
|
|
|
// images and masks are y_inverted.
|
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;
|
2022-08-24 02:42:08 -04:00
|
|
|
auto mask_texture =
|
|
|
|
mask ? ((struct gl_texture *)mask->inner)->texture : gd->default_mask_texture;
|
2021-06-13 22:22:13 -04:00
|
|
|
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
|
|
|
|
2022-07-17 09:24:46 -04:00
|
|
|
auto win_shader = inner->shader;
|
|
|
|
if (!win_shader) {
|
|
|
|
win_shader = gd->default_shader;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(win_shader);
|
|
|
|
assert(win_shader->prog);
|
|
|
|
glUseProgram(win_shader->prog);
|
|
|
|
if (win_shader->uniform_opacity >= 0) {
|
|
|
|
glUniform1f(win_shader->uniform_opacity, (float)img->opacity);
|
2019-04-01 14:55:11 -04:00
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
if (win_shader->uniform_invert_color >= 0) {
|
|
|
|
glUniform1i(win_shader->uniform_invert_color, img->color_inverted);
|
2019-04-01 14:55:11 -04:00
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
if (win_shader->uniform_tex >= 0) {
|
|
|
|
glUniform1i(win_shader->uniform_tex, 0);
|
2019-04-01 14:55:11 -04:00
|
|
|
}
|
2023-01-19 16:48:45 -05:00
|
|
|
if (win_shader->uniform_effective_size >= 0) {
|
|
|
|
glUniform2f(win_shader->uniform_effective_size, (float)img->ewidth,
|
|
|
|
(float)img->eheight);
|
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
if (win_shader->uniform_dim >= 0) {
|
|
|
|
glUniform1f(win_shader->uniform_dim, (float)img->dim);
|
2019-01-27 19:02:54 -05:00
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
if (win_shader->uniform_brightness >= 0) {
|
|
|
|
glUniform1i(win_shader->uniform_brightness, 1);
|
2019-11-05 18:34:57 -05:00
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
if (win_shader->uniform_max_brightness >= 0) {
|
|
|
|
glUniform1f(win_shader->uniform_max_brightness, (float)img->max_brightness);
|
2019-11-05 18:34:57 -05:00
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
if (win_shader->uniform_corner_radius >= 0) {
|
|
|
|
glUniform1f(win_shader->uniform_corner_radius, (float)img->corner_radius);
|
2021-07-16 19:01:09 -04:00
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
if (win_shader->uniform_border_width >= 0) {
|
2022-02-10 02:47:48 -05:00
|
|
|
auto border_width = img->border_width;
|
|
|
|
if (border_width > img->corner_radius) {
|
|
|
|
border_width = 0;
|
|
|
|
}
|
2022-07-17 09:24:46 -04:00
|
|
|
glUniform1f(win_shader->uniform_border_width, (float)border_width);
|
2022-01-19 05:25:01 -05:00
|
|
|
}
|
2020-10-24 06:53:12 -04:00
|
|
|
if (win_shader->uniform_time >= 0) {
|
|
|
|
struct timespec ts;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
|
|
glUniform1f(win_shader->uniform_time,
|
2022-08-24 02:35:49 -04:00
|
|
|
(float)ts.tv_sec * 1000.0F + (float)ts.tv_nsec / 1.0e6F);
|
2020-10-24 06:53:12 -04:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2022-08-24 02:42:08 -04:00
|
|
|
glUniform1i(win_shader->uniform_mask_tex, 2);
|
|
|
|
glUniform2f(win_shader->uniform_mask_offset, (float)mask_offset.x,
|
|
|
|
(float)mask_offset.y);
|
|
|
|
if (mask != NULL) {
|
|
|
|
glUniform1i(win_shader->uniform_mask_inverted, mask->color_inverted);
|
|
|
|
glUniform1f(win_shader->uniform_mask_corner_radius,
|
|
|
|
(GLfloat)mask->corner_radius);
|
|
|
|
} else {
|
|
|
|
glUniform1i(win_shader->uniform_mask_inverted, 0);
|
|
|
|
glUniform1f(win_shader->uniform_mask_corner_radius, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
glActiveTexture(GL_TEXTURE2);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, mask_texture);
|
|
|
|
|
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]);
|
2023-07-04 13:47:56 -04:00
|
|
|
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STREAM_DRAW);
|
2019-05-06 17:09:03 -04:00
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * nrects * 6,
|
2023-07-04 13:47:56 -04:00
|
|
|
indices, GL_STREAM_DRAW);
|
2019-05-06 17:09:03 -04:00
|
|
|
|
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
|
2022-08-24 07:24:49 -04:00
|
|
|
glActiveTexture(GL_TEXTURE2);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glActiveTexture(GL_TEXTURE1);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2019-05-06 17:09:03 -04:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-05-06 19:31:54 -04:00
|
|
|
/// Convert rectangles in X coordinates to OpenGL vertex and texture coordinates
|
|
|
|
/// @param[in] nrects, rects rectangles
|
2022-08-24 14:49:16 -04:00
|
|
|
/// @param[in] image_dst origin of the OpenGL texture, affect the calculated texture
|
2019-05-06 19:31:54 -04:00
|
|
|
/// coordinates
|
2022-08-25 12:49:37 -04:00
|
|
|
/// @param[in] extend_height height of the drawing extent
|
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
|
2022-08-25 12:49:37 -04:00
|
|
|
void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
|
|
|
|
int extent_height, int texture_height, int root_height,
|
|
|
|
bool y_inverted, GLint *coord, GLuint *indices) {
|
2022-08-24 02:35:49 -04:00
|
|
|
image_dst.y = root_height - image_dst.y;
|
2022-08-25 12:49:37 -04:00
|
|
|
image_dst.y -= extent_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
|
2022-08-24 02:35:49 -04:00
|
|
|
GLint texture_x1 = crect.x1 - image_dst.x,
|
|
|
|
texture_y1 = crect.y2 - image_dst.y,
|
2019-07-14 11:21:22 -04:00
|
|
|
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
|
2024-02-10 03:20:10 -05:00
|
|
|
void gl_compose(backend_t *base, image_handle image, coord_t image_dst,
|
|
|
|
image_handle mask_, coord_t mask_dst, const region_t *reg_tgt,
|
2022-08-24 02:35:49 -04:00
|
|
|
const region_t *reg_visible attr_unused) {
|
2020-03-21 11:18:02 -04:00
|
|
|
auto gd = (struct gl_data *)base;
|
2024-02-10 03:20:10 -05:00
|
|
|
auto img = (struct backend_image *)image;
|
|
|
|
auto mask = (struct backend_image *)mask_;
|
2021-06-13 22:22:13 -04:00
|
|
|
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);
|
2022-08-24 02:35:49 -04:00
|
|
|
coord_t mask_offset = {.x = mask_dst.x - image_dst.x, .y = mask_dst.y - image_dst.y};
|
2022-08-25 12:49:37 -04:00
|
|
|
x_rect_to_coords(nrects, rects, image_dst, inner->height, inner->height,
|
|
|
|
gd->height, inner->y_inverted, coord, indices);
|
2022-08-24 02:35:49 -04:00
|
|
|
_gl_compose(base, img, gd->back_fbo, mask, mask_offset, coord, indices, nrects);
|
2019-04-01 14:55:11 -04:00
|
|
|
|
|
|
|
free(indices);
|
|
|
|
free(coord);
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load a GLSL main program from shader strings.
|
|
|
|
*/
|
2022-07-17 09:24:46 -04:00
|
|
|
static bool gl_win_shader_from_stringv(const char **vshader_strv,
|
|
|
|
const char **fshader_strv, gl_win_shader_t *ret) {
|
2018-10-03 17:46:18 -04:00
|
|
|
// Build program
|
2022-07-17 09:24:46 -04:00
|
|
|
ret->prog = gl_create_program_from_strv(vshader_strv, fshader_strv);
|
2018-10-03 17:46:18 -04:00
|
|
|
if (!ret->prog) {
|
|
|
|
log_error("Failed to create GLSL program.");
|
2022-07-17 16:50:26 -04:00
|
|
|
gl_check_err();
|
2022-07-17 09:24:46 -04:00
|
|
|
return false;
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get uniform addresses
|
2022-01-13 06:43:40 -05:00
|
|
|
bind_uniform(ret, opacity);
|
|
|
|
bind_uniform(ret, invert_color);
|
|
|
|
bind_uniform(ret, tex);
|
2023-01-19 16:48:45 -05:00
|
|
|
bind_uniform(ret, effective_size);
|
2022-01-13 06:43:40 -05:00
|
|
|
bind_uniform(ret, dim);
|
|
|
|
bind_uniform(ret, brightness);
|
|
|
|
bind_uniform(ret, max_brightness);
|
|
|
|
bind_uniform(ret, corner_radius);
|
2022-01-19 05:25:01 -05:00
|
|
|
bind_uniform(ret, border_width);
|
2020-10-24 06:53:12 -04:00
|
|
|
bind_uniform(ret, time);
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2022-08-24 02:42:08 -04:00
|
|
|
bind_uniform(ret, mask_tex);
|
|
|
|
bind_uniform(ret, mask_offset);
|
|
|
|
bind_uniform(ret, mask_inverted);
|
|
|
|
bind_uniform(ret, mask_corner_radius);
|
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
gl_check_err();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-02-20 12:30:19 -05:00
|
|
|
void gl_root_change(backend_t *base, session_t *ps) {
|
|
|
|
auto gd = (struct gl_data *)base;
|
|
|
|
gl_resize(gd, ps->root_width, ps->root_height);
|
|
|
|
}
|
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
/**
|
|
|
|
* 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);
|
2022-12-01 13:54:22 -05:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, gd->back_format, width, height, 0, GL_BGR,
|
|
|
|
GL_UNSIGNED_BYTE, NULL);
|
2019-07-24 19:37:43 -04:00
|
|
|
|
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-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
|
|
|
}
|
|
|
|
|
2024-02-10 03:20:10 -05:00
|
|
|
image_handle gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
2022-08-24 02:35:49 -04:00
|
|
|
auto tex = ccalloc(1, struct gl_texture);
|
|
|
|
auto img = default_new_backend_image(size.width, size.height);
|
|
|
|
tex->width = size.width;
|
|
|
|
tex->height = size.height;
|
|
|
|
tex->texture = gl_new_texture(GL_TEXTURE_2D);
|
|
|
|
tex->has_alpha = false;
|
|
|
|
tex->y_inverted = true;
|
|
|
|
img->inner = (struct backend_image_inner_base *)tex;
|
|
|
|
img->inner->refcount = 1;
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex->texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size.width, size.height, 0, GL_RED,
|
|
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
GLuint fbo;
|
|
|
|
glBlendFunc(GL_ONE, GL_ZERO);
|
|
|
|
glGenFramebuffers(1, &fbo);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
|
|
tex->texture, 0);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
glClearColor(0, 0, 0, 1);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
_gl_fill(base, (struct color){1, 1, 1, 1}, reg, fbo, size.height, false);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
glDeleteFramebuffers(1, &fbo);
|
2024-02-10 03:20:10 -05:00
|
|
|
return (image_handle)img;
|
2022-08-24 02:35:49 -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;
|
2022-08-24 02:35:49 -04:00
|
|
|
if (inner->user_data) {
|
|
|
|
gd->release_user_data(base, inner);
|
|
|
|
}
|
2021-06-13 22:22:13 -04:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2024-04-08 04:02:55 -04:00
|
|
|
xcb_pixmap_t gl_release_image(backend_t *base, image_handle image) {
|
2024-02-10 03:20:10 -05:00
|
|
|
auto wd = (struct backend_image *)image;
|
2021-06-13 22:22:13 -04:00
|
|
|
auto inner = (struct gl_texture *)wd->inner;
|
|
|
|
inner->refcount--;
|
|
|
|
assert(inner->refcount >= 0);
|
2024-04-08 04:02:55 -04:00
|
|
|
|
|
|
|
xcb_pixmap_t pixmap = XCB_NONE;
|
2021-06-13 22:22:13 -04:00
|
|
|
if (inner->refcount == 0) {
|
2024-04-08 04:02:55 -04:00
|
|
|
pixmap = inner->pixmap;
|
2021-06-13 22:22:13 -04:00
|
|
|
gl_release_image_inner(base, inner);
|
|
|
|
}
|
|
|
|
free(wd);
|
2024-04-08 04:02:55 -04:00
|
|
|
return pixmap;
|
2019-05-06 17:09:03 -04:00
|
|
|
}
|
|
|
|
|
2022-07-17 09:24:46 -04:00
|
|
|
void *gl_create_window_shader(backend_t *backend_data attr_unused, const char *source) {
|
|
|
|
auto win_shader = (gl_win_shader_t *)ccalloc(1, gl_win_shader_t);
|
|
|
|
|
|
|
|
const char *vert_shaders[2] = {vertex_shader, NULL};
|
2022-08-24 04:36:56 -04:00
|
|
|
const char *frag_shaders[4] = {win_shader_glsl, masking_glsl, source, NULL};
|
2022-07-17 09:24:46 -04:00
|
|
|
|
|
|
|
if (!gl_win_shader_from_stringv(vert_shaders, frag_shaders, win_shader)) {
|
|
|
|
free(win_shader);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GLint viewport_dimensions[2];
|
|
|
|
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
|
|
|
|
|
|
|
|
// Set projection matrix to gl viewport dimensions so we can use screen
|
|
|
|
// coordinates for all vertices
|
|
|
|
// Note: OpenGL matrices are column major
|
|
|
|
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}};
|
|
|
|
|
|
|
|
int pml = glGetUniformLocationChecked(win_shader->prog, "projection");
|
|
|
|
glUseProgram(win_shader->prog);
|
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
|
|
|
glUseProgram(0);
|
|
|
|
|
|
|
|
return win_shader;
|
|
|
|
}
|
|
|
|
|
2022-08-10 23:53:14 -04:00
|
|
|
uint64_t gl_get_shader_attributes(backend_t *backend_data attr_unused, void *shader) {
|
|
|
|
auto win_shader = (gl_win_shader_t *)shader;
|
|
|
|
uint64_t ret = 0;
|
|
|
|
if (glGetUniformLocation(win_shader->prog, "time") >= 0) {
|
|
|
|
ret |= SHADER_ATTRIBUTE_ANIMATED;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
bool gl_init(struct gl_data *gd, session_t *ps) {
|
2022-12-14 03:07:36 -05:00
|
|
|
glGenQueries(2, gd->frame_timing);
|
|
|
|
gd->current_frame_timing = 0;
|
|
|
|
|
2023-06-18 12:34:53 -04:00
|
|
|
// Initialize GL data structure
|
2019-03-08 20:39:18 -05:00
|
|
|
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
|
2022-08-23 00:18:17 -04:00
|
|
|
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);
|
|
|
|
|
2022-08-24 02:42:08 -04:00
|
|
|
gd->default_mask_texture = gl_new_texture(GL_TEXTURE_2D);
|
|
|
|
if (!gd->default_mask_texture) {
|
|
|
|
log_error("Failed to generate a default mask texture");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, gd->default_mask_texture);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE,
|
|
|
|
(GLbyte[]){'\xff'});
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
2022-07-17 09:24:46 -04:00
|
|
|
// Initialize shaders
|
|
|
|
gd->default_shader = gl_create_window_shader(NULL, win_shader_default);
|
|
|
|
if (!gd->default_shader) {
|
|
|
|
log_error("Failed to create window shaders");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
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
|
2022-07-17 09:24:46 -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}};
|
|
|
|
|
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");
|
2022-07-17 09:24:46 -04:00
|
|
|
int pml = glGetUniformLocationChecked(gd->fill_shader.prog, "projection");
|
2020-04-23 08:20:03 -04:00
|
|
|
glUseProgram(gd->fill_shader.prog);
|
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
|
|
|
glUseProgram(0);
|
2019-03-09 20:22:38 -05:00
|
|
|
|
2022-11-30 00:28:57 -05:00
|
|
|
gd->dithered_present = ps->o.dithered_present;
|
2024-02-06 08:04:10 -05:00
|
|
|
gd->dummy_prog =
|
|
|
|
gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL},
|
|
|
|
(const char *[]){dummy_frag, NULL});
|
|
|
|
if (!gd->dummy_prog) {
|
|
|
|
log_error("Failed to create the dummy shader");
|
|
|
|
return false;
|
|
|
|
}
|
2022-11-30 00:28:57 -05:00
|
|
|
if (gd->dithered_present) {
|
|
|
|
gd->present_prog = gl_create_program_from_strv(
|
|
|
|
(const char *[]){present_vertex_shader, NULL},
|
|
|
|
(const char *[]){present_frag, dither_glsl, NULL});
|
|
|
|
} else {
|
2024-02-06 08:04:10 -05:00
|
|
|
gd->present_prog = gd->dummy_prog;
|
2022-11-30 00:28:57 -05:00
|
|
|
}
|
2019-07-24 19:37:43 -04:00
|
|
|
if (!gd->present_prog) {
|
|
|
|
log_error("Failed to create the present shader");
|
|
|
|
return false;
|
|
|
|
}
|
2024-02-06 08:04:10 -05:00
|
|
|
|
|
|
|
pml = glGetUniformLocationChecked(gd->dummy_prog, "projection");
|
|
|
|
glUseProgram(gd->dummy_prog);
|
|
|
|
glUniform1i(glGetUniformLocationChecked(gd->dummy_prog, "tex"), 0);
|
2020-04-23 08:20:03 -04:00
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
2024-02-06 08:04:10 -05:00
|
|
|
if (gd->present_prog != gd->dummy_prog) {
|
|
|
|
pml = glGetUniformLocationChecked(gd->present_prog, "projection");
|
|
|
|
glUseProgram(gd->present_prog);
|
|
|
|
glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0);
|
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
|
|
|
}
|
2019-07-24 19:37:43 -04:00
|
|
|
|
2022-08-25 13:00:25 -04:00
|
|
|
gd->shadow_shader.prog =
|
|
|
|
gl_create_program_from_str(present_vertex_shader, shadow_colorization_frag);
|
|
|
|
gd->shadow_shader.uniform_color =
|
|
|
|
glGetUniformLocationChecked(gd->shadow_shader.prog, "color");
|
|
|
|
pml = glGetUniformLocationChecked(gd->shadow_shader.prog, "projection");
|
|
|
|
glUseProgram(gd->shadow_shader.prog);
|
|
|
|
glUniform1i(glGetUniformLocationChecked(gd->shadow_shader.prog, "tex"), 0);
|
|
|
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
|
|
|
glBindFragDataLocation(gd->shadow_shader.prog, 0, "out_color");
|
|
|
|
|
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]);
|
2024-02-06 08:04:10 -05:00
|
|
|
|
2019-11-05 18:34:57 -05:00
|
|
|
glUseProgram(0);
|
|
|
|
|
2022-12-01 13:54:22 -05:00
|
|
|
// Set up the size and format of the back texture
|
2019-07-24 19:37:43 -04:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->back_fbo);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
2022-12-01 21:57:28 -05:00
|
|
|
const GLint *format = gd->dithered_present ? (const GLint[]){GL_RGB16, GL_RGBA16}
|
|
|
|
: (const GLint[]){GL_RGB8, GL_RGBA8};
|
2022-12-01 13:54:22 -05:00
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
gd->back_format = format[i];
|
|
|
|
gl_resize(gd, ps->root_width, ps->root_height);
|
|
|
|
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
|
|
GL_TEXTURE_2D, gd->back_texture, 0);
|
|
|
|
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
log_info("Using back buffer format %#x", gd->back_format);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!gl_check_fb_complete(GL_DRAW_FRAMEBUFFER)) {
|
2021-06-24 12:38:44 -04:00
|
|
|
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) {
|
2022-12-13 15:56:11 -05:00
|
|
|
log_info("GL vendor is NVIDIA, enable xrender sync fence.");
|
2019-04-19 20:10:04 -04:00
|
|
|
gd->is_nvidia = true;
|
|
|
|
} else {
|
|
|
|
gd->is_nvidia = false;
|
|
|
|
}
|
2024-02-10 08:28:13 -05:00
|
|
|
gd->has_robustness = epoxy_has_gl_extension("GL_ARB_robustness");
|
|
|
|
gd->has_egl_image_storage = epoxy_has_gl_extension("GL_EXT_EGL_image_storage");
|
2022-08-25 13:00:25 -04:00
|
|
|
gl_check_err();
|
2019-04-19 20:10:04 -04:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_deinit(struct gl_data *gd) {
|
2019-03-27 10:51:20 -04:00
|
|
|
if (gd->logger) {
|
|
|
|
log_remove_target_tls(gd->logger);
|
|
|
|
gd->logger = NULL;
|
|
|
|
}
|
|
|
|
|
2022-07-17 09:24:46 -04:00
|
|
|
if (gd->default_shader) {
|
|
|
|
gl_destroy_window_shader(&gd->base, gd->default_shader);
|
|
|
|
gd->default_shader = NULL;
|
|
|
|
}
|
2024-02-06 08:16:12 -05:00
|
|
|
glDeleteProgram(gd->dummy_prog);
|
|
|
|
if (gd->present_prog != gd->dummy_prog) {
|
|
|
|
glDeleteProgram(gd->present_prog);
|
|
|
|
}
|
|
|
|
gd->dummy_prog = 0;
|
|
|
|
gd->present_prog = 0;
|
|
|
|
|
|
|
|
glDeleteProgram(gd->fill_shader.prog);
|
|
|
|
glDeleteProgram(gd->brightness_shader.prog);
|
|
|
|
glDeleteProgram(gd->shadow_shader.prog);
|
|
|
|
gd->fill_shader.prog = 0;
|
|
|
|
gd->brightness_shader.prog = 0;
|
|
|
|
gd->shadow_shader.prog = 0;
|
2022-07-17 09:24:46 -04:00
|
|
|
|
2023-06-12 14:24:57 -04:00
|
|
|
glDeleteTextures(1, &gd->default_mask_texture);
|
|
|
|
glDeleteTextures(1, &gd->back_texture);
|
|
|
|
|
2024-02-06 08:16:12 -05:00
|
|
|
glDeleteQueries(2, gd->frame_timing);
|
|
|
|
|
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);
|
2024-02-02 15:55:29 -05:00
|
|
|
new_tex->y_inverted = inner->y_inverted;
|
|
|
|
new_tex->has_alpha = inner->has_alpha;
|
2021-06-13 22:22:13 -04:00
|
|
|
new_tex->height = inner->height;
|
|
|
|
new_tex->width = inner->width;
|
2024-02-02 15:55:29 -05:00
|
|
|
new_tex->shader = inner->shader;
|
2021-06-13 22:22:13 -04:00
|
|
|
new_tex->refcount = 1;
|
|
|
|
new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data);
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, new_tex->texture);
|
2021-06-24 12:47:40 -04:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, new_tex->width, new_tex->height, 0,
|
|
|
|
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
2021-06-13 22:22:13 -04:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2021-06-24 12:47:40 -04:00
|
|
|
|
2024-02-06 08:04:10 -05:00
|
|
|
glUseProgram(gd->dummy_prog);
|
2021-06-24 12:47:40 -04:00
|
|
|
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]);
|
2023-07-04 13:47:56 -04:00
|
|
|
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_STREAM_DRAW);
|
2021-06-24 12:47:40 -04:00
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
|
2023-07-04 13:47:56 -04:00
|
|
|
GL_STREAM_DRAW);
|
2021-06-24 12:47:40 -04:00
|
|
|
|
|
|
|
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);
|
2021-06-13 22:22:13 -04:00
|
|
|
glDeleteFramebuffers(1, &fbo);
|
|
|
|
|
2021-06-24 12:47:40 -04:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glUseProgram(0);
|
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
|
2021-06-13 22:22:13 -04:00
|
|
|
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);
|
2022-08-23 05:06:27 -04:00
|
|
|
_gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, fbo, inner->height,
|
|
|
|
!inner->y_inverted);
|
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);
|
2024-04-05 21:37:13 -04:00
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2019-07-24 19:37:43 -04:00
|
|
|
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
|
|
|
|
2022-12-14 03:07:36 -05:00
|
|
|
glEndQuery(GL_TIME_ELAPSED);
|
|
|
|
gd->current_frame_timing ^= 1;
|
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
|
2019-07-25 21:02:34 -04:00
|
|
|
free(coord);
|
|
|
|
free(indices);
|
2019-07-24 19:37:43 -04:00
|
|
|
}
|
|
|
|
|
2022-12-14 03:07:36 -05:00
|
|
|
bool gl_last_render_time(backend_t *base, struct timespec *ts) {
|
|
|
|
auto gd = (struct gl_data *)base;
|
|
|
|
GLint available = 0;
|
|
|
|
glGetQueryObjectiv(gd->frame_timing[gd->current_frame_timing ^ 1],
|
|
|
|
GL_QUERY_RESULT_AVAILABLE, &available);
|
|
|
|
if (!available) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
GLuint64 time;
|
|
|
|
glGetQueryObjectui64v(gd->frame_timing[gd->current_frame_timing ^ 1],
|
|
|
|
GL_QUERY_RESULT, &time);
|
|
|
|
ts->tv_sec = (long)(time / 1000000000);
|
|
|
|
ts->tv_nsec = (long)(time % 1000000000);
|
|
|
|
gl_check_err();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-02-10 03:20:10 -05:00
|
|
|
bool gl_image_op(backend_t *base, enum image_operations op, image_handle image,
|
2019-07-24 21:27:02 -04:00
|
|
|
const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
|
2024-02-10 03:20:10 -05:00
|
|
|
auto tex = (struct backend_image *)image;
|
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;
|
|
|
|
}
|
2022-02-08 02:53:15 -05:00
|
|
|
|
2022-07-17 16:32:31 -04:00
|
|
|
bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
|
2024-03-19 10:57:24 -04:00
|
|
|
image_handle image, const void *args) {
|
2022-07-17 16:32:31 -04:00
|
|
|
if (prop != IMAGE_PROPERTY_CUSTOM_SHADER) {
|
2024-02-10 03:20:10 -05:00
|
|
|
return default_set_image_property(backend_data, prop, image, args);
|
2022-07-17 16:32:31 -04:00
|
|
|
}
|
|
|
|
|
2024-02-10 03:20:10 -05:00
|
|
|
auto img = (struct backend_image *)image;
|
2022-07-17 16:32:31 -04:00
|
|
|
auto inner = (struct gl_texture *)img->inner;
|
|
|
|
inner->shader = args;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-08-25 13:00:25 -04:00
|
|
|
struct gl_shadow_context {
|
|
|
|
double radius;
|
|
|
|
void *blur_context;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius) {
|
|
|
|
auto ctx = ccalloc(1, struct gl_shadow_context);
|
|
|
|
ctx->radius = radius;
|
2022-11-17 17:07:59 -05:00
|
|
|
ctx->blur_context = NULL;
|
|
|
|
|
|
|
|
if (radius > 0) {
|
|
|
|
struct gaussian_blur_args args = {
|
|
|
|
.size = (int)radius,
|
|
|
|
.deviation = gaussian_kernel_std_for_size(radius, 0.5 / 256.0),
|
|
|
|
};
|
|
|
|
ctx->blur_context = gl_create_blur_context(base, BLUR_METHOD_GAUSSIAN, &args);
|
2022-11-17 17:16:27 -05:00
|
|
|
if (!ctx->blur_context) {
|
|
|
|
log_error("Failed to create shadow context");
|
|
|
|
free(ctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-11-17 17:07:59 -05:00
|
|
|
}
|
2022-08-25 13:00:25 -04:00
|
|
|
return (struct backend_shadow_context *)ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shadow_context *ctx) {
|
|
|
|
auto ctx_ = (struct gl_shadow_context *)ctx;
|
2022-11-17 17:07:59 -05:00
|
|
|
if (ctx_->blur_context) {
|
|
|
|
gl_destroy_blur_context(base, (struct backend_blur_context *)ctx_->blur_context);
|
|
|
|
}
|
2022-08-25 13:00:25 -04:00
|
|
|
free(ctx_);
|
|
|
|
}
|
|
|
|
|
2024-02-10 03:20:10 -05:00
|
|
|
image_handle gl_shadow_from_mask(backend_t *base, image_handle mask_,
|
|
|
|
struct backend_shadow_context *sctx, struct color color) {
|
2022-08-25 13:00:25 -04:00
|
|
|
log_debug("Create shadow from mask");
|
|
|
|
auto gd = (struct gl_data *)base;
|
2024-02-10 03:20:10 -05:00
|
|
|
auto mask = (struct backend_image *)mask_;
|
|
|
|
auto inner = (struct gl_texture *)mask->inner;
|
2022-08-25 13:00:25 -04:00
|
|
|
auto gsctx = (struct gl_shadow_context *)sctx;
|
|
|
|
int radius = (int)gsctx->radius;
|
|
|
|
|
|
|
|
auto new_inner = ccalloc(1, struct gl_texture);
|
|
|
|
new_inner->width = inner->width + radius * 2;
|
|
|
|
new_inner->height = inner->height + radius * 2;
|
|
|
|
new_inner->texture = gl_new_texture(GL_TEXTURE_2D);
|
|
|
|
new_inner->has_alpha = inner->has_alpha;
|
2022-08-26 00:28:19 -04:00
|
|
|
new_inner->y_inverted = true;
|
2022-08-25 13:00:25 -04:00
|
|
|
auto new_img = default_new_backend_image(new_inner->width, new_inner->height);
|
|
|
|
new_img->inner = (struct backend_image_inner_base *)new_inner;
|
|
|
|
new_img->inner->refcount = 1;
|
|
|
|
|
|
|
|
// Render the mask to a texture, so inversion and corner radius can be
|
|
|
|
// applied.
|
|
|
|
auto source_texture = gl_new_texture(GL_TEXTURE_2D);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, source_texture);
|
2022-11-11 10:51:11 -05:00
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
2022-08-25 13:00:25 -04:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, new_inner->width, new_inner->height, 0,
|
|
|
|
GL_RED, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
GLuint fbo;
|
|
|
|
glGenFramebuffers(1, &fbo);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
|
|
source_texture, 0);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
2024-02-10 03:20:10 -05:00
|
|
|
if (mask->color_inverted) {
|
2022-08-25 13:00:25 -04:00
|
|
|
// If the mask is inverted, clear the source_texture to white, so the
|
|
|
|
// "outside" of the mask would be correct
|
|
|
|
glClearColor(1, 1, 1, 1);
|
|
|
|
} else {
|
|
|
|
glClearColor(0, 0, 0, 1);
|
|
|
|
}
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
{
|
|
|
|
// clang-format off
|
|
|
|
// interleaved vertex coordinates and texture coordinates
|
|
|
|
GLint coords[] = {radius , radius , 0 , 0,
|
|
|
|
radius + inner->width, radius , inner->width, 0,
|
|
|
|
radius + inner->width, radius + inner->height, inner->width, inner->height,
|
|
|
|
radius , radius + inner->height, 0 , inner->height,};
|
|
|
|
// clang-format on
|
|
|
|
GLuint indices[] = {0, 1, 2, 2, 3, 0};
|
|
|
|
_gl_compose(base, mask, fbo, NULL, (coord_t){0}, coords, indices, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
|
2022-11-17 17:07:59 -05:00
|
|
|
auto tmp_texture = source_texture;
|
|
|
|
if (gsctx->blur_context != NULL) {
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
tmp_texture = gl_new_texture(GL_TEXTURE_2D);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tmp_texture);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, new_inner->width,
|
|
|
|
new_inner->height, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
|
|
GL_TEXTURE_2D, tmp_texture, 0);
|
|
|
|
|
|
|
|
region_t reg_blur;
|
|
|
|
pixman_region32_init_rect(®_blur, 0, 0, (unsigned int)new_inner->width,
|
|
|
|
(unsigned int)new_inner->height);
|
|
|
|
// gl_blur expects reg_blur to be in X coordinate system (i.e. y flipped),
|
|
|
|
// but we are covering the whole texture so we don't need to worry about
|
|
|
|
// that.
|
|
|
|
gl_blur_impl(
|
|
|
|
1.0, gsctx->blur_context, NULL, (coord_t){0}, ®_blur, NULL,
|
|
|
|
source_texture,
|
|
|
|
(geometry_t){.width = new_inner->width, .height = new_inner->height},
|
2022-11-30 00:28:57 -05:00
|
|
|
fbo, gd->default_mask_texture, gd->dithered_present);
|
2022-11-17 17:07:59 -05:00
|
|
|
pixman_region32_fini(®_blur);
|
|
|
|
}
|
2022-08-25 13:00:25 -04:00
|
|
|
|
|
|
|
// Colorize the shadow with color.
|
|
|
|
log_debug("Colorize shadow");
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, new_inner->texture);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, new_inner->width, new_inner->height, 0,
|
|
|
|
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
|
|
new_inner->texture, 0);
|
|
|
|
glClearColor(0, 0, 0, 0);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tmp_texture);
|
|
|
|
glUseProgram(gd->shadow_shader.prog);
|
2023-09-10 15:32:02 -04:00
|
|
|
// The shadow color is converted to the premultiplied format to respect the
|
|
|
|
// globally set glBlendFunc and thus get the correct and expected result.
|
|
|
|
glUniform4f(gd->shadow_shader.uniform_color, (GLfloat)(color.red * color.alpha),
|
|
|
|
(GLfloat)(color.green * color.alpha),
|
|
|
|
(GLfloat)(color.blue * color.alpha), (GLfloat)color.alpha);
|
2022-08-25 13:00:25 -04:00
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
GLuint indices[] = {0, 1, 2, 2, 3, 0};
|
|
|
|
GLint coord[] = {0 , 0 ,
|
|
|
|
new_inner->width , 0 ,
|
|
|
|
new_inner->width , new_inner->height,
|
|
|
|
0 , new_inner->height,};
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
GLuint vao;
|
|
|
|
glGenVertexArrays(1, &vao);
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
|
|
|
|
GLuint bo[2];
|
|
|
|
glGenBuffers(2, bo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
|
2023-07-04 13:47:56 -04:00
|
|
|
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 8, coord, GL_STREAM_DRAW);
|
2022-08-25 13:00:25 -04:00
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
|
2023-07-04 13:47:56 -04:00
|
|
|
GL_STREAM_DRAW);
|
2022-08-25 13:00:25 -04:00
|
|
|
|
|
|
|
glEnableVertexAttribArray(vert_coord_loc);
|
|
|
|
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 2, NULL);
|
|
|
|
|
|
|
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
|
|
|
|
|
|
|
|
glDisableVertexAttribArray(vert_coord_loc);
|
|
|
|
glBindVertexArray(0);
|
|
|
|
glDeleteVertexArrays(1, &vao);
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
glDeleteBuffers(2, bo);
|
|
|
|
|
2022-11-17 17:07:59 -05:00
|
|
|
glDeleteTextures(1, (GLuint[]){source_texture});
|
|
|
|
if (tmp_texture != source_texture) {
|
|
|
|
glDeleteTextures(1, (GLuint[]){tmp_texture});
|
|
|
|
}
|
2022-08-25 13:00:25 -04:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
glDeleteFramebuffers(1, &fbo);
|
|
|
|
gl_check_err();
|
2024-02-10 03:20:10 -05:00
|
|
|
return (image_handle)new_img;
|
2022-08-25 13:00:25 -04:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:53:15 -05:00
|
|
|
enum device_status gl_device_status(backend_t *base) {
|
|
|
|
auto gd = (struct gl_data *)base;
|
|
|
|
if (!gd->has_robustness) {
|
|
|
|
return DEVICE_STATUS_NORMAL;
|
|
|
|
}
|
|
|
|
if (glGetGraphicsResetStatusARB() == GL_NO_ERROR) {
|
|
|
|
return DEVICE_STATUS_NORMAL;
|
|
|
|
}
|
2022-08-25 12:49:37 -04:00
|
|
|
return DEVICE_STATUS_RESETTING;
|
2022-02-08 02:53:15 -05:00
|
|
|
}
|