From 60c10790d7035e303d2f01c82589baaf21e5cc5a Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sat, 9 Mar 2019 01:39:18 +0000 Subject: [PATCH] new glx: initial implementation This is basically just adapting old code to the new interface. A lot of the functions are still stubs, but the basic stuffs is working. What remains to be done: * Implement gl_image_op. (It should do the operation lazily, only update the flags on the texture. Actual processing will be delayed until composition.) * Implement gl_copy. Now it just return the same image after incrementing the refcount. It should actually copy the image data structure so it can have a separate set of flags. Signed-off-by: Yuxuan Shui --- src/backend/backend.h | 4 +- src/backend/gl/gl_common.c | 278 ++++++++++++++++++++++++------- src/backend/gl/gl_common.h | 68 +++++--- src/backend/gl/glx.c | 323 +++++++++++-------------------------- src/backend/gl/glx.h | 4 +- 5 files changed, 366 insertions(+), 311 deletions(-) diff --git a/src/backend/backend.h b/src/backend/backend.h index 4dbacbf..b213ae9 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -125,7 +125,7 @@ struct backend_operations { /// Free resources associated with an image data structure void (*release_image)(backend_t *backend_data, void *img_data) - __attribute__((nonnull(1, 2))); + attr_nonnull(1, 2); // =========== Query =========== @@ -136,7 +136,7 @@ struct backend_operations { /// window (e.g. when using a custom shader with the glx backend), so we only now /// the transparency after the window is rendered bool (*is_image_transparent)(backend_t *backend_data, void *image_data) - __attribute__((nonnull(1, 2))); + attr_nonnull(1, 2); /// Get the age of the buffer content we are currently rendering ontop /// of. The buffer that has just been `present`ed has a buffer age of 1. diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index f2aad85..2da8acd 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -11,11 +11,11 @@ #include "common.h" #include "compiler.h" #include "config.h" +#include "kernel.h" #include "log.h" #include "region.h" #include "string_utils.h" #include "utils.h" -#include "kernel.h" #include "backend/gl/gl_common.h" @@ -39,8 +39,6 @@ } \ while (0) -struct gl_data {}; - GLuint gl_create_shader(GLenum shader_type, const char *shader_str) { log_trace("===\n%s\n===", shader_str); @@ -156,7 +154,7 @@ GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_ return prog; } -void gl_free_prog_main(session_t *ps, gl_win_shader_t *pprogram) { +static void gl_free_prog_main(gl_win_shader_t *pprogram) { if (!pprogram) return; if (pprogram->prog) { @@ -193,18 +191,36 @@ unsigned char *gl_take_screenshot(session_t *ps, int *out_length) { } /** - * @brief Render a region with texture data. + * Render a region with texture data. + * + * @param ptex the texture + * @param dst_x,dst_y the top left corner of region where this texture + * should go. In Xorg coordinate system (important!). + * @param reg_tgt the clip region, also in Xorg coordinate system + * @param reg_visible ignored */ -bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int width, - int height, int z, double opacity, bool argb, bool neg, - const region_t *reg_tgt, const gl_win_shader_t *shader) { +void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y, + const region_t *reg_tgt, const region_t *reg_visible) { + + gl_texture_t *ptex = image_data; + struct gl_data *gd = (void *)base; + + // 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 if (!ptex || !ptex->texture) { log_error("Missing texture."); - return false; + return; } - // argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT == - // ps->psglx->fbconfigs[ptex->depth]->texture_fmt); + // dst_y is the top coordinate, in OpenGL, it is the upper bound of the y + // coordinate. + dst_y = gd->height - dst_y; + auto dst_y2 = dst_y - ptex->height; + bool dual_texture = false; // It's required by legacy versions of OpenGL to enable texture target @@ -212,28 +228,28 @@ bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int widt glEnable(ptex->target); // Enable blending if needed - if (opacity < 1.0 || argb) { + if (ptex->opacity < 1.0 || ptex->has_alpha) { glEnable(GL_BLEND); // Needed for handling opacity of ARGB texture glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - // This is all weird, but X Render is using premultiplied ARGB format, and - // we need to use those things to correct it. Thanks to derhass for help. + // X pixmap is in premultiplied ARGB format, so + // we need to do this to correct it. + // Thanks to derhass for help. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(opacity, opacity, opacity, opacity); + glColor4f(ptex->opacity, ptex->opacity, ptex->opacity, ptex->opacity); } - // Programmable path - if (shader->prog) { - glUseProgram(shader->prog); - if (shader->unifm_opacity >= 0) - glUniform1f(shader->unifm_opacity, opacity); - if (shader->unifm_invert_color >= 0) - glUniform1i(shader->unifm_invert_color, neg); - if (shader->unifm_tex >= 0) - glUniform1i(shader->unifm_tex, 0); + if (gd->win_shader.prog) { + glUseProgram(gd->win_shader.prog); + if (gd->win_shader.unifm_opacity >= 0) + glUniform1f(gd->win_shader.unifm_opacity, ptex->opacity); + if (gd->win_shader.unifm_invert_color >= 0) + glUniform1i(gd->win_shader.unifm_invert_color, ptex->color_inverted); + if (gd->win_shader.unifm_tex >= 0) + glUniform1i(gd->win_shader.unifm_tex, 0); } // log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", @@ -248,12 +264,29 @@ bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int widt } // Painting - P_PAINTREG_START(reg_tgt, crect) { + int nrects; + const rect_t *rects; + rects = pixman_region32_rectangles((region_t *)reg_tgt, &nrects); + + glBegin(GL_QUADS); + for (int ri = 0; ri < nrects; ++ri) { + // Y-flip. Note after this, crect.y1 > crect.y2 + rect_t crect = rects[ri]; + crect.y1 = gd->height - crect.y1; + crect.y2 = gd->height - crect.y2; + // Calculate texture coordinates - GLfloat texture_x1 = (double)(crect.x1 - dx + x); - GLfloat texture_y1 = (double)(crect.y1 - dy + y); - GLfloat texture_x2 = texture_x1 + (double)(crect.x2 - crect.x1); - GLfloat texture_y2 = texture_y1 + (double)(crect.y2 - crect.y1); + // (texture_x1, texture_y1), texture coord for the _bottom left_ corner + GLfloat texture_x1 = crect.x1 - dst_x; + GLfloat texture_y1 = crect.y2 - dst_y2; + GLfloat texture_x2 = texture_x1 + crect.x2 - crect.x1; + GLfloat texture_y2 = texture_y1 + crect.y1 - crect.y2; + + // X pixmaps might be Y inverted, invert the texture coordinates + if (ptex->y_inverted) { + texture_y1 = ptex->height - texture_y1; + texture_y2 = ptex->height - texture_y2; + } if (ptex->target == GL_TEXTURE_2D) { // GL_TEXTURE_2D coordinates are 0-1 @@ -265,15 +298,9 @@ bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int widt // Vertex coordinates GLint vx1 = crect.x1; - GLint vy1 = crect.y1; + GLint vy1 = crect.y2; GLint vx2 = crect.x2; - GLint vy2 = crect.y2; - - // X pixmaps might be Y inverted, invert the texture coordinates - if (ptex->y_inverted) { - texture_y1 = 1.0 - texture_y1; - texture_y2 = 1.0 - texture_y2; - } + GLint vy2 = crect.y1; // log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d", // ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); @@ -285,10 +312,11 @@ bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int widt for (int i = 0; i < 4; i++) { glTexCoord2f(texture_x[i], texture_y[i]); - glVertex3i(vx[i], vy[i], z); + glVertex3i(vx[i], vy[i], 0); } } - P_PAINTREG_END(); + + glEnd(); // Cleanup glBindTexture(ptex->target, 0); @@ -309,7 +337,7 @@ bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int widt gl_check_err(); - return true; + return; } bool gl_dim_reg(session_t *ps, int dx, int dy, int width, int height, float z, @@ -354,6 +382,7 @@ static inline int gl_gen_texture(GLenum tex_tgt, int width, int height, GLuint * return 0; } +#if 0 /** * Blur contents in a particular region. * @@ -479,8 +508,10 @@ bool gl_blur_dst(session_t *ps, const gl_cap_t *cap, int dx, int dy, int width, // Texture coordinates const GLfloat texture_x1 = (crect.x1 - dx) * texfac_x; const GLfloat texture_y1 = (crect.y1 - dy) * texfac_y; - const GLfloat texture_x2 = texture_x1 + (crect.x2 - crect.x1) * texfac_x; - const GLfloat texture_y2 = texture_y1 + (crect.y2 - crect.y1) * texfac_y; + const GLfloat texture_x2 = + texture_x1 + (crect.x2 - crect.x1) * texfac_x; + const GLfloat texture_y2 = + texture_y1 + (crect.y2 - crect.y1) * texfac_y; // Vertex coordinates // For passes before the last one, we are drawing into a buffer, @@ -537,6 +568,7 @@ end: return ret; } +#endif /** * Set clipping region on the target window. @@ -596,7 +628,7 @@ int gl_win_shader_from_string(session_t *ps, const char *vshader_str, /** * Callback to run on root window size change. */ -void gl_resize(int width, int height) { +void gl_resize(struct gl_data *gd, int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); @@ -604,6 +636,8 @@ void gl_resize(int width, int height) { glOrtho(0, width, 0, height, -1000.0, 1000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + gd->height = height; + gd->width = width; } static void attr_unused gl_destroy_win_shader(session_t *ps, gl_win_shader_t *shader) { @@ -616,6 +650,7 @@ static void attr_unused gl_destroy_win_shader(session_t *ps, gl_win_shader_t *sh shader->unifm_tex = -1; } +#if 0 /** * Initialize GL blur filters. * @@ -652,17 +687,22 @@ bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_ca "void main() {\n" " vec4 sum = vec4(0.0, 0.0, 0.0, " "0.0);\n"; - static const char *FRAG_SHADER_BLUR_ADD = - " sum += float(%.7g) * %s(tex_scr, vec2(gl_TexCoord[0].x + offset_x " - "* float(%d), gl_TexCoord[0].y + offset_y * float(%d)));\n"; - static const char *FRAG_SHADER_BLUR_ADD_GPUSHADER4 = - " sum += float(%.7g) * %sOffset(tex_scr, vec2(gl_TexCoord[0].x, " - "gl_TexCoord[0].y), ivec2(%d, %d));\n"; - static const char *FRAG_SHADER_BLUR_SUFFIX = - " sum += %s(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * " - "factor_center;\n" - " gl_FragColor = sum / (factor_center + float(%.7g));\n" - "}\n"; + static const char *FRAG_SHADER_BLUR_ADD = " sum += float(%.7g) * %s(tex_scr, " + "vec2(gl_TexCoord[0].x + offset_x " + "* float(%d), gl_TexCoord[0].y + " + "offset_y * float(%d)));\n"; + static const char *FRAG_SHADER_BLUR_ADD_GPUSHADER4 = " sum += float(%.7g) * " + "%sOffset(tex_scr, " + "vec2(gl_TexCoord[0].x, " + "gl_TexCoord[0].y), " + "ivec2(%d, %d));\n"; + static const char *FRAG_SHADER_BLUR_SUFFIX = " sum += %s(tex_scr, " + "vec2(gl_TexCoord[0].x, " + "gl_TexCoord[0].y)) * " + "factor_center;\n" + " gl_FragColor = sum / " + "(factor_center + float(%.7g));\n" + "}\n"; const bool use_texture_rect = !cap->non_power_of_two_texture; const char *sampler_type = (use_texture_rect ? "sampler2DRect" : "sampler2D"); @@ -685,7 +725,8 @@ bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_ca int nele = width * height - 1; unsigned int len = strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) + - strlen(extension) + (strlen(shader_add) + strlen(texture_func) + 42) * nele + + strlen(extension) + + (strlen(shader_add) + strlen(texture_func) + 42) * nele + strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1; char *shader_str = ccalloc(len, char); char *pc = shader_str; @@ -703,7 +744,8 @@ bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_ca continue; } sum += val; - sprintf(pc, shader_add, val, texture_func, k - width / 2, j - height / 2); + sprintf(pc, shader_add, val, texture_func, k - width / 2, + j - height / 2); pc += strlen(pc); assert(strlen(shader_str) < len); } @@ -728,8 +770,10 @@ bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_ca } // Get uniform addresses - pass->unifm_factor_center = - glGetUniformLocationChecked(pass->prog, "factor_center"); + pass->unifm_factor_center = glGetUniformLocationChecked(pass->prog, "fact" + "or_" + "cent" + "er"); if (!ps->o.glx_use_gpushader4) { pass->unifm_offset_x = glGetUniformLocationChecked(pass->prog, "offset_x"); @@ -752,3 +796,121 @@ err: free(lc_numeric_old); return false; } +#endif + +bool gl_init(struct gl_data *gd, session_t *ps) { + // Initialize GLX data structure + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + gd->blur_shader[i] = (gl_blur_shader_t){.frag_shader = 0, + .prog = 0, + .unifm_offset_x = -1, + .unifm_offset_y = -1, + .unifm_factor_center = -1}; + } + + gd->non_power_of_two_texture = gl_has_extension("GL_ARB_texture_non_power_of_" + "two"); + + // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles + // in regions don't overlap, so we must use stencil buffer to make sure + // we don't paint a region for more than one time, I think? + if (!ps->o.glx_no_stencil) { + GLint val = 0; + glGetIntegerv(GL_STENCIL_BITS, &val); + if (!val) { + log_error("Target window doesn't have stencil buffer."); + return false; + } + } + + // Render preparations + gl_resize(gd, ps->root_width, ps->root_height); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_BLEND); + + if (!ps->o.glx_no_stencil) { + // Initialize stencil buffer + glClear(GL_STENCIL_BUFFER_BIT); + glDisable(GL_STENCIL_TEST); + glStencilMask(0x1); + glStencilFunc(GL_EQUAL, 0x1, 0x1); + } + + // Clear screen + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // glXSwapBuffers(ps->dpy, get_tgt_window(ps)); + + // Initialize blur filters + // gl_create_blur_filters(ps, gd->blur_shader, &gd->cap); + + return true; +} + +static inline void gl_free_blur_shader(gl_blur_shader_t *shader) { + if (shader->prog) { + glDeleteShader(shader->prog); + } + if (shader->frag_shader) { + glDeleteShader(shader->frag_shader); + } + + shader->prog = 0; + shader->frag_shader = 0; +} + +void gl_deinit(struct gl_data *gd) { + // Free GLSL shaders/programs + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + gl_free_blur_shader(&gd->blur_shader[i]); + } + + gl_free_prog_main(&gd->win_shader); + + gl_check_err(); +} + +GLuint gl_new_texture(GLenum target) { + GLuint texture; + glGenTextures(1, &texture); + if (!texture) { + log_error("Failed to generate texture"); + return 0; + } + + glBindTexture(target, texture); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(target, 0); + + return texture; +} + +/// stub for backend_operations::image_op +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) { + return true; +} + +/// stub for backend_operations::copy +void *gl_copy(backend_t *base, const void *image_data, const region_t *reg_visible) { + struct gl_texture *t = (void *)image_data; + t->refcount++; + return (void *)image_data; +} + +bool gl_is_image_transparent(backend_t *base, void *image_data) { + gl_texture_t *img = image_data; + return img->has_alpha; +} + +/// stub for backend_operations::blur +bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur, + const region_t *reg_visible) { + return true; +} diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index c177cd8..24e5365 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -1,15 +1,18 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui #pragma once -#include -#include #include #include +#include +#include -#include "region.h" +#include "backend/backend.h" +#include "config.h" #include "log.h" +#include "region.h" -#define CASESTRRET(s) case s: return #s +#define CASESTRRET(s) \ + case s: return #s // Program and uniforms for window shader typedef struct { @@ -39,18 +42,26 @@ typedef struct { /// @brief Wrapper of a binded GLX texture. typedef struct gl_texture { + double opacity; + int refcount; GLuint texture; GLenum target; unsigned width; unsigned height; unsigned depth; bool y_inverted; + bool has_alpha; + bool color_inverted; } gl_texture_t; -// OpenGL capabilities -typedef struct gl_cap { +struct gl_data { + backend_t base; + // Height and width of the viewport + int height, width; + gl_win_shader_t win_shader; + gl_blur_shader_t blur_shader[MAX_BLUR_PASS]; bool non_power_of_two_texture; -} gl_cap_t; +}; typedef struct { /// Framebuffer used for blurring. @@ -70,25 +81,42 @@ typedef struct session session_t; GLuint gl_create_shader(GLenum shader_type, const char *shader_str); GLuint gl_create_program(const GLuint *const shaders, int nshaders); -GLuint -gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str); +GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str); + /** * @brief Render a region with texture data. */ -bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int width, - int height, int z, double opacity, bool argb, bool neg, - const region_t *reg_tgt, const gl_win_shader_t *shader); +void gl_compose(backend_t *, void *ptex, int dst_x, int dst_y, const region_t *reg_tgt, + const region_t *reg_visible); bool gl_load_prog_main(session_t *ps, const char *vshader_str, const char *fshader_str, gl_win_shader_t *pprogram); -void gl_free_prog_main(session_t *ps, gl_win_shader_t *prog); unsigned char *gl_take_screenshot(session_t *ps, int *out_length); -void gl_resize(int width, int height); -bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_cap_t *cap); +void gl_resize(struct gl_data *, int width, int height); +//bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_cap_t *cap); GLuint glGetUniformLocationChecked(GLuint p, const char *name); +bool gl_init(struct gl_data *gd, session_t *); +void gl_deinit(struct gl_data *gd); + +GLuint gl_new_texture(GLenum target); + +bool gl_image_op(backend_t *base, enum image_operations op, void *image_data, + const region_t *reg_op, const region_t *reg_visible, void *arg); + +void *gl_copy(backend_t *base, const void *image_data, const region_t *reg_visible); + +bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur, + const region_t *reg_visible); + +bool gl_is_image_transparent(backend_t *base, void *image_data); + +static inline void gl_delete_texture(GLuint texture) { + glDeleteTextures(1, &texture); +} + /** * Get a textual representation of an OpenGL error. */ @@ -147,13 +175,3 @@ static inline bool gl_has_extension(const char *ext) { log_info("Missing GL extension %s.", ext); return false; } - -static inline void gl_free_blur_shader(gl_blur_shader_t *shader) { - if (shader->prog) - glDeleteShader(shader->prog); - if (shader->frag_shader) - glDeleteShader(shader->frag_shader); - - shader->prog = 0; - shader->frag_shader = 0; -} diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index 62c6832..c371021 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -21,6 +21,7 @@ #include #include "backend/backend.h" +#include "backend/backend_common.h" #include "backend/gl/gl_common.h" #include "backend/gl/glx.h" #include "common.h" @@ -32,28 +33,23 @@ #include "win.h" #include "x.h" -struct _glx_win_data { +struct _glx_image_data { gl_texture_t texture; GLXPixmap glpixmap; xcb_pixmap_t pixmap; }; struct _glx_data { - backend_t base; + struct gl_data gl; + Display *display; + int screen; + int target_win; int glx_event; int glx_error; GLXContext ctx; - gl_cap_t cap; - gl_win_shader_t win_shader; - gl_blur_shader_t blur_shader[MAX_BLUR_PASS]; - - void (*glXBindTexImage)(Display *display, GLXDrawable drawable, int buffer, - const int *attrib_list); - void (*glXReleaseTexImage)(Display *display, GLXDrawable drawable, int buffer); }; -struct glx_fbconfig_info * -glx_find_fbconfig(Display *dpy, int screen, struct xvisual_info m) { +struct glx_fbconfig_info *glx_find_fbconfig(Display *dpy, int screen, struct xvisual_info m) { log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", m.red_size, m.blue_size, m.green_size, m.alpha_size, m.visual_depth); @@ -160,69 +156,47 @@ glx_find_fbconfig(Display *dpy, int screen, struct xvisual_info m) { } /** - * @brief Release binding of a texture. + * Free a glx_texture_t. */ -static void glx_release_pixmap(struct _glx_data *gd, Display *dpy, struct _glx_win_data *wd) { +void glx_release_image(backend_t *base, void *image_data) { + struct _glx_image_data *wd = image_data; + struct _glx_data *gd = (void *)base; + wd->texture.refcount--; + if (wd->texture.refcount != 0) { + return; + } // Release binding if (wd->glpixmap && wd->texture.texture) { glBindTexture(wd->texture.target, wd->texture.texture); - gd->glXReleaseTexImage(dpy, wd->glpixmap, GLX_FRONT_LEFT_EXT); + glXReleaseTexImageEXT(gd->display, wd->glpixmap, GLX_FRONT_LEFT_EXT); glBindTexture(wd->texture.target, 0); } // Free GLX Pixmap if (wd->glpixmap) { - glXDestroyPixmap(dpy, wd->glpixmap); + glXDestroyPixmap(gd->display, wd->glpixmap); wd->glpixmap = 0; } - gl_check_err(); -} - -/** - * Free a glx_texture_t. - */ -void glx_release_win(void *backend_data, session_t *ps, win *w, void *win_data) { - struct _glx_win_data *wd = win_data; - struct _glx_data *gd = backend_data; - glx_release_pixmap(gd, ps->dpy, wd); - glDeleteTextures(1, &wd->texture.texture); + gl_delete_texture(wd->texture.texture); // Free structure itself free(wd); -} -/** - * Free GLX part of win. - */ -static inline void free_win_res_glx(session_t *ps, win *w) { - /*free_paint_glx(ps, &w->paint);*/ - /*free_paint_glx(ps, &w->shadow_paint);*/ - /*free_glx_bc(ps, &w->glx_blur_cache);*/ + gl_check_err(); } /** * Destroy GLX related resources. */ -void glx_deinit(void *backend_data, session_t *ps) { - struct _glx_data *gd = backend_data; +void glx_deinit(backend_t *base) { + struct _glx_data *gd = (void *)base; - // Free all GLX resources of windows - for (win *w = ps->list; w; w = w->next) - free_win_res_glx(ps, w); - - // Free GLSL shaders/programs - for (int i = 0; i < MAX_BLUR_PASS; ++i) { - gl_free_blur_shader(&gd->blur_shader[i]); - } - - gl_free_prog_main(ps, &gd->win_shader); - - gl_check_err(); + gl_deinit(&gd->gl); // Destroy GLX context if (gd->ctx) { - glXDestroyContext(ps->dpy, gd->ctx); + glXDestroyContext(gd->display, gd->ctx); gd->ctx = 0; } @@ -232,13 +206,15 @@ void glx_deinit(void *backend_data, session_t *ps) { /** * Initialize OpenGL. */ -backend_t *backend_glx_init(session_t *ps) { +static backend_t *glx_init(session_t *ps) { bool success = false; glxext_init(ps->dpy, ps->scr); auto gd = ccalloc(1, struct _glx_data); - gd->base.c = ps->c; - gd->base.root = ps->root; - gd->base.ops = NULL; // TODO + gd->gl.base.c = ps->c; + gd->gl.base.root = ps->root; + gd->display = ps->dpy; + gd->screen = ps->scr; + gd->target_win = ps->overlay != XCB_NONE ? ps->overlay : ps->root; XVisualInfo *pvis = NULL; @@ -275,15 +251,6 @@ backend_t *backend_glx_init(session_t *ps) { goto end; } - // Initialize GLX data structure - for (int i = 0; i < MAX_BLUR_PASS; ++i) { - gd->blur_shader[i] = (gl_blur_shader_t){.frag_shader = -1, - .prog = -1, - .unifm_offset_x = -1, - .unifm_offset_y = -1, - .unifm_factor_center = -1}; - } - // Get GLX context gd->ctx = glXCreateContext(ps->dpy, pvis, NULL, GL_TRUE); @@ -313,93 +280,51 @@ backend_t *backend_glx_init(session_t *ps) { p_DebugMessageCallback(glx_debug_msg_callback, ps); #endif - // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles - // in regions don't overlap, so we must use stencil buffer to make sure - // we don't paint a region for more than one time, I think? - if (!ps->o.glx_no_stencil) { - GLint val = 0; - glGetIntegerv(GL_STENCIL_BITS, &val); - if (!val) { - log_error("Target window doesn't have stencil buffer."); - goto end; - } - } - - // Check GL_ARB_texture_non_power_of_two, requires a GLX context and - // must precede FBConfig fetching - gd->cap.non_power_of_two_texture = gl_has_extension("GL_ARB_texture_non_" - "power_of_two"); - - gd->glXBindTexImage = (void *)glXGetProcAddress((const GLubyte *)"glXBindTexImage" - "EXT"); - gd->glXReleaseTexImage = (void *)glXGetProcAddress((const GLubyte *)"glXReleaseTe" - "xImageEXT"); - if (!gd->glXBindTexImage || !gd->glXReleaseTexImage) { - log_error("Failed to acquire glXBindTexImageEXT() and/or " - "glXReleaseTexImageEXT(), make sure your OpenGL supports" - "GLX_EXT_texture_from_pixmap"); + if (!gl_init(&gd->gl, ps)) { + log_error("Failed to setup OpenGL"); goto end; } - // Render preparations - gl_resize(ps->root_width, ps->root_height); - - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glDisable(GL_BLEND); - - if (!ps->o.glx_no_stencil) { - // Initialize stencil buffer - glClear(GL_STENCIL_BUFFER_BIT); - glDisable(GL_STENCIL_TEST); - glStencilMask(0x1); - glStencilFunc(GL_EQUAL, 0x1, 0x1); - } - - // Clear screen - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // glXSwapBuffers(ps->dpy, get_tgt_window(ps)); - - // Initialize blur filters - // gl_create_blur_filters(ps, gd->blur_shader, &gd->cap); - success = true; end: - if (pvis) + if (pvis) { XFree(pvis); + } if (!success) { - glx_deinit(gd, ps); + glx_deinit(&gd->gl.base); return NULL; } - return &gd->base; + return &gd->gl.base; } -void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { - struct _glx_data *gd = backend_data; +static void *glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, + struct xvisual_info fmt, bool owned) { + struct _glx_data *gd = (void *)base; // Retrieve pixmap parameters, if they aren't provided - if (w->g.depth > OPENGL_MAX_DEPTH) { + if (fmt.visual_depth > OPENGL_MAX_DEPTH) { log_error("Requested depth %d higher than max possible depth %d.", - w->g.depth, OPENGL_MAX_DEPTH); + fmt.visual_depth, OPENGL_MAX_DEPTH); return false; } - auto wd = ccalloc(1, struct _glx_win_data); - wd->pixmap = xcb_generate_id(ps->c); - xcb_composite_name_window_pixmap(ps->c, w->id, wd->pixmap); - if (!wd->pixmap) { - log_error("Failed to get pixmap for window %#010x", w->id); - goto err; + auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), NULL); + if (!r) { + log_error("Invalid pixmap %#010x", pixmap); + return NULL; } - auto visual_info = x_get_visual_info(ps->c, w->a.visual); - auto fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, visual_info); + auto wd = ccalloc(1, struct _glx_image_data); + wd->pixmap = pixmap; + wd->texture.width = r->width; + wd->texture.height = r->height; + free(r); + + auto fbcfg = glx_find_fbconfig(gd->display, gd->screen, fmt); if (!fbcfg) { - log_error("Couldn't find FBConfig with requested visual %x", w->a.visual); + log_error("Couldn't find FBConfig with requested visual %x", fmt.visual); goto err; } @@ -407,7 +332,7 @@ void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { // Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean // of the bits in texture_tgts GLenum tex_tgt = 0; - if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts && gd->cap.non_power_of_two_texture) + if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts && gd->gl.non_power_of_two_texture) tex_tgt = GLX_TEXTURE_2D_EXT; else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts) tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; @@ -416,7 +341,7 @@ void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { else tex_tgt = GLX_TEXTURE_2D_EXT; - log_debug("depth %d, tgt %#x, rgba %d\n", w->g.depth, tex_tgt, + log_debug("depth %d, tgt %#x, rgba %d\n", fmt.visual_depth, tex_tgt, (GLX_TEXTURE_FORMAT_RGBA_EXT == fbcfg->texture_fmt)); GLint attrs[] = { @@ -431,122 +356,69 @@ void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE); wd->texture.y_inverted = fbcfg->y_inverted; - wd->glpixmap = glXCreatePixmap(ps->dpy, fbcfg->cfg, wd->pixmap, attrs); + wd->glpixmap = glXCreatePixmap(gd->display, fbcfg->cfg, wd->pixmap, attrs); free(fbcfg); if (!wd->glpixmap) { - log_error("Failed to create glpixmap for window %#010x", w->id); + log_error("Failed to create glpixmap for pixmap %#010x", pixmap); goto err; } // Create texture + wd->texture.texture = gl_new_texture(wd->texture.target); + wd->texture.opacity = 1; + wd->texture.depth = fmt.visual_depth; + wd->texture.color_inverted = false; + wd->texture.has_alpha = fmt.alpha_size != 0; + wd->texture.refcount = 1; + glBindTexture(wd->texture.target, wd->texture.texture); + glXBindTexImageEXT(gd->display, wd->glpixmap, GLX_FRONT_LEFT_EXT, NULL); + glBindTexture(wd->texture.target, 0); - GLuint texture = 0; - GLuint target = wd->texture.target; - glGenTextures(1, &texture); - if (!texture) { - log_error("Failed to generate texture for window %#010x", w->id); - goto err; - } - - glBindTexture(target, texture); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindTexture(target, 0); - - wd->texture.texture = texture; - wd->texture.width = w->widthb; - wd->texture.height = w->heightb; + gl_check_err(); return wd; err: - if (wd->pixmap && wd->pixmap != w->id) { - xcb_free_pixmap(ps->c, wd->pixmap); - } if (wd->glpixmap) { - glXDestroyPixmap(ps->dpy, wd->glpixmap); + glXDestroyPixmap(gd->display, wd->glpixmap); + } + if (owned) { + xcb_free_pixmap(base->c, pixmap); } free(wd); return NULL; } -/** - * Bind a X pixmap to an OpenGL texture. - */ -void glx_render_win(void *backend_data, session_t *ps, win *w, void *win_data, - const region_t *reg_paint) { - struct _glx_data *gd = backend_data; - struct _glx_win_data *wd = win_data; - - assert(wd->pixmap); - assert(wd->glpixmap); - assert(wd->texture.texture); - - glBindTexture(wd->texture.target, wd->texture.texture); - gd->glXBindTexImage(ps->dpy, wd->glpixmap, GLX_FRONT_LEFT_EXT, NULL); - glBindTexture(wd->texture.target, 0); - - gl_check_err(); +static void glx_present(backend_t *base) { + struct _glx_data *gd = (void *)base; + glXSwapBuffers(gd->display, gd->target_win); } -void glx_present(void *backend_data, session_t *ps) { - glXSwapBuffers(ps->dpy, ps->overlay != XCB_NONE ? ps->overlay : ps->root); -} - -int glx_buffer_age(void *backend_data, session_t *ps) { - if (ps->o.glx_swap_method == SWAPM_BUFFER_AGE) { - unsigned int val; - glXQueryDrawable(ps->dpy, get_tgt_window(ps), GLX_BACK_BUFFER_AGE_EXT, &val); - return (int)val ?: -1; - } else { +static int glx_buffer_age(backend_t *base) { + if (!glxext.has_GLX_EXT_buffer_age) { return -1; } + + struct _glx_data *gd = (void *)base; + unsigned int val; + glXQueryDrawable(gd->display, gd->target_win, GLX_BACK_BUFFER_AGE_EXT, &val); + return (int)val ?: -1; } -void glx_compose(void *backend_data, session_t *ps, win *w, void *win_data, - int dst_x, int dst_y, const region_t *region) { - struct _glx_data *gd = backend_data; - struct _glx_win_data *wd = win_data; - - // OpenGL and Xorg uses different coordinate systems. - // First, We need to flip the y axis of the paint region - region_t region_yflipped; - pixman_region32_init(®ion_yflipped); - pixman_region32_copy(®ion_yflipped, (region_t *)region); - - int nrects; - auto rect = pixman_region32_rectangles(®ion_yflipped, &nrects); - for (int i = 0; i < nrects; i++) { - auto tmp = rect[i].y1; - rect[i].y1 = ps->root_height - rect[i].y2; - rect[i].y2 = ps->root_height - tmp; - } - dump_region(®ion_yflipped); - - // Then, we still need to convert the origin of painting. - // Note, in GL coordinates, we need to specified the bottom left corner of the - // rectangle, while what we get from the arguments are the top left corner. - gl_compose(&wd->texture, 0, 0, dst_x, ps->root_height - dst_y - w->heightb, w->widthb, - w->heightb, 0, 1, true, false, ®ion_yflipped, &gd->win_shader); - pixman_region32_fini(®ion_yflipped); -} - -struct backend_operations glx_ops; - -/* backend_info_t glx_backend = { */ -/* .init = glx_init, */ -/* .deinit = glx_deinit, */ -/* .prepare_win = glx_prepare_win, */ -/* .render_win = glx_render_win, */ -/* .release_win = glx_release_win, */ -/* .present = glx_present, */ -/* .compose = glx_compose, */ -/* .is_win_transparent = default_is_win_transparent, */ -/* .is_frame_transparent = default_is_frame_transparent, */ -/* .buffer_age = glx_buffer_age, */ -/* .max_buffer_age = 5, // XXX why? */ -/* }; */ +struct backend_operations glx_ops = { + .init = glx_init, + .deinit = glx_deinit, + .bind_pixmap = glx_bind_pixmap, + .release_image = glx_release_image, + .compose = gl_compose, + .image_op = gl_image_op, + .copy = gl_copy, + .blur = gl_blur, + .is_image_transparent = gl_is_image_transparent, + .present = glx_present, + .buffer_age = glx_buffer_age, + .render_shadow = default_backend_render_shadow, + .max_buffer_age = 5, // Why? +}; /** * Check if a GLX extension exists. @@ -605,12 +477,13 @@ void glxext_init(Display *dpy, int screen) { check_ext(GLX_MESA_swap_control); check_ext(GLX_EXT_swap_control); check_ext(GLX_EXT_texture_from_pixmap); + check_ext(GLX_EXT_buffer_age); #undef check_ext #define lookup(name) (name = (__typeof__(name))glXGetProcAddress((GLubyte *)#name)) // Checking if the returned function pointer is NULL is not really necessary, - // or maybe not even useful, since glXGetProcAddress might always return something. - // We are doing it just for completeness' sake. + // or maybe not even useful, since glXGetProcAddress might always return + // something. We are doing it just for completeness' sake. if (!lookup(glXGetVideoSyncSGI) || !lookup(glXWaitVideoSyncSGI)) { glxext.has_GLX_SGI_video_sync = false; } diff --git a/src/backend/gl/glx.h b/src/backend/gl/glx.h index 9b7b955..173daf4 100644 --- a/src/backend/gl/glx.h +++ b/src/backend/gl/glx.h @@ -2,7 +2,8 @@ // Copyright (c) Yuxuan Shui #pragma once #include -// Older version of glx.h defines function prototypes... +// Older version of glx.h defines function prototypes for these extensions... +// Rename them to avoid conflicts #define glXSwapIntervalMESA glXSwapIntervalMESA_ #define glXBindTexImageEXT glXBindTexImageEXT_ #define glXReleaseTexImageEXT glXReleaseTexImageEXT @@ -51,6 +52,7 @@ struct glxext_info { bool has_GLX_MESA_swap_control; bool has_GLX_EXT_swap_control; bool has_GLX_EXT_texture_from_pixmap; + bool has_GLX_EXT_buffer_age; }; extern struct glxext_info glxext;