2019-02-03 18:47:36 +00:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
2018-10-03 21:46:18 +00:00
|
|
|
#pragma once
|
|
|
|
#include <GL/gl.h>
|
|
|
|
#include <GL/glext.h>
|
2019-03-09 01:39:18 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <string.h>
|
2018-10-03 21:46:18 +00:00
|
|
|
|
2019-03-09 01:39:18 +00:00
|
|
|
#include "backend/backend.h"
|
2019-01-20 16:53:39 +00:00
|
|
|
#include "log.h"
|
2019-03-09 01:39:18 +00:00
|
|
|
#include "region.h"
|
2018-10-03 21:46:18 +00:00
|
|
|
|
2019-03-09 01:39:18 +00:00
|
|
|
#define CASESTRRET(s) \
|
|
|
|
case s: return #s
|
2019-01-27 23:57:43 +00:00
|
|
|
|
2022-01-13 11:43:40 +00:00
|
|
|
static inline GLint glGetUniformLocationChecked(GLuint p, const char *name) {
|
|
|
|
auto ret = glGetUniformLocation(p, name);
|
|
|
|
if (ret < 0) {
|
|
|
|
log_info("Failed to get location of uniform '%s'. This is normal when "
|
|
|
|
"using custom shaders.",
|
|
|
|
name);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define bind_uniform(shader, uniform) \
|
|
|
|
(shader)->uniform_##uniform = glGetUniformLocationChecked((shader)->prog, #uniform)
|
|
|
|
|
2018-10-03 21:46:18 +00:00
|
|
|
// Program and uniforms for window shader
|
|
|
|
typedef struct {
|
2022-07-17 13:24:46 +00:00
|
|
|
UT_hash_handle hh;
|
|
|
|
uint32_t id;
|
2018-10-03 21:46:18 +00:00
|
|
|
GLuint prog;
|
2022-01-13 11:43:40 +00:00
|
|
|
GLint uniform_opacity;
|
|
|
|
GLint uniform_invert_color;
|
|
|
|
GLint uniform_tex;
|
|
|
|
GLint uniform_dim;
|
|
|
|
GLint uniform_brightness;
|
|
|
|
GLint uniform_max_brightness;
|
|
|
|
GLint uniform_corner_radius;
|
2022-01-19 10:25:01 +00:00
|
|
|
GLint uniform_border_width;
|
2020-10-24 10:53:12 +00:00
|
|
|
GLint uniform_time;
|
2022-08-24 06:42:08 +00:00
|
|
|
|
|
|
|
GLint uniform_mask_tex;
|
|
|
|
GLint uniform_mask_offset;
|
|
|
|
GLint uniform_mask_corner_radius;
|
|
|
|
GLint uniform_mask_inverted;
|
2018-10-03 21:46:18 +00:00
|
|
|
} gl_win_shader_t;
|
|
|
|
|
2019-11-05 23:34:57 +00:00
|
|
|
// Program and uniforms for brightness shader
|
|
|
|
typedef struct {
|
|
|
|
GLuint prog;
|
|
|
|
} gl_brightness_shader_t;
|
|
|
|
|
2018-10-03 21:46:18 +00:00
|
|
|
// Program and uniforms for blur shader
|
|
|
|
typedef struct {
|
|
|
|
GLuint prog;
|
2022-01-13 11:43:40 +00:00
|
|
|
GLint uniform_pixel_norm;
|
|
|
|
GLint uniform_opacity;
|
2019-07-24 23:37:43 +00:00
|
|
|
GLint texorig_loc;
|
2019-12-20 20:33:37 +00:00
|
|
|
GLint scale_loc;
|
2018-10-03 21:46:18 +00:00
|
|
|
} gl_blur_shader_t;
|
|
|
|
|
2019-04-01 18:34:02 +00:00
|
|
|
typedef struct {
|
|
|
|
GLuint prog;
|
|
|
|
GLint color_loc;
|
|
|
|
} gl_fill_shader_t;
|
|
|
|
|
2021-06-14 02:22:13 +00:00
|
|
|
/// @brief Wrapper of a binded GLX texture.
|
2019-05-06 16:27:14 +00:00
|
|
|
struct gl_texture {
|
|
|
|
int refcount;
|
2021-06-14 02:22:13 +00:00
|
|
|
bool has_alpha;
|
2019-05-06 16:27:14 +00:00
|
|
|
GLuint texture;
|
|
|
|
int width, height;
|
|
|
|
bool y_inverted;
|
2019-11-10 02:47:59 +00:00
|
|
|
|
|
|
|
// Textures for auxiliary uses.
|
|
|
|
GLuint auxiliary_texture[2];
|
2022-07-17 13:24:46 +00:00
|
|
|
gl_win_shader_t *shader;
|
2019-05-06 21:09:03 +00:00
|
|
|
void *user_data;
|
2019-05-06 16:27:14 +00:00
|
|
|
};
|
|
|
|
|
2019-03-09 01:39:18 +00:00
|
|
|
struct gl_data {
|
|
|
|
backend_t base;
|
2019-04-20 00:10:04 +00:00
|
|
|
// If we are using proprietary NVIDIA driver
|
|
|
|
bool is_nvidia;
|
2022-02-08 07:53:15 +00:00
|
|
|
// If ARB_robustness extension is present
|
|
|
|
bool has_robustness;
|
2020-04-23 12:20:03 +00:00
|
|
|
// Height and width of the root window
|
2019-03-09 01:39:18 +00:00
|
|
|
int height, width;
|
2022-07-17 13:24:46 +00:00
|
|
|
// Hash-table of window shaders
|
|
|
|
gl_win_shader_t *default_shader;
|
2019-11-05 23:34:57 +00:00
|
|
|
gl_brightness_shader_t brightness_shader;
|
2019-04-01 18:34:02 +00:00
|
|
|
gl_fill_shader_t fill_shader;
|
2019-07-24 23:37:43 +00:00
|
|
|
GLuint back_texture, back_fbo;
|
|
|
|
GLuint present_prog;
|
2019-03-27 14:51:20 +00:00
|
|
|
|
2022-08-24 06:42:08 +00:00
|
|
|
GLuint default_mask_texture;
|
|
|
|
|
2019-05-06 21:09:03 +00:00
|
|
|
/// Called when an gl_texture is decoupled from the texture it refers. Returns
|
|
|
|
/// the decoupled user_data
|
|
|
|
void *(*decouple_texture_user_data)(backend_t *base, void *user_data);
|
|
|
|
|
|
|
|
/// Release the user data attached to a gl_texture
|
|
|
|
void (*release_user_data)(backend_t *base, struct gl_texture *);
|
|
|
|
|
2019-03-27 14:51:20 +00:00
|
|
|
struct log_target *logger;
|
2019-03-09 01:39:18 +00:00
|
|
|
};
|
2018-10-03 21:46:18 +00:00
|
|
|
|
2019-02-03 16:41:04 +00:00
|
|
|
typedef struct session session_t;
|
|
|
|
|
2018-10-03 21:46:18 +00:00
|
|
|
#define GL_PROG_MAIN_INIT \
|
|
|
|
{ .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, .unifm_tex = -1, }
|
|
|
|
|
|
|
|
GLuint gl_create_shader(GLenum shader_type, const char *shader_str);
|
|
|
|
GLuint gl_create_program(const GLuint *const shaders, int nshaders);
|
2019-03-09 01:39:18 +00:00
|
|
|
GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str);
|
2022-07-17 13:24:46 +00:00
|
|
|
void *gl_create_window_shader(backend_t *backend_data, const char *source);
|
|
|
|
void gl_destroy_window_shader(backend_t *backend_data, void *shader);
|
2022-08-11 03:53:14 +00:00
|
|
|
uint64_t gl_get_shader_attributes(backend_t *backend_data, void *shader);
|
2022-07-17 20:32:31 +00:00
|
|
|
bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
|
|
|
|
void *image_data, void *args);
|
2019-03-09 01:39:18 +00:00
|
|
|
|
2019-01-28 00:02:54 +00:00
|
|
|
/**
|
|
|
|
* @brief Render a region with texture data.
|
|
|
|
*/
|
2022-08-24 06:35:49 +00:00
|
|
|
void gl_compose(backend_t *, void *image_data, coord_t image_dst, void *mask,
|
|
|
|
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible);
|
2018-10-03 21:46:18 +00:00
|
|
|
|
2019-03-09 01:39:18 +00:00
|
|
|
void gl_resize(struct gl_data *, int width, int height);
|
2018-10-03 21:46:18 +00:00
|
|
|
|
2019-03-09 01:39:18 +00:00
|
|
|
bool gl_init(struct gl_data *gd, session_t *);
|
|
|
|
void gl_deinit(struct gl_data *gd);
|
|
|
|
|
|
|
|
GLuint gl_new_texture(GLenum target);
|
|
|
|
|
|
|
|
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
|
|
|
|
const region_t *reg_op, const region_t *reg_visible, void *arg);
|
|
|
|
|
2019-05-06 21:09:03 +00:00
|
|
|
void gl_release_image(backend_t *base, void *image_data);
|
2022-08-24 06:35:49 +00:00
|
|
|
void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg);
|
2019-05-06 21:09:03 +00:00
|
|
|
|
2021-06-14 00:51:09 +00:00
|
|
|
void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visible);
|
2019-05-06 21:09:03 +00:00
|
|
|
|
2022-08-24 07:35:24 +00:00
|
|
|
bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mask_dst,
|
|
|
|
const region_t *reg_blur, const region_t *reg_visible);
|
2019-06-09 18:25:34 +00:00
|
|
|
void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
|
|
|
|
void gl_destroy_blur_context(backend_t *base, void *ctx);
|
2019-07-13 15:55:26 +00:00
|
|
|
void gl_get_blur_size(void *blur_context, int *width, int *height);
|
2019-03-09 01:39:18 +00:00
|
|
|
|
2019-05-10 07:34:58 +00:00
|
|
|
void gl_fill(backend_t *base, struct color, const region_t *clip);
|
2019-03-09 01:39:18 +00:00
|
|
|
|
2019-07-26 01:02:34 +00:00
|
|
|
void gl_present(backend_t *base, const region_t *);
|
2021-04-13 13:58:48 +00:00
|
|
|
bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color *output);
|
2022-02-08 07:53:15 +00:00
|
|
|
enum device_status gl_device_status(backend_t *base);
|
2019-07-24 23:37:43 +00:00
|
|
|
|
2019-03-09 01:39:18 +00:00
|
|
|
static inline void gl_delete_texture(GLuint texture) {
|
|
|
|
glDeleteTextures(1, &texture);
|
|
|
|
}
|
|
|
|
|
2018-10-03 21:46:18 +00:00
|
|
|
/**
|
|
|
|
* Get a textual representation of an OpenGL error.
|
|
|
|
*/
|
|
|
|
static inline const char *gl_get_err_str(GLenum err) {
|
|
|
|
switch (err) {
|
|
|
|
CASESTRRET(GL_NO_ERROR);
|
|
|
|
CASESTRRET(GL_INVALID_ENUM);
|
|
|
|
CASESTRRET(GL_INVALID_VALUE);
|
|
|
|
CASESTRRET(GL_INVALID_OPERATION);
|
|
|
|
CASESTRRET(GL_INVALID_FRAMEBUFFER_OPERATION);
|
|
|
|
CASESTRRET(GL_OUT_OF_MEMORY);
|
|
|
|
CASESTRRET(GL_STACK_UNDERFLOW);
|
|
|
|
CASESTRRET(GL_STACK_OVERFLOW);
|
2021-06-24 16:38:44 +00:00
|
|
|
CASESTRRET(GL_FRAMEBUFFER_UNDEFINED);
|
|
|
|
CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
|
|
|
|
CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
|
|
|
|
CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER);
|
|
|
|
CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER);
|
|
|
|
CASESTRRET(GL_FRAMEBUFFER_UNSUPPORTED);
|
|
|
|
CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE);
|
|
|
|
CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS);
|
2018-10-03 21:46:18 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check for GLX error.
|
|
|
|
*
|
|
|
|
* http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/
|
|
|
|
*/
|
|
|
|
static inline void gl_check_err_(const char *func, int line) {
|
|
|
|
GLenum err = GL_NO_ERROR;
|
|
|
|
|
|
|
|
while (GL_NO_ERROR != (err = glGetError())) {
|
|
|
|
const char *errtext = gl_get_err_str(err);
|
|
|
|
if (errtext) {
|
|
|
|
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
|
|
|
|
"GLX error at line %d: %s", line, errtext);
|
|
|
|
} else {
|
|
|
|
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
|
|
|
|
"GLX error at line %d: %d", line, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-16 01:19:48 +00:00
|
|
|
static inline void gl_clear_err(void) {
|
2020-06-06 19:27:25 +00:00
|
|
|
while (glGetError() != GL_NO_ERROR)
|
|
|
|
;
|
2019-05-16 01:19:48 +00:00
|
|
|
}
|
|
|
|
|
2018-10-03 21:46:18 +00:00
|
|
|
#define gl_check_err() gl_check_err_(__func__, __LINE__)
|
|
|
|
|
2021-06-24 16:38:44 +00:00
|
|
|
/**
|
|
|
|
* Check for GL framebuffer completeness.
|
|
|
|
*/
|
|
|
|
static inline bool gl_check_fb_complete_(const char *func, int line, GLenum fb) {
|
|
|
|
GLenum status = glCheckFramebufferStatus(fb);
|
|
|
|
|
|
|
|
if (status == GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *stattext = gl_get_err_str(status);
|
|
|
|
if (stattext) {
|
|
|
|
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
|
|
|
|
"Framebuffer attachment failed at line %d: %s", line, stattext);
|
|
|
|
} else {
|
|
|
|
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
|
|
|
|
"Framebuffer attachment failed at line %d: %d", line, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define gl_check_fb_complete(fb) gl_check_fb_complete_(__func__, __LINE__, (fb))
|
|
|
|
|
2018-10-03 21:46:18 +00:00
|
|
|
/**
|
|
|
|
* Check if a GLX extension exists.
|
|
|
|
*/
|
2019-01-26 23:07:59 +00:00
|
|
|
static inline bool gl_has_extension(const char *ext) {
|
2019-07-13 18:30:26 +00:00
|
|
|
int nexts = 0;
|
|
|
|
glGetIntegerv(GL_NUM_EXTENSIONS, &nexts);
|
|
|
|
for (int i = 0; i < nexts || !nexts; i++) {
|
2019-03-30 09:07:21 +00:00
|
|
|
const char *exti = (const char *)glGetStringi(GL_EXTENSIONS, (GLuint)i);
|
2019-07-13 18:30:26 +00:00
|
|
|
if (exti == NULL) {
|
2019-05-16 01:19:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (strcmp(ext, exti) == 0) {
|
2018-10-03 21:46:18 +00:00
|
|
|
return true;
|
2019-05-16 01:19:48 +00:00
|
|
|
}
|
2018-10-03 21:46:18 +00:00
|
|
|
}
|
2019-05-16 01:19:48 +00:00
|
|
|
gl_clear_err();
|
2018-10-03 21:46:18 +00:00
|
|
|
log_info("Missing GL extension %s.", ext);
|
|
|
|
return false;
|
|
|
|
}
|