mirror of
https://github.com/yshui/picom.git
synced 2025-04-21 18:03:02 -04:00
renderer: implement damage calculation
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
c1f996f795
commit
24aa1e68d8
8 changed files with 470 additions and 10 deletions
|
@ -11,7 +11,7 @@ srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.
|
|||
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
|
||||
'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'statistics.c',
|
||||
'vblank.c', 'transition.c', 'wm.c', 'renderer/layout.c', 'renderer/command_builder.c',
|
||||
'renderer/renderer.c') ]
|
||||
'renderer/renderer.c', 'renderer/damage.c') ]
|
||||
picom_inc = include_directories('.')
|
||||
|
||||
cflags = []
|
||||
|
|
25
src/region.h
25
src/region.h
|
@ -147,3 +147,28 @@ static inline void region_union(region_t *region, struct coord origin, const reg
|
|||
pixman_region32_union(region, region, other);
|
||||
pixman_region32_translate(region, origin.x, origin.y);
|
||||
}
|
||||
|
||||
/// Intersect `region` with `other` placed at `origin`.
|
||||
static inline void
|
||||
region_intersect(region_t *region, struct coord origin, const region_t *other) {
|
||||
pixman_region32_translate(region, -origin.x, -origin.y);
|
||||
pixman_region32_intersect(region, region, other);
|
||||
pixman_region32_translate(region, origin.x, origin.y);
|
||||
}
|
||||
|
||||
/// Calculate the symmetric difference of `region1`, and `region2` placed at
|
||||
/// `origin2`, and union the result into `result`.
|
||||
///
|
||||
/// @param scratch a region to store temporary results
|
||||
static inline void
|
||||
region_symmetric_difference(region_t *result, region_t *scratch, struct coord origin1,
|
||||
const region_t *region1, struct coord origin2,
|
||||
const region_t *region2) {
|
||||
pixman_region32_copy(scratch, region1);
|
||||
region_subtract(scratch, coord_sub(origin2, origin1), region2);
|
||||
region_union(result, origin1, scratch);
|
||||
|
||||
pixman_region32_copy(scratch, region2);
|
||||
region_subtract(scratch, coord_sub(origin1, origin2), region1);
|
||||
region_union(result, origin2, scratch);
|
||||
}
|
||||
|
|
295
src/renderer/damage.c
Normal file
295
src/renderer/damage.c
Normal file
|
@ -0,0 +1,295 @@
|
|||
#include "damage.h"
|
||||
|
||||
#include "layout.h"
|
||||
#include "region.h"
|
||||
#include "win.h"
|
||||
static inline bool attr_unused layer_key_eq(const struct layer_key *a,
|
||||
const struct layer_key *b) {
|
||||
if (!a->generation || !b->generation) {
|
||||
return false;
|
||||
}
|
||||
return a->window == b->window && a->generation == b->generation;
|
||||
}
|
||||
|
||||
/// Compare two layers that contain the same window, return if they are the "same". Same
|
||||
/// means these two layers are render in the same way at the same position, with the only
|
||||
/// possible differences being the contents inside the window.
|
||||
static bool
|
||||
layer_compare(const struct layer *past_layer, const struct backend_command *past_layer_cmd,
|
||||
const struct layer *curr_layer, const struct backend_command *curr_layer_cmd) {
|
||||
if (past_layer->origin.x != curr_layer->origin.x ||
|
||||
past_layer->origin.y != curr_layer->origin.y ||
|
||||
past_layer->size.width != curr_layer->size.width ||
|
||||
past_layer->size.height != curr_layer->size.height) {
|
||||
// Window moved or size changed
|
||||
return false;
|
||||
}
|
||||
|
||||
if (past_layer->shadow_origin.x != curr_layer->shadow_origin.x ||
|
||||
past_layer->shadow_origin.y != curr_layer->shadow_origin.y ||
|
||||
past_layer->shadow_size.width != curr_layer->shadow_size.width ||
|
||||
past_layer->shadow_size.height != curr_layer->shadow_size.height) {
|
||||
// Shadow moved or size changed
|
||||
return false;
|
||||
}
|
||||
if (past_layer->number_of_commands != curr_layer->number_of_commands) {
|
||||
// Number of render commands changed. We are being conservative
|
||||
// here, because even though the number of commands changed, we can still
|
||||
// try to match them up. For example, maybe this window just has shadow
|
||||
// disabled, but other commands are still the same. We are not do that
|
||||
// here, this could be a TODO
|
||||
// TODO(yshui) match render commands here
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < past_layer->number_of_commands; i++) {
|
||||
auto cmd1 = &past_layer_cmd[i];
|
||||
auto cmd2 = &curr_layer_cmd[i];
|
||||
if (cmd1->op != cmd2->op || !coord_eq(cmd1->origin, cmd2->origin) ||
|
||||
cmd1->source != cmd2->source) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Add all regions of `layer`'s commands to `region`
|
||||
static inline void region_union_render_layer(region_t *region, const struct layer *layer,
|
||||
const struct backend_command *cmds) {
|
||||
for (auto i = cmds; i < &cmds[layer->number_of_commands]; i++) {
|
||||
region_union(region, coord_add(i->origin, i->mask.origin), &i->mask.region);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
command_blit_damage(region_t *damage, region_t *scratch_region, struct backend_command *cmd1,
|
||||
struct backend_command *cmd2, const struct layout_manager *lm,
|
||||
unsigned layer_index, unsigned buffer_age) {
|
||||
auto origin1 = coord_add(cmd1->origin, cmd1->mask.origin);
|
||||
auto origin2 = coord_add(cmd2->origin, cmd2->mask.origin);
|
||||
// clang-format off
|
||||
// First part, if any blit argument that would affect the whole image changed
|
||||
if (cmd1->blit.dim != cmd2->blit.dim ||
|
||||
cmd1->blit.shader != cmd2->blit.shader ||
|
||||
cmd1->blit.opacity != cmd2->blit.opacity ||
|
||||
cmd1->blit.corner_radius != cmd2->blit.corner_radius ||
|
||||
cmd1->blit.max_brightness != cmd2->blit.max_brightness ||
|
||||
cmd1->blit.color_inverted != cmd2->blit.color_inverted ||
|
||||
|
||||
// Second part, if round corner is enabled, then border width and effective size
|
||||
// affect the whole image too.
|
||||
(cmd1->blit.corner_radius > 0 &&
|
||||
(cmd1->blit.border_width != cmd2->blit.border_width ||
|
||||
cmd1->blit.ewidth != cmd2->blit.ewidth ||
|
||||
cmd1->blit.eheight != cmd2->blit.eheight )))
|
||||
{
|
||||
region_union(damage, origin1, &cmd1->mask.region);
|
||||
region_union(damage, origin2, &cmd2->mask.region);
|
||||
return;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
if (cmd1->blit.opacity == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Damage from layers below that is covered up by the current layer, won't be
|
||||
// visible. So remove them.
|
||||
pixman_region32_subtract(damage, damage, &cmd2->opaque_region);
|
||||
region_symmetric_difference(damage, scratch_region, origin1, &cmd1->mask.region,
|
||||
origin2, &cmd2->mask.region);
|
||||
if (cmd1->source == BACKEND_COMMAND_SOURCE_WINDOW) {
|
||||
layout_manager_collect_window_damage(lm, layer_index, buffer_age,
|
||||
scratch_region);
|
||||
region_intersect(scratch_region, origin1, &cmd1->mask.region);
|
||||
region_intersect(scratch_region, origin2, &cmd2->mask.region);
|
||||
pixman_region32_union(damage, damage, scratch_region);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
command_blur_damage(region_t *damage, region_t *scratch_region, struct backend_command *cmd1,
|
||||
struct backend_command *cmd2, struct geometry blur_size) {
|
||||
auto origin1 = coord_add(cmd1->origin, cmd1->mask.origin);
|
||||
auto origin2 = coord_add(cmd2->origin, cmd2->mask.origin);
|
||||
if (cmd1->blur.opacity != cmd2->blur.opacity) {
|
||||
region_union(damage, origin1, &cmd1->mask.region);
|
||||
region_union(damage, origin2, &cmd2->mask.region);
|
||||
return;
|
||||
}
|
||||
if (cmd1->blur.opacity == 0) {
|
||||
return;
|
||||
}
|
||||
region_symmetric_difference(damage, scratch_region, origin1, &cmd1->mask.region,
|
||||
origin2, &cmd2->mask.region);
|
||||
|
||||
// We need to expand the damage region underneath the blur. Because blur
|
||||
// "diffuses" the changes from below.
|
||||
pixman_region32_copy(scratch_region, damage);
|
||||
resize_region_in_place(scratch_region, blur_size.width, blur_size.height);
|
||||
region_intersect(scratch_region, origin2, &cmd2->mask.region);
|
||||
pixman_region32_union(damage, damage, scratch_region);
|
||||
}
|
||||
|
||||
/// Do the first step of render planning, collecting damages and calculating which
|
||||
/// parts of the final screen will be affected by the damages.
|
||||
void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age,
|
||||
struct geometry blur_size, region_t *damage) {
|
||||
log_trace("Damage for buffer age %d", buffer_age);
|
||||
unsigned past_layer_rank = 0, curr_layer_rank = 0;
|
||||
auto past_layout = layout_manager_layout(lm, buffer_age);
|
||||
auto curr_layout = layout_manager_layout(lm, 0);
|
||||
auto past_layer = &past_layout->layers[past_layer_rank];
|
||||
auto curr_layer = &curr_layout->layers[curr_layer_rank];
|
||||
auto past_layer_cmd = &past_layout->commands[past_layout->first_layer_start];
|
||||
auto curr_layer_cmd = &curr_layout->commands[curr_layout->first_layer_start];
|
||||
region_t scratch_region;
|
||||
pixman_region32_init(&scratch_region);
|
||||
pixman_region32_clear(damage);
|
||||
if (past_layout->size.width != curr_layout->size.width ||
|
||||
past_layout->size.height != curr_layout->size.height) {
|
||||
// We might be able to do better for blur size change, but currently we
|
||||
// don't even support changing that.
|
||||
pixman_region32_union_rect(damage, damage, 0, 0,
|
||||
(unsigned)curr_layout->size.width,
|
||||
(unsigned)curr_layout->size.height);
|
||||
return;
|
||||
}
|
||||
if (log_get_level_tls() <= LOG_LEVEL_TRACE) {
|
||||
log_trace("Comparing across %d layouts:", buffer_age);
|
||||
for (unsigned l = 0; l <= buffer_age; l++) {
|
||||
log_trace("Layout[%d]: ", -l);
|
||||
auto layout = layout_manager_layout(lm, l);
|
||||
for (unsigned i = 0; i < layout->len; i++) {
|
||||
log_trace(
|
||||
"\t%#010x %dx%d+%dx%d (prev %d, next %d)",
|
||||
layout->layers[i].key.window, layout->layers[i].size.width,
|
||||
layout->layers[i].size.height,
|
||||
layout->layers[i].origin.x, layout->layers[i].origin.y,
|
||||
layout->layers[i].prev_rank, layout->layers[i].next_rank);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Explanation of what's happening here. We want to get damage by comparing
|
||||
// `past_layout` and `curr_layout` But windows in them could be different. And
|
||||
// comparing different windows doesn't really make sense. So we want to "align"
|
||||
// the layouts so we compare matching windows and skip over non-matching ones. For
|
||||
// example, say past layout has window "ABCDE"; and in current layout, window C is
|
||||
// closed, and F is opened: "ABDFE", we want to align them like this:
|
||||
// ABCD E
|
||||
// AB DFE
|
||||
// Note there can be multiple ways of aligning windows, some of them are not
|
||||
// optimal. For example, in layout "ABCDEFG", if we move B to after F: "ACDEFBG",
|
||||
// we want to align them like this:
|
||||
// ABCDEF G
|
||||
// A CDEFBG
|
||||
// not like this:
|
||||
// A BCDEFG
|
||||
// ACDEFB G
|
||||
//
|
||||
// This is the classic Longest Common Sequence (LCS) problem, but we are not doing
|
||||
// a full LCS algorithm here. Since damage is calculated every frame, there is
|
||||
// likely not a lot of changes between the two layouts. We use a simple linear
|
||||
// time greedy approximation that should work well enough in those cases.
|
||||
|
||||
for (;; past_layer_rank += 1, curr_layer_rank += 1,
|
||||
past_layer_cmd += past_layer->number_of_commands,
|
||||
curr_layer_cmd += curr_layer->number_of_commands, past_layer += 1,
|
||||
curr_layer += 1) {
|
||||
int past_layer_curr_rank = -1, curr_layer_past_rank = -1;
|
||||
unsigned past_layer_rank_target = past_layer_rank,
|
||||
curr_layer_rank_target = curr_layer_rank;
|
||||
log_region(TRACE, damage);
|
||||
|
||||
// Skip layers in the past layout doesn't contain a window that has a
|
||||
// match in the remaining layers of the current layout; and vice versa.
|
||||
while (past_layer_rank_target < past_layout->len) {
|
||||
past_layer_curr_rank =
|
||||
layer_next_rank(lm, buffer_age, past_layer_rank_target);
|
||||
if (past_layer_curr_rank >= (int)curr_layer_rank) {
|
||||
break;
|
||||
}
|
||||
past_layer_rank_target++;
|
||||
};
|
||||
while (curr_layer_rank_target < curr_layout->len) {
|
||||
curr_layer_past_rank =
|
||||
layer_prev_rank(lm, buffer_age, curr_layer_rank_target);
|
||||
if (curr_layer_past_rank >= (int)past_layer_rank) {
|
||||
break;
|
||||
}
|
||||
curr_layer_rank_target++;
|
||||
};
|
||||
|
||||
// past_layer_curr_rank/curr_layer_past_rank can be -1
|
||||
if (past_layer_curr_rank >= (int)curr_layer_rank ||
|
||||
curr_layer_past_rank >= (int)past_layer_rank) {
|
||||
// Now past_layer_current_rank and current_layer_past_rank both
|
||||
// have a matching layer in the other layout. We check which side
|
||||
// has less layers to skip.
|
||||
assert((unsigned)curr_layer_past_rank >= past_layer_rank_target);
|
||||
assert((unsigned)past_layer_curr_rank >= curr_layer_rank_target);
|
||||
// How many layers will be skipped on either side if we move
|
||||
// past_layer_rank to past_layer_rank_target. And vice versa.
|
||||
auto skipped_using_past_target =
|
||||
past_layer_rank_target - past_layer_rank +
|
||||
((unsigned)past_layer_curr_rank - curr_layer_rank);
|
||||
auto skipped_using_curr_target =
|
||||
curr_layer_rank_target - curr_layer_rank +
|
||||
((unsigned)curr_layer_past_rank - past_layer_rank);
|
||||
if (skipped_using_curr_target < skipped_using_past_target) {
|
||||
past_layer_rank_target = (unsigned)curr_layer_past_rank;
|
||||
} else {
|
||||
curr_layer_rank_target = (unsigned)past_layer_curr_rank;
|
||||
}
|
||||
}
|
||||
|
||||
// For the skipped layers, we need to add them to the damage region.
|
||||
for (; past_layer_rank < past_layer_rank_target; past_layer_rank++) {
|
||||
region_union_render_layer(damage, past_layer, past_layer_cmd);
|
||||
past_layer_cmd += past_layer->number_of_commands;
|
||||
past_layer += 1;
|
||||
}
|
||||
for (; curr_layer_rank < curr_layer_rank_target; curr_layer_rank++) {
|
||||
region_union_render_layer(damage, curr_layer, curr_layer_cmd);
|
||||
curr_layer_cmd += curr_layer->number_of_commands;
|
||||
curr_layer += 1;
|
||||
}
|
||||
|
||||
if (past_layer_rank >= past_layout->len || curr_layer_rank >= curr_layout->len) {
|
||||
// No more matching layers left.
|
||||
assert(past_layer_rank >= past_layout->len &&
|
||||
curr_layer_rank >= curr_layout->len);
|
||||
break;
|
||||
}
|
||||
|
||||
assert(layer_key_eq(&past_layer->key, &curr_layer->key));
|
||||
log_trace("%#010x == %#010x %s", past_layer->key.window,
|
||||
curr_layer->key.window, curr_layer->win->name);
|
||||
|
||||
if (!layer_compare(past_layer, past_layer_cmd, curr_layer, curr_layer_cmd)) {
|
||||
region_union_render_layer(damage, curr_layer, curr_layer_cmd);
|
||||
region_union_render_layer(damage, past_layer, past_layer_cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Layers are otherwise identical besides the window content. We will
|
||||
// process their render command and add appropriate damage.
|
||||
log_trace("Adding window damage");
|
||||
for (struct backend_command *cmd1 = past_layer_cmd, *cmd2 = curr_layer_cmd;
|
||||
cmd1 < past_layer_cmd + past_layer->number_of_commands; cmd1++, cmd2++) {
|
||||
switch (cmd1->op) {
|
||||
case BACKEND_COMMAND_BLIT:
|
||||
command_blit_damage(damage, &scratch_region, cmd1, cmd2,
|
||||
lm, curr_layer_rank, buffer_age);
|
||||
break;
|
||||
case BACKEND_COMMAND_BLUR:
|
||||
command_blur_damage(damage, &scratch_region, cmd1, cmd2,
|
||||
blur_size);
|
||||
break;
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
pixman_region32_fini(&scratch_region);
|
||||
}
|
23
src/renderer/damage.h
Normal file
23
src/renderer/damage.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct pixman_region32 region_t;
|
||||
struct layout;
|
||||
struct layout_manager;
|
||||
|
||||
/// Calculate damage of the screen for the last `buffer_age` layouts. Assuming the
|
||||
/// current, yet to be rendered frame is numbered frame 0, the previous frame is numbered
|
||||
/// frame -1, and so on. This function returns the region of the screen that will be
|
||||
/// different between frame `-buffer_age` and frame 0. The region is in screen
|
||||
/// coordinates. `buffer_age` is at least 1, and must be less than the `max_buffer_age`
|
||||
/// passed to the `layout_manager_new` that was used to create `lm`.
|
||||
///
|
||||
/// The layouts you want to calculate damage for must already have commands built for
|
||||
/// them. `blur_size` is the size of the background blur, and is assumed to not change
|
||||
/// over time.
|
||||
///
|
||||
/// Note `layout_manager_damage` cannot take desktop background change into
|
||||
/// account.
|
||||
void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age,
|
||||
struct geometry blur_size, region_t *damage);
|
|
@ -224,6 +224,52 @@ void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm,
|
|||
}
|
||||
}
|
||||
|
||||
struct layout *layout_manager_layout(struct layout_manager *lm) {
|
||||
return &lm->layouts[lm->current];
|
||||
struct layout *layout_manager_layout(struct layout_manager *lm, unsigned age) {
|
||||
if (age >= lm->max_buffer_age) {
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
return &lm->layouts[(lm->current + lm->max_buffer_age - age) % lm->max_buffer_age];
|
||||
}
|
||||
|
||||
void layout_manager_collect_window_damage(const struct layout_manager *lm, unsigned index,
|
||||
unsigned buffer_age, region_t *damage) {
|
||||
auto curr = lm->current;
|
||||
auto layer = &lm->layouts[curr].layers[index];
|
||||
for (unsigned i = 0; i < buffer_age; i++) {
|
||||
pixman_region32_union(damage, damage, &layer->damaged);
|
||||
curr = (curr + lm->max_buffer_age - 1) % lm->max_buffer_age;
|
||||
assert(layer->prev_rank >= 0);
|
||||
layer = &lm->layouts[curr].layers[layer->prev_rank];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned layout_manager_max_buffer_age(const struct layout_manager *lm) {
|
||||
return lm->max_buffer_age - 1;
|
||||
}
|
||||
|
||||
int layer_prev_rank(struct layout_manager *lm, unsigned buffer_age, unsigned index_) {
|
||||
int index = to_int_checked(index_);
|
||||
unsigned layout = lm->current;
|
||||
while (buffer_age--) {
|
||||
index = lm->layouts[layout].layers[index].prev_rank;
|
||||
if (index < 0) {
|
||||
break;
|
||||
}
|
||||
layout = (layout + lm->max_buffer_age - 1) % lm->max_buffer_age;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int layer_next_rank(struct layout_manager *lm, unsigned buffer_age, unsigned index_) {
|
||||
int index = to_int_checked(index_);
|
||||
unsigned layout = (lm->current + lm->max_buffer_age - buffer_age) % lm->max_buffer_age;
|
||||
while (buffer_age--) {
|
||||
index = lm->layouts[layout].layers[index].next_rank;
|
||||
if (index < 0) {
|
||||
break;
|
||||
}
|
||||
layout = (layout + 1) % lm->max_buffer_age;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
|
|
@ -102,7 +102,17 @@ struct layout_manager;
|
|||
/// at the end of the ring buffer, and remove the oldest layout if the buffer is full.
|
||||
void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm,
|
||||
struct geometry size);
|
||||
struct layout *layout_manager_layout(struct layout_manager *lm);
|
||||
/// Get the layout `age` frames into the past. Age `0` is the most recently appended
|
||||
/// layout.
|
||||
struct layout *layout_manager_layout(struct layout_manager *lm, unsigned age);
|
||||
void layout_manager_free(struct layout_manager *lm);
|
||||
/// Create a new render lm with a ring buffer for `max_buffer_age` layouts.
|
||||
struct layout_manager *layout_manager_new(unsigned max_buffer_age);
|
||||
/// Collect damage from the window for the past `buffer_age` frames.
|
||||
void layout_manager_collect_window_damage(const struct layout_manager *lm, unsigned index,
|
||||
unsigned buffer_age, region_t *damage);
|
||||
/// Find where layer at `index` was `buffer_age` frames ago.
|
||||
int layer_prev_rank(struct layout_manager *lm, unsigned buffer_age, unsigned index_);
|
||||
/// Find layer that was at `index` `buffer_age` aga in the current layout.
|
||||
int layer_next_rank(struct layout_manager *lm, unsigned buffer_age, unsigned index_);
|
||||
unsigned layout_manager_max_buffer_age(const struct layout_manager *lm);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "backend/backend.h"
|
||||
#include "backend/backend_common.h"
|
||||
#include "command_builder.h"
|
||||
#include "damage.h"
|
||||
#include "layout.h"
|
||||
|
||||
struct renderer {
|
||||
|
@ -18,6 +19,8 @@ struct renderer {
|
|||
image_handle white_image;
|
||||
/// 1x1 black image
|
||||
image_handle black_image;
|
||||
/// 1x1 image with the monitor repaint color
|
||||
image_handle monitor_repaint_pixel;
|
||||
/// 1x1 shadow colored xrender picture
|
||||
xcb_render_picture_t shadow_pixel;
|
||||
struct geometry canvas_size;
|
||||
|
@ -39,6 +42,9 @@ void renderer_free(struct backend_base *backend, struct renderer *r) {
|
|||
if (r->back_image) {
|
||||
backend->ops->v2.release_image(backend, r->back_image);
|
||||
}
|
||||
if (r->monitor_repaint_pixel) {
|
||||
backend->ops->v2.release_image(backend, r->monitor_repaint_pixel);
|
||||
}
|
||||
if (r->shadow_blur_context) {
|
||||
backend->ops->destroy_blur_context(backend, r->shadow_blur_context);
|
||||
}
|
||||
|
@ -393,17 +399,26 @@ bool renderer_render(struct renderer *r, struct backend_base *backend,
|
|||
image_handle root_image, struct layout_manager *lm,
|
||||
struct command_builder *cb, void *blur_context,
|
||||
uint64_t render_start_us, bool use_damage attr_unused,
|
||||
bool monitor_repaint attr_unused, bool force_blend, bool blur_frame,
|
||||
bool monitor_repaint, bool force_blend, bool blur_frame,
|
||||
bool inactive_dim_fixed, double max_brightness, double inactive_dim,
|
||||
const region_t *shadow_exclude, const struct x_monitors *monitors,
|
||||
const struct win_option *wintype_options, uint64_t *after_damage_us) {
|
||||
auto layout = layout_manager_layout(lm);
|
||||
auto layout = layout_manager_layout(lm, 0);
|
||||
if (!renderer_set_root_size(
|
||||
r, backend, (struct geometry){layout->size.width, layout->size.height})) {
|
||||
log_error("Failed to allocate back image");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (monitor_repaint && r->monitor_repaint_pixel == NULL) {
|
||||
r->monitor_repaint_pixel = backend->ops->v2.new_image(
|
||||
backend, BACKEND_IMAGE_FORMAT_PIXMAP, (struct geometry){1, 1});
|
||||
if (r->monitor_repaint_pixel) {
|
||||
backend->ops->v2.clear(backend, r->monitor_repaint_pixel,
|
||||
(struct color){.alpha = 0.5, .red = 0.5});
|
||||
}
|
||||
}
|
||||
|
||||
command_builder_build(cb, layout, force_blend, blur_frame, inactive_dim_fixed,
|
||||
max_brightness, inactive_dim, shadow_exclude, monitors,
|
||||
wintype_options);
|
||||
|
@ -443,15 +458,42 @@ bool renderer_render(struct renderer *r, struct backend_base *backend,
|
|||
return false;
|
||||
}
|
||||
|
||||
region_t screen_region;
|
||||
pixman_region32_init_rect(&screen_region, 0, 0, (unsigned)r->canvas_size.width,
|
||||
(unsigned)r->canvas_size.height);
|
||||
if (monitor_repaint && r->monitor_repaint_pixel) {
|
||||
struct backend_mask mask = {};
|
||||
pixman_region32_init(&mask.region);
|
||||
pixman_region32_copy(&mask.region, &screen_region);
|
||||
auto buffer_age = backend->ops->buffer_age(backend);
|
||||
if (buffer_age > 0 &&
|
||||
(unsigned)buffer_age <= layout_manager_max_buffer_age(lm)) {
|
||||
struct geometry blur_size = {};
|
||||
if (backend->ops->get_blur_size && blur_context) {
|
||||
backend->ops->get_blur_size(
|
||||
blur_context, &blur_size.width, &blur_size.height);
|
||||
}
|
||||
layout_manager_damage(lm, (unsigned)buffer_age, blur_size, &mask.region);
|
||||
}
|
||||
struct backend_blit_args blit = {
|
||||
.source_image = r->monitor_repaint_pixel,
|
||||
.max_brightness = 1,
|
||||
.opacity = 1,
|
||||
.ewidth = r->canvas_size.width,
|
||||
.eheight = r->canvas_size.height,
|
||||
.mask = &mask,
|
||||
};
|
||||
log_trace("Blit for monitor repaint");
|
||||
backend->ops->v2.blit(backend, (struct coord){}, r->back_image, &blit);
|
||||
pixman_region32_fini(&mask.region);
|
||||
}
|
||||
|
||||
if (backend->ops->present) {
|
||||
region_t screen_region;
|
||||
pixman_region32_init_rect(&screen_region, 0, 0, (unsigned)r->canvas_size.width,
|
||||
(unsigned)r->canvas_size.height);
|
||||
backend->ops->v2.copy_area_quantize(backend, (struct coord){},
|
||||
backend->ops->v2.back_buffer(backend),
|
||||
r->back_image, &screen_region);
|
||||
backend->ops->v2.present(backend);
|
||||
pixman_region32_fini(&screen_region);
|
||||
}
|
||||
pixman_region32_fini(&screen_region);
|
||||
return true;
|
||||
}
|
||||
|
|
19
src/types.h
19
src/types.h
|
@ -5,6 +5,7 @@
|
|||
|
||||
/// Some common types
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/// Enumeration type to represent switches.
|
||||
|
@ -39,5 +40,23 @@ typedef struct coord {
|
|||
int x, y;
|
||||
} coord_t;
|
||||
|
||||
static inline struct coord coord_add(struct coord a, struct coord b) {
|
||||
return (struct coord){
|
||||
.x = a.x + b.x,
|
||||
.y = a.y + b.y,
|
||||
};
|
||||
}
|
||||
|
||||
static inline struct coord coord_sub(struct coord a, struct coord b) {
|
||||
return (struct coord){
|
||||
.x = a.x - b.x,
|
||||
.y = a.y - b.y,
|
||||
};
|
||||
}
|
||||
|
||||
static inline bool coord_eq(struct coord a, struct coord b) {
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
#define MARGIN_INIT \
|
||||
{ 0, 0, 0, 0 }
|
||||
|
|
Loading…
Add table
Reference in a new issue