backend interface: Add visible hint

Pass the visible region of the image to the backend, so the backend
could optimize based on that information.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-03-04 21:37:22 +00:00
parent a2ae36c1d6
commit c57f267535
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
5 changed files with 217 additions and 116 deletions

View File

@ -6,9 +6,9 @@
#include "common.h"
#include "compiler.h"
#include "config.h"
#include "log.h"
#include "region.h"
#include "win.h"
#include "log.h"
backend_init_fn backend_list[NUM_BKEND] = {
[BKEND_XRENDER] = backend_xrender_init,
@ -37,16 +37,19 @@ region_t get_damage(session_t *ps) {
/// paint all windows
void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
region_t region;
// All painting will be limited to the damage, if _some_ of
// the paints bleed out of the damage region, it will destroy
// part of the image we want to reuse
region_t reg_damage;
if (!ignore_damage) {
region = get_damage(ps);
reg_damage = get_damage(ps);
} else {
pixman_region32_init(&region);
pixman_region32_copy(&region, &ps->screen_reg);
pixman_region32_init(&reg_damage);
pixman_region32_copy(&reg_damage, &ps->screen_reg);
}
if (!pixman_region32_not_empty(&region)) {
pixman_region32_fini(&region);
if (!pixman_region32_not_empty(&reg_damage)) {
pixman_region32_fini(&reg_damage);
return;
}
@ -54,27 +57,26 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
static struct timespec last_paint = {0};
#endif
region_t reg_tmp;
const region_t *reg_paint;
pixman_region32_init(&reg_tmp);
// A hint to backend, the region that will be visible on screen
// backend can optimize based on this info
region_t reg_visible;
pixman_region32_init(&reg_visible);
pixman_region32_copy(&reg_visible, &ps->screen_reg);
if (t) {
// Calculate the region upon which the root window (wallpaper) is to be
// painted based on the ignore region of the lowest window, if available
pixman_region32_subtract(&reg_tmp, &region, t->reg_ignore);
reg_paint = &reg_tmp;
} else {
reg_paint = &region;
pixman_region32_subtract(&reg_visible, &reg_visible, t->reg_ignore);
}
// TODO Bind root pixmap
if (ps->backend_data->ops->prepare) {
ps->backend_data->ops->prepare(ps->backend_data, reg_paint);
ps->backend_data->ops->prepare(ps->backend_data, &reg_visible);
}
if (ps->root_image) {
ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0,
reg_paint);
&reg_damage, &reg_visible);
}
// Windows are sorted from bottom to top
@ -83,28 +85,25 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
//
// Whether this is beneficial is to be determined XXX
for (win *w = t; w; w = w->prev_trans) {
// Calculate the region based on the reg_ignore of the next (higher)
// window and the bounding region
// XXX XXX
pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore);
if (!pixman_region32_not_empty(&reg_tmp)) {
continue;
}
pixman_region32_subtract(&reg_visible, &ps->screen_reg, w->reg_ignore);
// The bounding shape of the window, in global/target coordinates
// reminder: bounding shape contains the WM frame
auto reg_bound = win_get_bounding_shape_global_by_val(w);
// Draw shadow on target
if (w->shadow) {
// Clip region for the shadow
// reg_shadow \in reg_damage
auto reg_shadow = win_extents_by_val(w);
pixman_region32_intersect(&reg_shadow, &reg_shadow, &reg_tmp);
pixman_region32_intersect(&reg_shadow, &reg_shadow, &reg_damage);
if (!ps->o.wintype_option[w->window_type].full_shadow) {
pixman_region32_subtract(&reg_shadow, &reg_shadow, &reg_bound);
}
// Mask out the region we don't want shadow on
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
pixman_region32_subtract(&reg_tmp, &reg_tmp,
pixman_region32_subtract(&reg_shadow, &reg_shadow,
&ps->shadow_exclude_reg);
}
@ -123,17 +122,18 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
}
assert(w->shadow_image);
ps->backend_data->ops->compose(ps->backend_data, w->shadow_image,
w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow);
ps->backend_data->ops->compose(
ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
pixman_region32_fini(&reg_shadow);
}
pixman_region32_intersect(&reg_tmp, &reg_tmp, &reg_bound);
pixman_region32_fini(&reg_bound);
if (!pixman_region32_not_empty(&reg_tmp)) {
continue;
}
// The clip region for the current window, in global/target coordinates
// reg_paint \in reg_damage
region_t reg_paint;
pixman_region32_init(&reg_paint);
pixman_region32_intersect(&reg_paint, &reg_bound, &reg_damage);
// Blur window background
bool win_transparent = ps->backend_data->ops->is_image_transparent(
ps->backend_data, w->win_image);
@ -142,35 +142,61 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
(win_transparent || (ps->o.blur_background_frame && frame_transparent))) {
// Minimize the region we try to blur, if the window
// itself is not opaque, only the frame is.
if (win_is_solid(ps, w)) {
region_t reg_blur;
pixman_region32_init(&reg_blur);
win_get_region_noframe_local(w, &reg_blur);
// TODO resize blur region to fix black line artifact
if (!win_is_solid(ps, w)) {
// We need to blur the bounding shape of the window
// (reg_paint = reg_bound \cap reg_damage)
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_paint, &reg_visible);
} else if (frame_transparent && ps->o.blur_background_frame) {
// Window itself is solid, we only need to blur the frame region
auto reg_blur = win_get_region_frame_local_by_val(w);
pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
pixman_region32_subtract(&reg_blur, &reg_tmp, &reg_blur);
// make sure reg_blur \in reg_damage
pixman_region32_intersect(&reg_blur, &reg_blur, &reg_damage);
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_blur);
&reg_blur, &reg_visible);
pixman_region32_fini(&reg_blur);
} else {
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_tmp);
}
}
// Draw window on target
if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) {
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
w->g.x, w->g.y, &reg_tmp);
ps->backend_data->ops->compose(ps->backend_data, w->win_image, w->g.x,
w->g.y, &reg_paint, &reg_visible);
} else {
region_t reg_local;
pixman_region32_init(&reg_local);
pixman_region32_copy(&reg_local, &reg_tmp);
pixman_region32_translate(&reg_local, -w->g.x, -w->g.y);
// For window image processing, we don't need to limit the process
// region to damage, since the window image data is independent from
// the target image data, which we want to protect.
// The bounding shape, in window local coordinates
region_t reg_bound_local;
pixman_region32_init(&reg_bound_local);
pixman_region32_copy(&reg_bound_local, &reg_bound);
pixman_region32_translate(&reg_bound_local, -w->g.x, -w->g.y);
// The visible region, in window local coordinates
// Although we don't limit process region to damage, we provide
// that info in reg_visible as a hint. Since window image data
// outside of the damage region won't be painted onto target
region_t reg_visible_local;
pixman_region32_init(&reg_visible_local);
pixman_region32_intersect(&reg_visible_local, &reg_visible, &reg_damage);
pixman_region32_translate(&reg_visible_local, -w->g.x, -w->g.y);
// Data outside of the bounding shape won't be visible, but it is
// not necessary to limit the image operations to the bounding shape
// yet. So pass that as the visible region, not the clip region.
pixman_region32_intersect(&reg_visible_local, &reg_visible_local,
&reg_bound_local);
// A region covers the entire window
region_t reg_win;
pixman_region32_init_rect(&reg_win, 0, 0, w->g.width, w->g.height);
auto new_img = ps->backend_data->ops->copy(
ps->backend_data, w->win_image, &reg_local);
ps->backend_data, w->win_image, &reg_visible_local);
if (w->invert_color) {
ps->backend_data->ops->image_op(ps->backend_data,
IMAGE_OP_INVERT_COLOR,
new_img, &reg_local, NULL);
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_INVERT_COLOR, new_img,
&reg_win, &reg_visible_local, NULL);
}
if (w->dim) {
double dim_opacity = ps->o.inactive_dim;
@ -178,30 +204,32 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
dim_opacity *= w->opacity;
}
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_DIM, new_img, &reg_local,
(double[]){dim_opacity});
ps->backend_data, IMAGE_OP_DIM, new_img, &reg_win,
&reg_visible_local, (double[]){dim_opacity});
}
if (w->frame_opacity != 1) {
auto reg_frame = win_get_region_noframe_local_by_val(w);
pixman_region32_subtract(&reg_frame, &reg_local, &reg_frame);
auto reg_frame = win_get_region_frame_local_by_val(w);
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img,
&reg_frame, (double[]){w->frame_opacity});
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame,
&reg_visible_local, (double[]){w->frame_opacity});
pixman_region32_fini(&reg_frame);
}
if (w->opacity != 1) {
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, NULL,
(double[]){w->opacity});
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible_local, (double[]){w->opacity});
}
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
w->g.y, &reg_tmp);
w->g.y, &reg_paint, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_win);
pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
}
pixman_region32_fini(&reg_bound);
pixman_region32_fini(&reg_paint);
}
// Free up all temporary regions
pixman_region32_fini(&reg_tmp);
if (ps->backend_data->ops->present) {
// Present the rendered scene
// Vsync is done here

View File

@ -23,9 +23,15 @@ typedef struct backend_base {
} backend_t;
enum image_operations {
// Invert the color of the image
IMAGE_OP_INVERT_COLOR,
// Dim the image, argument is the percentage
IMAGE_OP_DIM,
// Multiply the alpha channel by the argument
IMAGE_OP_APPLY_ALPHA,
// Same as APPLY_ALPHA, but `reg_op` is ignored and the operation applies to the
// full image
IMAGE_OP_APPLY_ALPHA_ALL,
};
struct backend_operations {
@ -64,25 +70,29 @@ struct backend_operations {
/// Optional?
void (*prepare)(backend_t *backend_data, const region_t *reg_paint);
/// Paint the content of an imageonto the (possibly buffered)
/// target picture. Always called after render_win(). Maybe called
/// multiple times between render_win() and finish_render_win().
/// The origin is the top left of the window, exclude the shadow,
/// (dst_x, dst_y) refers to where the origin should be in the target
/// buffer.
/**
* Paint the content of an image onto the (possibly buffered)
* target picture.
*
* @param backend_data the backend data
* @param image_data the image to paint
* @param dst_x, dst_y the top left corner of the image in the target
* @param reg_paint the clip region, in target coordinates
* @param reg_visibile the visible region, in target coordinates
*/
void (*compose)(backend_t *backend_data, void *image_data, int dst_x, int dst_y,
const region_t *reg_paint);
const region_t *reg_paint, const region_t *reg_visible);
/// Blur a given region on of the target.
bool (*blur)(backend_t *backend_data, double opacity, const region_t *)
__attribute__((nonnull(1, 3)));
bool (*blur)(backend_t *backend_data, double opacity, const region_t *reg_blur,
const region_t *reg_visible) attr_nonnull(1, 3, 4);
/// Present the buffered target picture onto the screen. If target
/// is not buffered, this should be NULL. Otherwise, it should always
/// be non-NULL.
///
/// Optional
void (*present)(backend_t *backend_data) __attribute__((nonnull(1)));
void (*present)(backend_t *backend_data) attr_nonnull(1);
/**
* Bind a X pixmap to the backend's internal image data structure.
@ -137,17 +147,22 @@ struct backend_operations {
* Manipulate an image
*
* @param backend_data backend data
* @param op the operation to perform
* @param image_data an image data structure returned by the backend
* @param reg_paint the clip region, limit the region of the image to be
* manipulated
* @param args extra arguments, specific to each operation
* @return a new image data structure contains the same image as `image_data`
* @param op the operation to perform
* @param image_data an image data structure returned by the backend
* @param reg_op the clip region, define the part of the image to be
* operated on.
* @param reg_visible define the part of the image that will eventually
* be visible on screen. this is a hint to the backend
* for optimization purposes.
* @param args extra arguments, operation specific
* @return a new image data structure containing the result
*/
void (*image_op)(backend_t *backend_data, enum image_operations op,
void *image_data, const region_t *reg_paint, void *args);
bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data,
const region_t *reg_op, const region_t *reg_visible, void *args);
void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_copy);
/// Create another instance of the `image_data`. All `image_op` calls on the
/// returned image should not affect the original image
void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_visible);
// =========== Hooks ============
/// Let the backend hook into the event handling queue

