backend: gl: implement the new new new backend interface

And implement a compatibility layer between the old/new interfaces so
rendering code is unchanged.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-04-09 17:55:45 +01:00
parent 8513369cff
commit c286d376d7
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
7 changed files with 379 additions and 689 deletions

View File

@ -37,6 +37,10 @@ struct gl_blur_context {
enum backend_image_format format;
};
// TODO(yshui) small optimization for kernel blur, if source and target are different,
// single pass blur can paint directly from source to target. Currently a temporary
// texture is always used.
/**
* Blur contents in a particular region.
*/
@ -307,9 +311,10 @@ static bool gl_blur_context_preallocate_textures(struct gl_blur_context *bctx,
return true;
}
/// Internal function that does the blur.
bool gl_blur_impl(struct gl_data *gd, struct coord origin, struct gl_texture *target,
struct backend_blur_args *args) {
bool gl_blur(struct backend_base *base, struct coord origin, image_handle target_,
struct backend_blur_args *args) {
auto gd = (struct gl_data *)base;
auto target = (struct gl_texture *)target_;
auto source = (struct gl_texture *)args->source_image;
auto bctx = (struct gl_blur_context *)args->blur_context;
log_trace("Blur size: %dx%d, method: %d", source->width, source->height, bctx->method);
@ -420,39 +425,6 @@ bool gl_blur_impl(struct gl_data *gd, struct coord origin, struct gl_texture *ta
return ret;
}
bool gl_blur(backend_t *base, double opacity, void *ctx, image_handle mask_, coord_t mask_dst,
const region_t *reg_blur, const region_t *reg_visible attr_unused) {
log_trace("Blurring, mask %p, mask_dst (%d, %d), reg_blur %p", mask_, mask_dst.x,
mask_dst.y, reg_blur);
auto mask = (struct backend_image *)mask_;
auto gd = (struct gl_data *)base;
// Fake a gl_texture for gd->back_texture
struct gl_texture temp_tex = {
.texture = gd->back_texture,
.width = gd->back_image.width,
.height = gd->back_image.height,
.y_inverted = true,
};
struct backend_mask mask_args = {
.image = mask ? (image_handle)mask->inner : NULL,
.origin = mask_dst,
.corner_radius = mask ? mask->corner_radius : 0,
.inverted = mask ? mask->color_inverted : false,
};
pixman_region32_init(&mask_args.region);
pixman_region32_copy(&mask_args.region, reg_blur);
pixman_region32_translate(&mask_args.region, -mask_dst.x, -mask_dst.y);
struct backend_blur_args args = {
.source_image = (image_handle)&temp_tex,
.opacity = opacity,
.mask = &mask_args,
.blur_context = ctx,
};
auto succeeded = gl_blur_impl(gd, (struct coord){0, 0}, &temp_tex, &args);
pixman_region32_fini(&mask_args.region);
return succeeded;
}
static inline void gl_free_blur_shader(struct gl_shader *shader) {
if (shader->prog) {
glDeleteProgram(shader->prog);
@ -890,7 +862,7 @@ void *gl_create_blur_context(backend_t *base, enum blur_method method,
out:
if (!success) {
gl_destroy_blur_context(&gd->base, ctx);
gl_destroy_blur_context(&gd->compat.base, ctx);
ctx = NULL;
}

View File

@ -13,6 +13,7 @@
#include "backend/backend.h"
#include "backend/backend_common.h"
#include "backend/compat.h"
#include "backend/gl/egl.h"
#include "backend/gl/gl_common.h"
#include "common.h"
@ -106,6 +107,7 @@ static bool egl_set_swap_interval(int interval, EGLDisplay dpy) {
return eglSwapInterval(dpy, interval);
}
struct backend_operations egl_ops;
/**
* Initialize OpenGL.
*/
@ -151,7 +153,8 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
}
eglext_init(gd->display);
init_backend_base(&gd->gl.base, ps);
init_backend_base(&gd->gl.compat.base, ps);
gd->gl.compat.base.ops = &egl_ops;
if (!eglext.has_EGL_KHR_image_pixmap) {
log_error("EGL_KHR_image_pixmap not available.");
goto end;
@ -226,12 +229,12 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
end:
if (!success) {
if (gd != NULL) {
egl_deinit(&gd->gl.base);
egl_deinit(&gd->gl.compat.base);
}
return NULL;
}
return &gd->gl.base;
return &gd->gl.compat.base;
}
static image_handle
@ -247,12 +250,9 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt) {
}
log_trace("Binding pixmap %#010x", pixmap);
auto wd = ccalloc(1, struct backend_image);
wd->max_brightness = 1;
auto inner = ccalloc(1, struct gl_texture);
inner->width = wd->ewidth = r->width;
inner->height = wd->eheight = r->height;
wd->inner = (struct backend_image_inner_base *)inner;
inner->width = r->width;
inner->height = r->height;
free(r);
log_debug("depth %d", fmt.visual_depth);
@ -275,30 +275,25 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt) {
// Create texture
inner->user_data = eglpixmap;
inner->texture = gl_new_texture();
inner->has_alpha = fmt.alpha_size != 0;
wd->opacity = 1;
wd->color_inverted = false;
wd->dim = 0;
wd->inner->refcount = 1;
glBindTexture(GL_TEXTURE_2D, inner->texture);
glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, *eglpixmap, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
gl_check_err();
return (image_handle)wd;
return (image_handle)inner;
err:
if (eglpixmap && *eglpixmap) {
eglDestroyImage(gd->display, *eglpixmap);
}
free(eglpixmap);
free(wd);
return NULL;
}
static void egl_present(backend_t *base, const region_t *region attr_unused) {
static bool egl_present(backend_t *base) {
struct egl_data *gd = (void *)base;
gl_present(base, region);
gl_finish_render(&gd->gl);
eglSwapBuffers(gd->display, gd->target_win);
return true;
}
static int egl_buffer_age(backend_t *base) {
@ -344,27 +339,43 @@ static void egl_diagnostics(backend_t *base) {
}
struct backend_operations egl_ops = {
.v2 =
{
.apply_alpha = gl_apply_alpha,
.back_buffer = gl_back_buffer,
.blit = gl_blit,
.blur = gl_blur,
.bind_pixmap = egl_bind_pixmap,
.clear = gl_clear,
.copy_area = gl_copy_area,
.copy_area_quantize = gl_copy_area_quantize,
.is_format_supported = gl_is_format_supported,
.image_capabilities = gl_image_capabilities,
.new_image = gl_new_image,
.release_image = gl_release_image,
.present = egl_present,
},
.init = egl_init,
.deinit = egl_deinit,
.root_change = gl_root_change,
.bind_pixmap = egl_bind_pixmap,
.release_image = gl_release_image,
.bind_pixmap = backend_compat_bind_pixmap,
.release_image = backend_compat_release_image,
.prepare = gl_prepare,
.compose = gl_compose,
.image_op = gl_image_op,
.compose = backend_compat_compose,
.image_op = backend_compat_image_op,
.set_image_property = default_set_image_property,
.clone_image = default_clone_image,
.blur = gl_blur,
.blur = backend_compat_blur,
.is_image_transparent = default_is_image_transparent,
.present = egl_present,
.present = backend_compat_present,
.buffer_age = egl_buffer_age,
.last_render_time = gl_last_render_time,
.create_shadow_context = gl_create_shadow_context,
.destroy_shadow_context = gl_destroy_shadow_context,
.create_shadow_context = backend_compat_create_shadow_context,
.destroy_shadow_context = backend_compat_destroy_shadow_context,
.render_shadow = backend_render_shadow_from_mask,
.shadow_from_mask = gl_shadow_from_mask,
.make_mask = gl_make_mask,
.fill = gl_fill,
.shadow_from_mask = backend_compat_shadow_from_mask,
.make_mask = backend_compat_make_mask,
.fill = backend_compat_fill,
.create_blur_context = gl_create_blur_context,
.destroy_blur_context = gl_destroy_blur_context,
.get_blur_size = gl_get_blur_size,

View File

@ -13,7 +13,6 @@
#include "common.h"
#include "compiler.h"
#include "config.h"
#include "kernel.h"
#include "log.h"
#include "region.h"
#include "types.h"
@ -164,18 +163,20 @@ GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_
return gl_create_program_from_strv(vert_shaders, frag_shaders);
}
void gl_destroy_window_shader(backend_t *backend_data attr_unused, void *shader) {
static void gl_destroy_window_shader_inner(struct gl_shader *shader) {
if (!shader) {
return;
}
auto pprogram = (struct gl_shader *)shader;
if (pprogram->prog) {
glDeleteProgram(pprogram->prog);
pprogram->prog = 0;
if (shader->prog) {
glDeleteProgram(shader->prog);
shader->prog = 0;
}
gl_check_err();
}
void gl_destroy_window_shader(backend_t *backend_data attr_unused, void *shader) {
gl_destroy_window_shader_inner(shader);
free(shader);
}
@ -371,13 +372,6 @@ static const struct gl_vertex_attribs_definition gl_blit_vertex_attribs = {
{GL_INT, vert_in_texcoord_loc, ((GLint *)NULL) + 2}},
};
// For when texture coordinates are the same as vertex coordinates
static const struct gl_vertex_attribs_definition gl_simple_vertex_attribs = {
.stride = sizeof(GLint) * 2,
.count = 2,
.attribs = {{GL_INT, vert_coord_loc, NULL}, {GL_INT, vert_in_texcoord_loc, NULL}},
};
/**
* Render a region with texture data.
*
@ -534,22 +528,12 @@ static int gl_lower_blit_args(struct gl_data *gd, struct coord origin,
auto img = (struct gl_texture *)args->source_image;
int nrects;
const rect_t *rects;
rect_t source_extent;
if (args->mask != NULL) {
rects = pixman_region32_rectangles(&args->mask->region, &nrects);
} else {
nrects = 1;
source_extent = (rect_t){0, 0, args->ewidth, args->eheight};
rects = &source_extent;
}
rects = pixman_region32_rectangles(&args->mask->region, &nrects);
if (!nrects) {
// Nothing to paint
return 0;
}
struct coord mask_origin = {};
if (args->mask != NULL) {
mask_origin = args->mask->origin;
}
struct coord mask_origin = args->mask->origin;
*coord = ccalloc(nrects * 16, GLint);
*indices = ccalloc(nrects * 6, GLuint);
gl_mask_rects_to_coords(origin, mask_origin, nrects, rects, *coord, *indices);
@ -557,7 +541,7 @@ static int gl_lower_blit_args(struct gl_data *gd, struct coord origin,
gl_y_flip_texture(nrects, *coord, img->height);
}
auto mask_image = args->mask ? (struct gl_texture *)args->mask->image : NULL;
auto mask_image = (struct gl_texture *)args->mask->image;
auto mask_texture = mask_image ? mask_image->texture : gd->default_mask_texture;
auto mask_sampler =
mask_image ? gd->samplers[GL_SAMPLER_BORDER] : gd->samplers[GL_SAMPLER_REPEAT];
@ -609,79 +593,144 @@ static int gl_lower_blit_args(struct gl_data *gd, struct coord origin,
return nrects;
}
static const struct gl_uniform_value default_blit_uniforms[] = {
[UNIFORM_OPACITY_LOC] = {.type = GL_FLOAT, .f = 1.0F},
[UNIFORM_INVERT_COLOR_LOC] = {.type = GL_INT, .i = 0},
[UNIFORM_TEX_LOC] = {.type = GL_TEXTURE_2D, .tu = {}},
[UNIFORM_EFFECTIVE_SIZE_LOC] = {.type = GL_FLOAT_VEC2, .f2 = {0.0F, 0.0F}}, // Must be set
[UNIFORM_DIM_LOC] = {.type = GL_FLOAT, .f = 0.0F},
[UNIFORM_BRIGHTNESS_LOC] = {.type = GL_TEXTURE_2D, .tu = {}},
[UNIFORM_MAX_BRIGHTNESS_LOC] = {.type = GL_FLOAT, .f = 1.0F},
[UNIFORM_CORNER_RADIUS_LOC] = {.type = GL_FLOAT, .f = 0.0F},
[UNIFORM_BORDER_WIDTH_LOC] = {.type = GL_FLOAT, .f = 0.0F},
[UNIFORM_TIME_LOC] = {.type = GL_FLOAT, .f = 0.0F},
[UNIFORM_MASK_TEX_LOC] = {.type = GL_TEXTURE_2D, .tu = {}}, // Must be set
[UNIFORM_MASK_OFFSET_LOC] = {.type = GL_FLOAT_VEC2, .f2 = {0.0F, 0.0F}},
[UNIFORM_MASK_CORNER_RADIUS_LOC] = {.type = GL_FLOAT, .f = 0.0F},
[UNIFORM_MASK_INVERTED_LOC] = {.type = GL_INT, .i = 0},
};
// TODO(yshui) make use of reg_visible
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,
const region_t *reg_visible attr_unused) {
bool gl_blit(backend_t *base, struct coord origin, image_handle target_,
struct backend_blit_args *args) {
auto gd = (struct gl_data *)base;
auto img = (struct backend_image *)image;
auto mask = (struct backend_image *)mask_;
auto source = (struct gl_texture *)args->source_image;
auto target = (struct gl_texture *)target_;
struct coord mask_offset = {.x = mask_dst.x - image_dst.x,
.y = mask_dst.y - image_dst.y};
log_trace("Mask is %p, texture %d", mask ? mask->inner : NULL,
mask ? ((struct gl_texture *)mask->inner)->texture : 0);
struct backend_mask mask_args = {
.image = mask ? (image_handle)mask->inner : NULL,
.origin = mask_offset,
.corner_radius = mask ? mask->corner_radius : 0,
.inverted = mask ? mask->color_inverted : false,
};
pixman_region32_init(&mask_args.region);
pixman_region32_copy(&mask_args.region, (region_t *)reg_tgt);
pixman_region32_translate(&mask_args.region, -mask_dst.x, -mask_dst.y);
struct backend_blit_args blit_args = {
.source_image = (image_handle)img->inner,
.mask = &mask_args,
.shader = (void *)img->shader ?: &gd->default_shader,
.opacity = img->opacity,
.color_inverted = img->color_inverted,
.ewidth = img->ewidth,
.eheight = img->eheight,
.dim = img->dim,
.corner_radius = img->corner_radius,
.border_width = img->border_width,
.max_brightness = img->max_brightness,
};
struct gl_shader *shader;
struct gl_uniform_value uniforms[NUMBER_OF_UNIFORMS] = {};
GLint *coord = NULL;
GLuint *indices = NULL;
int nrects = gl_lower_blit_args(gd, image_dst, &blit_args, &coord, &indices,
&shader, uniforms);
if (nrects > 0) {
gl_blit_inner(gd->back_fbo, nrects, coord, indices, &gl_blit_vertex_attribs,
shader, NUMBER_OF_UNIFORMS, uniforms);
if (source == &gd->back_image) {
log_error("Trying to blit from the back texture, this is not allowed");
return false;
}
pixman_region32_fini(&mask_args.region);
GLint *coord;
GLuint *indices;
struct gl_shader *shader;
struct gl_uniform_value uniforms[NUMBER_OF_UNIFORMS] = {};
int nrects =
gl_lower_blit_args(gd, origin, args, &coord, &indices, &shader, uniforms);
if (nrects == 0) {
return true;
}
if (!target->y_inverted) {
log_trace("Flipping target texture");
gl_y_flip_target(nrects, coord, target->height);
}
auto fbo = gl_bind_image_to_fbo(gd, target_);
gl_blit_inner(fbo, nrects, coord, indices, &gl_blit_vertex_attribs, shader,
NUMBER_OF_UNIFORMS, uniforms);
free(indices);
free(coord);
return true;
}
/// Copy areas by glBlitFramebuffer. This is only used to copy data from the back
/// buffer.
static bool gl_copy_area_blit_fbo(struct gl_data *gd, struct coord origin,
image_handle target, const region_t *region) {
gl_bind_image_to_fbo(gd, target);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
int nrects;
const rect_t *rects = pixman_region32_rectangles(region, &nrects);
for (ptrdiff_t i = 0; i < nrects; i++) {
// Remember GL back buffer is Y-up, but the destination image is only
// allowed to be Y-down
// clang-format off
glBlitFramebuffer(
rects[i].x1 , gd->back_image.height - rects[i].y1,
rects[i].x2 , gd->back_image.height - rects[i].y2,
rects[i].x1 + origin.x, rects[i].y1 + origin.y ,
rects[i].x2 + origin.x, rects[i].y2 + origin.y ,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
// clang-format on
}
gl_check_err();
return true;
}
/// Copy areas by drawing. This is the common path to copy from one texture to another.
static bool gl_copy_area_draw(struct gl_data *gd, struct coord origin,
image_handle target_handle, image_handle source_handle,
struct gl_shader *shader, const region_t *region) {
auto source = (struct gl_texture *)source_handle;
auto target = (struct gl_texture *)target_handle;
assert(source->y_inverted);
int nrects;
const rect_t *rects = pixman_region32_rectangles(region, &nrects);
if (nrects == 0) {
return true;
}
auto coord = ccalloc(16 * nrects, GLint);
auto indices = ccalloc(6 * nrects, GLuint);
gl_mask_rects_to_coords(origin, (struct coord){}, nrects, rects, coord, indices);
if (!target->y_inverted) {
gl_y_flip_target(nrects, coord, target->height);
}
struct gl_uniform_value uniforms[] = {
[UNIFORM_TEX_LOC] = {.type = GL_TEXTURE_2D,
.tu = {source->texture, gd->samplers[GL_SAMPLER_EDGE]}},
};
auto fbo = gl_bind_image_to_fbo(gd, target_handle);
gl_blit_inner(fbo, nrects, coord, indices, &gl_blit_vertex_attribs, shader,
ARR_SIZE(uniforms), uniforms);
free(indices);
free(coord);
return true;
}
bool gl_copy_area(backend_t *backend_data, struct coord origin, image_handle target,
image_handle source, const region_t *region) {
auto gd = (struct gl_data *)backend_data;
if ((struct gl_texture *)source == &gd->back_image) {
return gl_copy_area_blit_fbo(gd, origin, target, region);
}
return gl_copy_area_draw(gd, origin, target, source, &gd->copy_area_prog, region);
}
bool gl_copy_area_quantize(backend_t *backend_data, struct coord origin,
image_handle target_handle, image_handle source_handle,
const region_t *region) {
auto gd = (struct gl_data *)backend_data;
auto target = (struct gl_texture *)target_handle;
auto source = (struct gl_texture *)source_handle;
if (source->compat.format != BACKEND_IMAGE_FORMAT_PIXMAP_HIGH ||
source->compat.format == target->compat.format) {
return gl_copy_area(backend_data, origin, target_handle, source_handle, region);
}
return gl_copy_area_draw(gd, origin, target_handle, source_handle,
&gd->copy_area_with_dither_prog, region);
}
uint32_t gl_image_capabilities(backend_t *base, image_handle img) {
auto gd = (struct gl_data *)base;
auto inner = (struct gl_texture *)img;
if (&gd->back_image == inner) {
return BACKEND_IMAGE_CAP_DST;
}
if (inner->user_data) {
// user_data indicates that the image is a bound X pixmap
return BACKEND_IMAGE_CAP_SRC;
}
return BACKEND_IMAGE_CAP_SRC | BACKEND_IMAGE_CAP_DST;
}
bool gl_is_format_supported(backend_t *base attr_unused,
enum backend_image_format format attr_unused) {
return true;
}
/**
* Load a GLSL main program from shader strings.
*/
static bool gl_win_shader_from_stringv(const char **vshader_strv,
const char **fshader_strv, struct gl_shader *ret) {
static bool gl_shader_from_stringv(const char **vshader_strv, const char **fshader_strv,
struct gl_shader *ret) {
// Build program
ret->prog = gl_create_program_from_strv(vshader_strv, fshader_strv);
if (!ret->prog) {
@ -713,83 +762,19 @@ void gl_resize(struct gl_data *gd, int width, int height) {
assert(viewport_dimensions[0] >= gd->back_image.width);
assert(viewport_dimensions[1] >= gd->back_image.height);
glBindTexture(GL_TEXTURE_2D, gd->back_texture);
glTexImage2D(GL_TEXTURE_2D, 0, gd->back_format, width, height, 0, GL_BGR,
GL_UNSIGNED_BYTE, NULL);
backend_compat_resize(&gd->compat, (struct geometry){.width = width, .height = height});
gl_check_err();
}
/// Fill a given region in bound framebuffer.
/// @param[in] y_inverted whether the y coordinates in `clip` should be inverted
static void
gl_fill_inner(backend_t *base, struct color c, const region_t *clip, GLuint target) {
static const struct gl_vertex_attribs_definition vertex_attribs = {
.stride = sizeof(GLint) * 4,
.count = 1,
.attribs = {{GL_INT, vert_coord_loc, NULL}},
};
int nrects;
const rect_t *rect = pixman_region32_rectangles((region_t *)clip, &nrects);
xcb_pixmap_t gl_release_image(backend_t *base, image_handle image) {
auto inner = (struct gl_texture *)image;
auto gd = (struct gl_data *)base;
if (inner == &gd->back_image) {
return XCB_NONE;
}
auto coord = ccalloc(nrects * 16, GLint);
auto indices = ccalloc(nrects * 6, GLuint);
struct gl_uniform_value uniforms[] = {
[UNIFORM_COLOR_LOC] = {.type = GL_FLOAT_VEC4,
.f4 = {(float)c.red, (float)c.green, (float)c.blue,
(float)c.alpha}},
};
gl_mask_rects_to_coords_simple(nrects, rect, coord, indices);
gl_blit_inner(target, nrects, coord, indices, &vertex_attribs, &gd->fill_shader,
ARR_SIZE(uniforms), uniforms);
free(indices);
free(coord);
gl_check_err();
}
void gl_fill(backend_t *base, struct color c, const region_t *clip) {
auto gd = (struct gl_data *)base;
return gl_fill_inner(base, c, clip, gd->back_fbo);
}
image_handle gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
auto tex = ccalloc(1, struct gl_texture);
auto gd = (struct gl_data *)base;
auto img = ccalloc(1, struct backend_image);
default_init_backend_image(img, size.width, size.height);
log_trace("Creating mask texture %dx%d", size.width, size.height);
tex->width = size.width;
tex->height = size.height;
tex->texture = gl_new_texture();
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size.width, size.height, 0, GL_RED,
GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBlendFunc(GL_ONE, GL_ZERO);
glBindFramebuffer(GL_FRAMEBUFFER, gd->temp_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_inner(base, (struct color){1, 1, 1, 1}, reg, gd->temp_fbo);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
return (image_handle)img;
}
static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
auto gd = (struct gl_data *)base;
xcb_pixmap_t pixmap = inner->user_data ? inner->pixmap : XCB_NONE;
if (inner->user_data) {
gd->release_user_data(base, inner);
}
@ -799,20 +784,6 @@ static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
glDeleteTextures(2, inner->auxiliary_texture);
free(inner);
gl_check_err();
}
xcb_pixmap_t gl_release_image(backend_t *base, image_handle image) {
auto wd = (struct backend_image *)image;
auto inner = (struct gl_texture *)wd->inner;
inner->refcount--;
assert(inner->refcount >= 0);
xcb_pixmap_t pixmap = XCB_NONE;
if (inner->refcount == 0) {
pixmap = inner->pixmap;
gl_release_image_inner(base, inner);
}
free(wd);
return pixmap;
}
@ -829,10 +800,10 @@ static inline void gl_init_uniform_bitmask(struct gl_shader *shader) {
}
static bool gl_create_window_shader_inner(struct gl_shader *out_shader, const char *source) {
const char *vert_shaders[2] = {vertex_shader, NULL};
const char *frag_shaders[4] = {win_shader_glsl, masking_glsl, source, NULL};
const char *vert[2] = {vertex_shader, NULL};
const char *frag[] = {blit_shader_glsl, masking_glsl, source, NULL};
if (!gl_win_shader_from_stringv(vert_shaders, frag_shaders, out_shader)) {
if (!gl_shader_from_stringv(vert, frag, out_shader)) {
return false;
}
@ -911,12 +882,6 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glGenFramebuffers(1, &gd->temp_fbo);
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;
}
gd->default_mask_texture = gl_new_texture();
if (!gd->default_mask_texture) {
@ -930,7 +895,7 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glBindTexture(GL_TEXTURE_2D, 0);
// Initialize shaders
if (!gl_create_window_shader_inner(&gd->default_shader, win_shader_default)) {
if (!gl_create_window_shader_inner(&gd->default_shader, blit_shader_default)) {
log_error("Failed to create window shaders");
return false;
}
@ -951,43 +916,32 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glUseProgram(0);
gd->dithered_present = ps->o.dithered_present;
gd->dummy_prog.prog = gl_create_program_from_strv(
(const char *[]){vertex_shader, NULL}, (const char *[]){dummy_frag, NULL});
if (!gd->dummy_prog.prog) {
log_error("Failed to create the dummy shader");
gd->copy_area_prog.prog = gl_create_program_from_strv(
(const char *[]){vertex_shader, NULL}, (const char *[]){copy_area_frag, NULL});
if (!gd->copy_area_prog.prog) {
log_error("Failed to create the copy_area shader");
return false;
}
gd->dummy_prog.uniform_bitmask = (uint32_t)-1; // make sure our uniforms
// are not ignored.
if (gd->dithered_present) {
gd->present_prog.prog = gl_create_program_from_strv(
(const char *[]){vertex_shader, NULL},
(const char *[]){present_frag, dither_glsl, NULL});
} else {
gd->present_prog.prog = gd->dummy_prog.prog;
}
gd->present_prog.uniform_bitmask = (uint32_t)-1;
if (!gd->present_prog.prog) {
log_error("Failed to create the present shader");
gd->copy_area_prog.uniform_bitmask = (uint32_t)-1; // make sure our
// uniforms are not
// ignored.
glUseProgram(gd->copy_area_prog.prog);
glUniform1i(UNIFORM_TEX_LOC, 0);
glUniformMatrix4fv(UNIFORM_PROJECTION_LOC, 1, false, projection_matrix[0]);
gd->copy_area_with_dither_prog.prog = gl_create_program_from_strv(
(const char *[]){vertex_shader, NULL},
(const char *[]){copy_area_with_dither_frag, dither_glsl, NULL});
if (!gd->copy_area_with_dither_prog.prog) {
log_error("Failed to create the copy_area with dither shader");
return false;
}
gd->copy_area_with_dither_prog.uniform_bitmask = (uint32_t)-1;
glUseProgram(gd->dummy_prog.prog);
glUseProgram(gd->copy_area_with_dither_prog.prog);
glUniform1i(UNIFORM_TEX_LOC, 0);
glUniformMatrix4fv(UNIFORM_PROJECTION_LOC, 1, false, projection_matrix[0]);
if (gd->present_prog.prog != gd->dummy_prog.prog) {
glUseProgram(gd->present_prog.prog);
glUniformMatrix4fv(UNIFORM_PROJECTION_LOC, 1, false, projection_matrix[0]);
}
gd->shadow_shader.prog =
gl_create_program_from_str(vertex_shader, shadow_colorization_frag);
gd->shadow_shader.uniform_bitmask = (uint32_t)-1; // make sure our uniforms
// are not ignored.
glUseProgram(gd->shadow_shader.prog);
glUniform1i(UNIFORM_TEX_LOC, 0);
glUniformMatrix4fv(UNIFORM_PROJECTION_LOC, 1, false, projection_matrix[0]);
glBindFragDataLocation(gd->shadow_shader.prog, 0, "out_color");
gd->brightness_shader.prog =
gl_create_program_from_str(interpolating_vert, interpolating_frag);
@ -998,29 +952,10 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glUseProgram(gd->brightness_shader.prog);
glUniform1i(UNIFORM_TEX_LOC, 0);
glUniformMatrix4fv(UNIFORM_PROJECTION_LOC, 1, false, projection_matrix[0]);
glUseProgram(0);
// Set up the size and format of the back texture
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->back_fbo);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
const GLint *format = gd->dithered_present ? (const GLint[]){GL_RGB16, GL_RGBA16}
: (const GLint[]){GL_RGB8, GL_RGBA8};
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)) {
return false;
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gd->back_image.width = ps->root_width;
gd->back_image.height = ps->root_height;
glGenSamplers(GL_MAX_SAMPLERS, gd->samplers);
for (int i = 0; i < GL_MAX_SAMPLERS; i++) {
@ -1054,9 +989,11 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
gd->has_robustness = epoxy_has_gl_extension("GL_ARB_robustness");
gd->has_egl_image_storage = epoxy_has_gl_extension("GL_EXT_EGL_image_storage");
gd->back_image.y_inverted = false;
bool succeeded = backend_compat_init(&gd->compat, ps);
gl_check_err();
return true;
return succeeded;
}
void gl_deinit(struct gl_data *gd) {
@ -1065,33 +1002,26 @@ void gl_deinit(struct gl_data *gd) {
gd->logger = NULL;
}
if (gd->default_shader.prog) {
glDeleteProgram(gd->default_shader.prog);
gd->default_shader.prog = 0;
}
glDeleteProgram(gd->dummy_prog.prog);
if (gd->present_prog.prog != gd->dummy_prog.prog) {
glDeleteProgram(gd->present_prog.prog);
}
gd->dummy_prog.prog = 0;
gd->present_prog.prog = 0;
backend_compat_deinit(&gd->compat);
gl_destroy_window_shader_inner(&gd->default_shader);
glDeleteProgram(gd->copy_area_prog.prog);
glDeleteProgram(gd->copy_area_with_dither_prog.prog);
gd->copy_area_prog.prog = 0;
gd->copy_area_with_dither_prog.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;
glDeleteTextures(1, &gd->default_mask_texture);
glDeleteTextures(1, &gd->back_texture);
for (int i = 0; i < GL_MAX_SAMPLERS; i++) {
glDeleteSamplers(1, &gd->samplers[i]);
}
glDeleteFramebuffers(1, &gd->temp_fbo);
glDeleteFramebuffers(1, &gd->back_fbo);
glDeleteQueries(2, gd->frame_timing);
@ -1108,102 +1038,94 @@ GLuint gl_new_texture(void) {
return texture;
}
/// Actually duplicate a texture into a new one, if this texture is shared
static inline void gl_image_decouple(backend_t *base, struct backend_image *img) {
if (img->inner->refcount == 1) {
return;
bool gl_clear(backend_t *backend_data, image_handle target, struct color color) {
auto gd = (struct gl_data *)backend_data;
auto fbo = gl_bind_image_to_fbo(gd, target);
auto target_image = (struct gl_texture *)target;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
if (target_image->compat.format == BACKEND_IMAGE_FORMAT_MASK) {
glClearColor((GLfloat)color.alpha, 0, 0, 1);
} else {
glClearColor((GLfloat)color.red, (GLfloat)color.green,
(GLfloat)color.blue, (GLfloat)color.alpha);
}
log_trace("Decoupling image %p", img);
auto gd = (struct gl_data *)base;
auto inner = (struct gl_texture *)img->inner;
auto new_tex = ccalloc(1, struct gl_texture);
*new_tex = *inner;
new_tex->pixmap = XCB_NONE;
new_tex->texture = gl_new_texture();
new_tex->refcount = 1;
new_tex->user_data = gd->decouple_texture_user_data(base, new_tex->user_data);
// Prepare the new texture
glBindTexture(GL_TEXTURE_2D, new_tex->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, new_tex->width, new_tex->height, 0,
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->temp_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);
glUseProgram(gd->dummy_prog.prog);
glBindTexture(GL_TEXTURE_2D, inner->texture);
// clang-format off
GLint coord[] = {
0, 0, // top left
new_tex->width, 0, // top right
new_tex->width, new_tex->height, // bottom right
0, new_tex->height, // bottom left
};
GLuint indices[] = {0, 1, 2, 2, 3, 0};
// clang-format on
struct gl_uniform_value uniforms[] = {
[UNIFORM_TEX_LOC] = {.type = GL_TEXTURE_2D,
.tu = {inner->texture, gd->samplers[GL_SAMPLER_EDGE]}},
};
gl_blit_inner(gd->temp_fbo, 1, coord, indices, &gl_simple_vertex_attribs,
&gd->dummy_prog, ARR_SIZE(uniforms), uniforms);
img->inner = (struct backend_image_inner_base *)new_tex;
inner->refcount--;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
return true;
}
static void gl_image_apply_alpha(backend_t *base, struct backend_image *img,
const region_t *reg_op, double alpha) {
// Result color = 0 (GL_ZERO) + alpha (GL_CONSTANT_ALPHA) * original color
auto inner = (struct gl_texture *)img->inner;
image_handle gl_back_buffer(struct backend_base *base) {
auto gd = (struct gl_data *)base;
return (image_handle)&gd->back_image;
}
image_handle gl_new_image(backend_t *backend_data attr_unused,
enum backend_image_format format, geometry_t size) {
auto tex = ccalloc(1, struct gl_texture);
log_trace("Creating texture %dx%d", size.width, size.height);
tex->width = size.width;
tex->height = size.height;
tex->texture = gl_new_texture();
tex->y_inverted = true;
tex->user_data = NULL;
tex->pixmap = XCB_NONE;
GLint gl_format;
switch (format) {
case BACKEND_IMAGE_FORMAT_PIXMAP: gl_format = GL_RGBA8; break;
case BACKEND_IMAGE_FORMAT_PIXMAP_HIGH: gl_format = GL_RGBA16; break;
case BACKEND_IMAGE_FORMAT_MASK: gl_format = GL_R8; break;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex->texture);
glTexImage2D(GL_TEXTURE_2D, 0, gl_format, size.width, size.height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, NULL);
if (format == BACKEND_IMAGE_FORMAT_MASK) {
// Mask images needs a border, so sampling from outside of the texture
// will correctly return 0
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
(GLfloat[]){0, 0, 0, 0});
}
glBindTexture(GL_TEXTURE_2D, 0);
return (image_handle)tex;
}
bool gl_apply_alpha(backend_t *base, image_handle target, double alpha, const region_t *reg_op) {
auto gd = (struct gl_data *)base;
static const struct gl_vertex_attribs_definition vertex_attribs = {
.stride = sizeof(GLint) * 4,
.count = 1,
.attribs = {{GL_INT, vert_coord_loc, NULL}},
};
if (alpha == 1.0 || !pixman_region32_not_empty(reg_op)) {
return true;
}
gl_bind_image_to_fbo(gd, target);
// Result color = 0 (GL_ZERO) + alpha (GL_CONSTANT_ALPHA) * original color
glBlendFunc(GL_ZERO, GL_CONSTANT_ALPHA);
glBlendColor(0, 0, 0, (GLclampf)alpha);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->temp_fbo);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
inner->texture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
gl_fill_inner(base, (struct color){0, 0, 0, 0}, reg_op, gd->temp_fbo);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
void gl_present(backend_t *base, const region_t *region) {
auto gd = (struct gl_data *)base;
int nrects;
const rect_t *rect = pixman_region32_rectangles((region_t *)region, &nrects);
const rect_t *rect = pixman_region32_rectangles(reg_op, &nrects);
auto coord = ccalloc(nrects * 16, GLint);
auto indices = ccalloc(nrects * 6, GLuint);
gl_mask_rects_to_coords_simple(nrects, rect, coord, indices);
// Our back_texture is in Xorg coordinate system, but the GL back buffer is in
// GL clip space, which has the Y axis flipped.
gl_y_flip_target(nrects, coord, gd->back_image.height);
struct gl_uniform_value uniforms[] = {
[UNIFORM_TEX_LOC] = {.type = GL_TEXTURE_2D,
.tu = {gd->back_texture, gd->samplers[GL_SAMPLER_EDGE]}},
};
gl_blit_inner(0, nrects, coord, indices, &gl_blit_vertex_attribs,
&gd->present_prog, ARR_SIZE(uniforms), uniforms);
glEndQuery(GL_TIME_ELAPSED);
gd->current_frame_timing ^= 1;
struct gl_uniform_value uniforms[] = {
[UNIFORM_COLOR_LOC] = {.type = GL_FLOAT_VEC4, .f4 = {0, 0, 0, 0}},
};
gl_mask_rects_to_coords_simple(nrects, rect, coord, indices);
gl_blit_inner(gd->temp_fbo, nrects, coord, indices, &vertex_attribs,
&gd->fill_shader, ARR_SIZE(uniforms), uniforms);
free(indices);
free(coord);
gl_check_err();
free(coord);
free(indices);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
return true;
}
bool gl_last_render_time(backend_t *base, struct timespec *ts) {
@ -1224,225 +1146,6 @@ bool gl_last_render_time(backend_t *base, struct timespec *ts) {
return true;
}
bool gl_image_op(backend_t *base, enum image_operations op, image_handle image,
const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
auto tex = (struct backend_image *)image;
switch (op) {
case IMAGE_OP_APPLY_ALPHA:
gl_image_decouple(base, tex);
assert(tex->inner->refcount == 1);
gl_image_apply_alpha(base, tex, reg_op, *(double *)arg);
break;
}
return true;
}
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;
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, BACKEND_IMAGE_FORMAT_MASK, &args);
if (!ctx->blur_context) {
log_error("Failed to create shadow context");
free(ctx);
return NULL;
}
}
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;
if (ctx_->blur_context) {
gl_destroy_blur_context(base, (struct backend_blur_context *)ctx_->blur_context);
}
free(ctx_);
}
image_handle gl_shadow_from_mask(backend_t *base, image_handle mask_,
struct backend_shadow_context *sctx, struct color color) {
log_debug("Create shadow from mask");
auto gd = (struct gl_data *)base;
auto mask = (struct backend_image *)mask_;
auto inner = (struct gl_texture *)mask->inner;
auto gsctx = (struct gl_shadow_context *)sctx;
int radius = (int)gsctx->radius;
if (mask->eheight != inner->height || mask->ewidth != inner->width ||
mask->dim != 0 || mask->max_brightness != 1 || mask->border_width != 0 ||
mask->opacity != 1 || mask->shader != NULL) {
log_error("Unsupported mask properties for shadow generation");
return NULL;
}
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();
new_inner->has_alpha = inner->has_alpha;
new_inner->y_inverted = true;
auto new_img = ccalloc(1, struct backend_image);
default_init_backend_image(new_img, new_inner->width, new_inner->height);
new_img->inner = (struct backend_image_inner_base *)new_inner;
new_img->inner->refcount = 1;
// We apply the mask properties by blitting a pure white texture with the mask
// image as mask.
auto white_texture = gl_new_texture();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, white_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE,
(GLbyte[]){'\xff'});
glBindTexture(GL_TEXTURE_2D, 0);
auto source_texture = gl_new_texture();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, source_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, gd->temp_fbo);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
source_texture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
if (mask->color_inverted) {
// If the mask is inverted, clear the source_texture to white, so the
// "outside" of the mask would be correct.
// FIXME(yshui): we should make masks clamp to border, and set border
// color to 0.
glClearColor(1, 1, 1, 1);
} else {
glClearColor(0, 0, 0, 1);
}
glClear(GL_COLOR_BUFFER_BIT);
{
// clang-format off
// | vertex coordinates | 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};
auto shader = &gd->default_shader;
struct gl_uniform_value uniforms[ARR_SIZE(default_blit_uniforms)];
memcpy(uniforms, default_blit_uniforms, sizeof(default_blit_uniforms));
uniforms[UNIFORM_EFFECTIVE_SIZE_LOC].f2[0] = (float)inner->width;
uniforms[UNIFORM_EFFECTIVE_SIZE_LOC].f2[1] = (float)inner->height;
uniforms[UNIFORM_TEX_LOC].tu =
(struct gl_texture_unit){white_texture, gd->samplers[GL_SAMPLER_EDGE]};
uniforms[UNIFORM_MASK_TEX_LOC].tu = (struct gl_texture_unit){
inner->texture, gd->samplers[GL_SAMPLER_BORDER]};
uniforms[UNIFORM_MASK_INVERTED_LOC].i = mask->color_inverted;
uniforms[UNIFORM_MASK_CORNER_RADIUS_LOC].f = (float)mask->corner_radius;
gl_blit_inner(gd->temp_fbo, 1, coords, indices, &gl_blit_vertex_attribs,
shader, ARR_SIZE(default_blit_uniforms), uniforms);
glDeleteTextures(1, &white_texture);
}
gl_check_err();
auto tmp_texture = source_texture;
if (gsctx->blur_context != NULL) {
glActiveTexture(GL_TEXTURE0);
tmp_texture = gl_new_texture();
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, gd->temp_fbo);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tmp_texture, 0);
region_t reg_blur;
pixman_region32_init_rect(&reg_blur, 0, 0, (unsigned int)new_inner->width,
(unsigned int)new_inner->height);
// A fake gl_texture for the target texture (tmp_texture).
struct gl_texture target = {
.texture = tmp_texture,
.width = new_inner->width,
.height = new_inner->height,
.y_inverted = true,
};
// A fake gl_texture for the source texture (source_texture).
struct gl_texture source = {
.texture = source_texture,
.width = new_inner->width,
.height = new_inner->height,
.y_inverted = true,
};
struct backend_mask mask_args = {
.image = NULL,
.origin = {0, 0},
.corner_radius = 0,
.inverted = false,
};
mask_args.region = reg_blur;
struct backend_blur_args args = {
.blur_context = gsctx->blur_context,
.mask = &mask_args,
.source_image = (image_handle)&source,
.opacity = 1.0,
};
gl_blur_impl(gd, (struct coord){}, &target, &args);
pixman_region32_fini(&reg_blur);
}
// Colorize the shadow with color.
log_debug("Colorize shadow");
// First prepare the new texture
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, gd->temp_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);
struct gl_uniform_value uniforms[] = {
[UNIFORM_TEX_LOC] = {.type = GL_TEXTURE_2D,
.tu = {tmp_texture, gd->samplers[GL_SAMPLER_EDGE]}},
// The shadow color is converted to the premultiplied format to respect the
// globally set glBlendFunc and thus get the correct and expected result.
[UNIFORM_COLOR_LOC] = {.type = GL_FLOAT_VEC4,
.f4 = {(float)(color.red * color.alpha),
(float)(color.green * color.alpha),
(float)(color.blue * color.alpha), (float)color.alpha}},
};
// 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
gl_blit_inner(gd->temp_fbo, 1, coord, indices, &gl_simple_vertex_attribs,
&gd->shadow_shader, ARR_SIZE(uniforms), uniforms);
glDeleteTextures(1, (GLuint[]){source_texture});
if (tmp_texture != source_texture) {
glDeleteTextures(1, (GLuint[]){tmp_texture});
}
gl_check_err();
return (image_handle)new_img;
}
enum device_status gl_device_status(backend_t *base) {
auto gd = (struct gl_data *)base;
if (!gd->has_robustness) {

View File

@ -8,6 +8,7 @@
#include "backend/backend.h"
#include "backend/backend_common.h"
#include "backend/compat.h"
#include "log.h"
#include "region.h"
@ -50,8 +51,7 @@ struct gl_shader {
/// @brief Wrapper of a bound GL texture.
struct gl_texture {
int refcount;
bool has_alpha;
struct backend_compat_image_base compat;
GLuint texture;
int width, height;
/// Whether the texture is Y-inverted
@ -79,7 +79,7 @@ enum gl_sampler {
};
struct gl_data {
backend_t base;
struct backend_compat_base compat;
// If we are using proprietary NVIDIA driver
bool is_nvidia;
// If ARB_robustness extension is present
@ -91,14 +91,11 @@ struct gl_data {
struct gl_shader default_shader;
struct gl_shader brightness_shader;
struct gl_shader fill_shader;
struct gl_shader shadow_shader;
GLuint back_texture, back_fbo;
GLuint temp_fbo;
GLint back_format;
GLuint frame_timing[2];
int current_frame_timing;
struct gl_shader present_prog;
struct gl_shader dummy_prog;
struct gl_shader copy_area_prog;
struct gl_shader copy_area_with_dither_prog;
GLuint samplers[GL_MAX_SAMPLERS];
bool dithered_present;
@ -149,11 +146,11 @@ void gl_destroy_window_shader(backend_t *backend_data, void *shader);
uint64_t gl_get_shader_attributes(backend_t *backend_data, void *shader);
bool gl_last_render_time(backend_t *backend_data, struct timespec *time);
/**
* @brief Render a region with texture data.
*/
void gl_compose(backend_t *, image_handle image, coord_t image_dst, image_handle mask,
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible);
bool gl_blit(backend_t *base, struct coord origin, image_handle target,
struct backend_blit_args *args);
image_handle gl_new_image(backend_t *backend_data attr_unused,
enum backend_image_format format, geometry_t size);
bool gl_clear(backend_t *backend_data, image_handle target, struct color color);
void gl_root_change(backend_t *base, session_t *);
@ -164,36 +161,36 @@ void gl_deinit(struct gl_data *gd);
GLuint gl_new_texture(void);
bool gl_image_op(backend_t *base, enum image_operations op, image_handle image,
const region_t *reg_op, const region_t *reg_visible, void *arg);
xcb_pixmap_t gl_release_image(backend_t *base, image_handle image);
image_handle gl_make_mask(backend_t *base, geometry_t size, const region_t *reg);
image_handle gl_clone(backend_t *base, image_handle image, const region_t *reg_visible);
bool gl_blur(backend_t *base, double opacity, void *ctx, image_handle mask,
coord_t mask_dst, const region_t *reg_blur, const region_t *reg_visible);
bool gl_blur_impl(struct gl_data *gd, struct coord origin, struct gl_texture *target,
struct backend_blur_args *args);
void gl_apply_alpha(backend_t *base, image_handle target, double alpha, const region_t *reg_op);
bool gl_blur(struct backend_base *gd, struct coord origin, image_handle target,
struct backend_blur_args *args);
bool gl_copy_area(backend_t *backend_data, struct coord origin, image_handle target,
image_handle source, const region_t *region);
bool gl_copy_area_quantize(backend_t *backend_data, struct coord origin,
image_handle target_handle, image_handle source_handle,
const region_t *region);
bool gl_apply_alpha(backend_t *base, image_handle target, double alpha, const region_t *reg_op);
image_handle gl_back_buffer(struct backend_base *base);
uint32_t gl_image_capabilities(backend_t *base, image_handle img);
bool gl_is_format_supported(backend_t *base, enum backend_image_format format);
void *gl_create_blur_context(backend_t *base, enum blur_method,
enum backend_image_format format, void *args);
void gl_destroy_blur_context(backend_t *base, void *ctx);
struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius);
void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shadow_context *ctx);
image_handle gl_shadow_from_mask(backend_t *base, image_handle mask,
struct backend_shadow_context *sctx, struct color color);
void gl_get_blur_size(void *blur_context, int *width, int *height);
void gl_fill(backend_t *base, struct color, const region_t *clip);
void gl_present(backend_t *base, const region_t *);
enum device_status gl_device_status(backend_t *base);
#define gl_check_fb_complete(fb) gl_check_fb_complete_(__func__, __LINE__, (fb))
static inline bool gl_check_fb_complete_(const char *func, int line, GLenum fb);
static inline void gl_finish_render(struct gl_data *gd) {
glEndQuery(GL_TIME_ELAPSED);
gd->current_frame_timing ^= 1;
}
/// Return a FBO with `image` bound to the first color attachment. `GL_DRAW_FRAMEBUFFER`
/// will be bound to the returned FBO.
static inline GLuint gl_bind_image_to_fbo(struct gl_data *gd, image_handle image_) {
@ -311,7 +308,7 @@ static const GLuint vert_in_texcoord_loc = 1;
#define GLSL(version, ...) GLSL_(version, __VA_ARGS__)
#define QUOTE(...) #__VA_ARGS__
extern const char vertex_shader[], blend_with_mask_frag[], masking_glsl[], dummy_frag[],
present_frag[], fill_frag[], fill_vert[], interpolating_frag[], interpolating_vert[],
win_shader_glsl[], win_shader_default[], present_vertex_shader[], dither_glsl[],
shadow_colorization_frag[];
extern const char vertex_shader[], blend_with_mask_frag[], masking_glsl[],
copy_area_frag[], copy_area_with_dither_frag[], fill_frag[], fill_vert[],
interpolating_frag[], interpolating_vert[], blit_shader_glsl[], blit_shader_default[],
present_vertex_shader[], dither_glsl[];

View File

@ -24,6 +24,7 @@
#include "backend/backend.h"
#include "backend/backend_common.h"
#include "backend/compat.h"
#include "backend/gl/gl_common.h"
#include "backend/gl/glx.h"
#include "common.h"
@ -225,6 +226,7 @@ static bool glx_set_swap_interval(int interval, Display *dpy, GLXDrawable drawab
return vsync_enabled;
}
struct backend_operations glx_ops;
/**
* Initialize OpenGL.
*/
@ -232,7 +234,8 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
bool success = false;
glxext_init(ps->c.dpy, ps->c.screen);
auto gd = ccalloc(1, struct _glx_data);
init_backend_base(&gd->gl.base, ps);
init_backend_base(&gd->gl.compat.base, ps);
gd->gl.compat.base.ops = &glx_ops;
gd->target_win = target;
@ -358,11 +361,11 @@ end:
}
if (!success) {
glx_deinit(&gd->gl.base);
glx_deinit(&gd->gl.compat.base);
return NULL;
}
return &gd->gl.base;
return &gd->gl.compat.base;
}
static image_handle
@ -389,12 +392,9 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt) {
}
log_trace("Binding pixmap %#010x", pixmap);
auto wd = ccalloc(1, struct backend_image);
default_init_backend_image(wd, r->width, r->height);
auto inner = ccalloc(1, struct gl_texture);
inner->width = r->width;
inner->height = r->height;
wd->inner = (struct backend_image_inner_base *)inner;
free(r);
struct glx_fbconfig_cache *cached_fbconfig = NULL;
@ -453,27 +453,25 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt) {
// Create texture
inner->user_data = glxpixmap;
inner->texture = gl_new_texture();
inner->has_alpha = fmt.alpha_size != 0;
wd->inner->refcount = 1;
glBindTexture(GL_TEXTURE_2D, inner->texture);
glXBindTexImageEXT(base->c->dpy, *glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
gl_check_err();
return (image_handle)wd;
return (image_handle)inner;
err:
if (glxpixmap && *glxpixmap) {
glXDestroyPixmap(base->c->dpy, *glxpixmap);
}
free(glxpixmap);
free(wd);
return NULL;
}
static void glx_present(backend_t *base, const region_t *region attr_unused) {
static bool glx_present(backend_t *base) {
struct _glx_data *gd = (void *)base;
gl_present(base, region);
gl_finish_render(&gd->gl);
glXSwapBuffers(base->c->dpy, gd->target_win);
return true;
}
static int glx_buffer_age(backend_t *base) {
@ -526,27 +524,43 @@ static void glx_diagnostics(backend_t *base) {
}
struct backend_operations glx_ops = {
.v2 =
{
.apply_alpha = gl_apply_alpha,
.back_buffer = gl_back_buffer,
.bind_pixmap = glx_bind_pixmap,
.blit = gl_blit,
.blur = gl_blur,
.clear = gl_clear,
.copy_area = gl_copy_area,
.copy_area_quantize = gl_copy_area_quantize,
.image_capabilities = gl_image_capabilities,
.is_format_supported = gl_is_format_supported,
.new_image = gl_new_image,
.present = glx_present,
.release_image = gl_release_image,
},
.init = glx_init,
.deinit = glx_deinit,
.root_change = gl_root_change,
.bind_pixmap = glx_bind_pixmap,
.release_image = gl_release_image,
.bind_pixmap = backend_compat_bind_pixmap,
.release_image = backend_compat_release_image,
.prepare = gl_prepare,
.compose = gl_compose,
.image_op = gl_image_op,
.compose = backend_compat_compose,
.image_op = backend_compat_image_op,
.set_image_property = default_set_image_property,
.clone_image = default_clone_image,
.blur = gl_blur,
.blur = backend_compat_blur,
.is_image_transparent = default_is_image_transparent,
.present = glx_present,
.present = backend_compat_present,
.buffer_age = glx_buffer_age,
.last_render_time = gl_last_render_time,
.create_shadow_context = gl_create_shadow_context,
.destroy_shadow_context = gl_destroy_shadow_context,
.create_shadow_context = backend_compat_create_shadow_context,
.destroy_shadow_context = backend_compat_destroy_shadow_context,
.render_shadow = backend_render_shadow_from_mask,
.shadow_from_mask = gl_shadow_from_mask,
.make_mask = gl_make_mask,
.fill = gl_fill,
.shadow_from_mask = backend_compat_shadow_from_mask,
.make_mask = backend_compat_make_mask,
.fill = backend_compat_fill,
.create_blur_context = gl_create_blur_context,
.destroy_blur_context = gl_destroy_blur_context,
.get_blur_size = gl_get_blur_size,

View File

@ -2,22 +2,24 @@
// Don't macro expand bool!
#undef bool
// clang-format off
const char dummy_frag[] = GLSL(330,
const char copy_area_frag[] = GLSL(330,
layout(location = UNIFORM_TEX_LOC)
uniform sampler2D tex;
in vec2 texcoord;
void main() {
gl_FragColor = texelFetch(tex, ivec2(texcoord.xy), 0);
vec2 texsize = textureSize(tex, 0);
gl_FragColor = texture2D(tex, texcoord / texsize, 0);
}
);
const char present_frag[] = GLSL(330,
const char copy_area_with_dither_frag[] = GLSL(330,
layout(location = UNIFORM_TEX_LOC)
uniform sampler2D tex;
in vec2 texcoord;
vec4 dither(vec4, vec2);
void main() {
gl_FragColor = dither(texelFetch(tex, ivec2(texcoord.xy), 0), texcoord);
vec2 texsize = textureSize(tex, 0);
gl_FragColor = dither(texture2D(tex, texcoord / texsize, 0), gl_FragCoord.xy);
}
);
@ -104,7 +106,7 @@ const char masking_glsl[] = GLSL(330,
return mask.r;
}
);
const char win_shader_glsl[] = GLSL(330,
const char blit_shader_glsl[] = GLSL(330,
layout(location = UNIFORM_OPACITY_LOC)
uniform float opacity;
layout(location = UNIFORM_DIM_LOC)
@ -177,7 +179,7 @@ const char win_shader_glsl[] = GLSL(330,
}
);
const char win_shader_default[] = GLSL(330,
const char blit_shader_default[] = GLSL(330,
in vec2 texcoord;
uniform sampler2D tex;
vec4 default_post_processing(vec4 c);
@ -201,6 +203,7 @@ const char vertex_shader[] = GLSL(330,
texcoord = in_texcoord;
}
);
/// Add dithering for downsampling from 16-bit color to 8-bit color.
const char dither_glsl[] = GLSL(330,
// Stolen from: https://www.shadertoy.com/view/7sfXDn
float bayer2(vec2 a) {
@ -225,16 +228,4 @@ const char dither_glsl[] = GLSL(330,
return vec4(c + dithered * bayer(coord) / 255.0);
}
);
const char shadow_colorization_frag[] = GLSL(330,
layout(location = UNIFORM_COLOR_LOC)
uniform vec4 color;
layout(location = UNIFORM_TEX_LOC)
uniform sampler2D tex;
in vec2 texcoord;
out vec4 out_color;
void main() {
vec4 c = texelFetch(tex, ivec2(texcoord), 0);
out_color = c.r * color;
}
);
// clang-format on

View File

@ -385,6 +385,8 @@ bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color
struct backend_shadow_context *sctx) {
assert(!w->shadow_image);
assert(w->shadow);
log_trace("Binding shadow for %#010x (%s), color: %f %f %f %F", w->base.id,
w->name, c.red, c.green, c.blue, c.alpha);
if ((w->corner_radius == 0 && w->bounding_shaped == false) ||
b->ops->shadow_from_mask == NULL) {
w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, sctx, c);