View File

@ -83,28 +83,42 @@ struct _xrender_image_data {
bool owned;
};
static void
compose(backend_t *base, void *img_data, int dst_x, int dst_y, const region_t *reg_paint) {
static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
const region_t *reg_paint, const region_t *reg_visible) {
struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = img_data;
int op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
auto alpha_pict = xd->alpha_pict[(int)(img->opacity * 255.0)];
region_t reg;
pixman_region32_init(&reg);
pixman_region32_intersect(&reg, (region_t *)reg_paint, (region_t *)reg_visible);
// Clip region of rendered_pict might be set during rendering, clear it to make
// sure we get everything into the buffer
x_clear_picture_clip_region(base->c, img->pict);
x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, reg_paint);
x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, &reg);
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back],
0, 0, 0, 0, dst_x, dst_y, img->width, img->height);
pixman_region32_fini(&reg);
}
static bool blur(backend_t *backend_data, double opacity, const region_t *reg_paint) {
static bool blur(backend_t *backend_data, double opacity, const region_t *reg_blur,
const region_t *reg_visible) {
struct _xrender_data *xd = (void *)backend_data;
xcb_connection_t *c = xd->base.c;
const pixman_box32_t *reg = pixman_region32_extents((region_t *)reg_paint);
const int height = reg->y2 - reg->y1;
const int width = reg->x2 - reg->x1;
region_t reg_op;
pixman_region32_init(&reg_op);
pixman_region32_intersect(&reg_op, (region_t *)reg_blur, (region_t *)reg_visible);
if (!pixman_region32_not_empty(&reg_op)) {
pixman_region32_fini(&reg_op);
return true;
}
const pixman_box32_t *extent = pixman_region32_extents(&reg_op);
const int height = extent->y2 - extent->y1;
const int width = extent->x2 - extent->x1;
int src_x = extent->x1, src_y = extent->y1;
static const char *filter0 = "Nearest"; // The "null" filter
static const char *filter = "convolution";
@ -116,26 +130,26 @@ static bool blur(backend_t *backend_data, double opacity, const region_t *reg_pa
x_create_picture_with_visual(xd->base.c, xd->base.root, width, height,
xd->default_visual, 0, NULL)};
region_t clip;
pixman_region32_init(&clip);
pixman_region32_copy(&clip, (region_t *)reg_paint);
pixman_region32_translate(&clip, -reg->x1, -reg->y1);
if (!tmp_picture[0] || !tmp_picture[1]) {
log_error("Failed to build intermediate Picture.");
pixman_region32_fini(&reg_op);
return false;
}
region_t clip;
pixman_region32_init(&clip);
pixman_region32_copy(&clip, &reg_op);
pixman_region32_translate(&clip, -src_x, -src_y);
x_set_picture_clip_region(c, tmp_picture[0], 0, 0, &clip);
x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip);
pixman_region32_fini(&clip);
// The multipass blur implemented here is not correct, but this is what old
// compton did anyway. XXX
xcb_render_picture_t src_pict = xd->back[xd->curr_back], dst_pict = tmp_picture[0];
auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)];
int current = 0;
int src_x = reg->x1, src_y = reg->y1;
x_set_picture_clip_region(c, src_pict, 0, 0, reg_paint);
x_set_picture_clip_region(c, src_pict, 0, 0, &reg_op);
// For more than 1 pass, we do:
// back -(pass 1)-> tmp0 -(pass 2)-> tmp1 ...
@ -162,7 +176,7 @@ static bool blur(backend_t *backend_data, double opacity, const region_t *reg_pa
// This is the last pass, and this is also not the first
xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict,
alpha_pict, xd->back[xd->curr_back], 0, 0, 0,
0, reg->x1, reg->y1, width, height);
0, src_x, src_y, width, height);
}
// reset filter
@ -178,12 +192,13 @@ static bool blur(backend_t *backend_data, double opacity, const region_t *reg_pa
// There is only 1 pass
if (i == 1) {
xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict,
xd->back[xd->curr_back], 0, 0, 0, 0, reg->x1,
reg->y1, width, height);
xd->back[xd->curr_back], 0, 0, 0, 0, extent->x1, extent->y1,
width, height);
}
xcb_render_free_picture(c, tmp_picture[0]);
xcb_render_free_picture(c, tmp_picture[1]);
pixman_region32_fini(&reg_op);
return true;
}
@ -321,16 +336,30 @@ static bool is_image_transparent(backend_t *bd, void *image) {
return img->has_alpha;
}
static void image_op(backend_t *base, enum image_operations op, void *image,
const region_t *reg, void *arg) {
static bool image_op(backend_t *base, enum image_operations op, void *image,
const region_t *reg_op, const region_t *reg_visible, void *arg) {
struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = image;
region_t reg;
double dim_opacity;
double alpha_multiplier;
if (op == IMAGE_OP_APPLY_ALPHA_ALL) {
alpha_multiplier = *(double *)arg;
img->opacity *= alpha_multiplier;
img->has_alpha = true;
return true;
}
pixman_region32_init(&reg);
pixman_region32_intersect(&reg, (region_t *)reg_op, (region_t *)reg_visible);
if (!pixman_region32_not_empty(&reg)) {
pixman_region32_fini(&reg);
return true;
}
switch (op) {
case IMAGE_OP_INVERT_COLOR:
assert(reg);
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
x_set_picture_clip_region(base->c, img->pict, 0, 0, &reg);
if (img->has_alpha) {
auto tmp_pict =
x_create_picture_with_visual(base->c, base->root, img->width,
@ -355,8 +384,7 @@ static void image_op(backend_t *base, enum image_operations op, void *image,
}
break;
case IMAGE_OP_DIM:
assert(reg);
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
x_set_picture_clip_region(base->c, img->pict, 0, 0, &reg);
dim_opacity = *(double *)arg;
xcb_render_color_t color = {
@ -378,19 +406,17 @@ static void image_op(backend_t *base, enum image_operations op, void *image,
if (alpha_multiplier == 1) {
break;
}
if (!reg) {
img->opacity *= alpha_multiplier;
img->has_alpha = true;
break;
}
auto alpha_pict = xd->alpha_pict[(int)(alpha_multiplier * 255)];
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
x_set_picture_clip_region(base->c, img->pict, 0, 0, &reg);
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN, img->pict, XCB_NONE,
alpha_pict, 0, 0, 0, 0, 0, 0, img->width, img->height);
img->has_alpha = true;
break;
case IMAGE_OP_APPLY_ALPHA_ALL: assert(false);
}
pixman_region32_fini(&reg);
return true;
}
static void *copy(backend_t *base, const void *image, const region_t *reg) {

View File

@ -137,6 +137,29 @@ void win_get_region_noframe_local(const win *w, region_t *res) {
gen_by_val(win_get_region_noframe_local)
void win_get_region_frame_local(const win *w, region_t *res) {
const margin_t extents = win_calc_frame_extents(w);
pixman_region32_fini(res);
pixman_region32_init_rects(res, (rect_t[]){
// top
{.x1 = 0, .y1 = 0, .x2 = w->g.width, .y2 = extents.top},
// bottom
{.x1 = 0, .y1 = w->g.height - extents.bottom, .x2 = w->g.width, .y2 = w->g.height},
//left
{.x1 = 0, .y1 = 0, .x2 = extents.left, .y2 = w->g.height},
// right
{.x1 = w->g.width - extents.right, .y1 = 0, .x2 = w->g.width, .y2 = w->g.height},
}, 4);
// limit the frame region to inside the window
region_t reg_win;
pixman_region32_init_rect(&reg_win, 0, 0, w->g.width, w->g.height);
pixman_region32_intersect(res, &reg_win, res);
pixman_region32_fini(&reg_win);
}
gen_by_val(win_get_region_frame_local)
/**
* Add a window to damaged area.
*
@ -1235,7 +1258,10 @@ void win_update_bounding_shape(session_t *ps, win *w) {
free(rects);
// Add border width because we are using a different origin.
// X thinks the top left of the inner window is the origin,
// X thinks the top left of the inner window is the origin
// (for the bounding shape, althought xcb_get_geometry thinks
// the outer top left (outer means outside of the window
// border) is the origin),
// We think the top left of the border is the origin
pixman_region32_translate(&br, w->g.border_width, w->g.border_width);

View File

@ -111,7 +111,8 @@ typedef enum {
* X goes from left to right, Y goes downwards.
*
* Global: the origin is the top left corner of the Xorg screen.
* Local: the origin is the top left corner of the window, including border.
* Local: the origin is the top left corner of the window, border is
* considered part of the window.
*/
/// Structure representing a top-level window compton manages.
@ -354,6 +355,11 @@ void add_damage_from_win(session_t *ps, win *w);
*/
void win_get_region_noframe_local(const win *w, region_t *);
region_t win_get_region_noframe_local_by_val(const win *w);
/// Get the region for the frame of the window
void win_get_region_frame_local(const win *w, region_t *res);
/// Get the region for the frame of the window, by value
region_t win_get_region_frame_local_by_val(const win *w);
/**
* Retrieve frame extents from a window.
*/