wlr_scene: Hook up HIGHLIGHT logic

This will display red translucent rectangles on the screen regions that
have been damaged. These rectangles will fade out over the span of 250
msecs. If the area is damaged again while the region is fading out,
the timer is reset.

Let's also disable direct scan out when this option is enabled, or else
we won't be able to render the highlight damage regions.
This commit is contained in:
Alexander Orzechowski 2022-05-23 15:58:49 -04:00
parent b6fc882782
commit 41124592dd
2 changed files with 102 additions and 1 deletions

View file

@ -91,6 +91,7 @@ struct wlr_scene {
struct wl_listener presentation_destroy;
enum wlr_scene_debug_damage_option debug_damage_option;
struct wl_list damage_highlight_regions;
};
/** A sub-tree in the scene-graph. */

View file

@ -1,3 +1,4 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -12,6 +13,9 @@
#include <wlr/util/region.h>
#include "types/wlr_scene.h"
#include "util/signal.h"
#include "util/time.h"
#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250
static struct wlr_scene *scene_root_from_node(struct wlr_scene_node *node) {
assert(node->type == WLR_SCENE_NODE_ROOT);
@ -67,6 +71,18 @@ static void scene_node_init(struct wlr_scene_node *node,
static void scene_node_damage_whole(struct wlr_scene_node *node);
struct highlight_region {
pixman_region32_t region;
struct timespec when;
struct wl_list link;
};
static void highlight_region_destroy(struct highlight_region *damage) {
wl_list_remove(&damage->link);
pixman_region32_fini(&damage->region);
free(damage);
}
void wlr_scene_node_destroy(struct wlr_scene_node *node) {
if (node == NULL) {
return;
@ -88,6 +104,11 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
wlr_scene_output_destroy(scene_output);
}
struct highlight_region *damage, *tmp_damage;
wl_list_for_each_safe(damage, tmp_damage, &scene->damage_highlight_regions, link) {
highlight_region_destroy(damage);
}
wl_list_remove(&scene->presentation_destroy.link);
break;
case WLR_SCENE_NODE_BUFFER:;
@ -130,6 +151,7 @@ struct wlr_scene *wlr_scene_create(void) {
scene_node_init(&scene->node, WLR_SCENE_NODE_ROOT, NULL);
wl_list_init(&scene->outputs);
wl_list_init(&scene->presentation_destroy.link);
wl_list_init(&scene->damage_highlight_regions);
char *debug_damage = getenv("WLR_SCENE_DEBUG_DAMAGE");
if (debug_damage) {
@ -1056,6 +1078,13 @@ static void check_scanout_iterator(struct wlr_scene_node *node,
}
static bool scene_output_scanout(struct wlr_scene_output *scene_output) {
if (scene_output->scene->debug_damage_option ==
WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
// We don't want to enter direct scan out if we have highlight regions
// enabled. Otherwise, we won't be able to render the damage regions.
return false;
}
struct wlr_output *output = scene_output->output;
struct wlr_box viewport_box = { .x = scene_output->x, .y = scene_output->y };
@ -1126,6 +1155,45 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
wlr_output_damage_add_whole(scene_output->damage);
}
struct timespec now;
if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
struct wl_list *regions = &scene_output->scene->damage_highlight_regions;
clock_gettime(CLOCK_MONOTONIC, &now);
// add the current frame's damage if there is damage
if (pixman_region32_not_empty(&scene_output->damage->current)) {
struct highlight_region *current_damage =
calloc(1, sizeof(*current_damage));
if (current_damage) {
pixman_region32_init(&current_damage->region);
pixman_region32_copy(&current_damage->region,
&scene_output->damage->current);
memcpy(&current_damage->when, &now, sizeof(now));
wl_list_insert(regions, &current_damage->link);
}
}
pixman_region32_t acc_damage;
pixman_region32_init(&acc_damage);
struct highlight_region *damage, *tmp_damage;
wl_list_for_each_safe(damage, tmp_damage, regions, link) {
// remove overlaping damage regions
pixman_region32_subtract(&damage->region, &damage->region, &acc_damage);
pixman_region32_union(&acc_damage, &acc_damage, &damage->region);
// if this damage is too old or has nothing in it, get rid of it
struct timespec time_diff;
timespec_sub(&time_diff, &now, &damage->when);
if (timespec_to_msec(&time_diff) >= HIGHLIGHT_DAMAGE_FADEOUT_TIME ||
!pixman_region32_not_empty(&damage->region)) {
highlight_region_destroy(damage);
}
}
wlr_output_damage_add(scene_output->damage, &acc_damage);
pixman_region32_fini(&acc_damage);
}
bool needs_frame;
pixman_region32_t damage;
pixman_region32_init(&damage);
@ -1158,6 +1226,31 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
-scene_output->x, -scene_output->y,
render_node_iterator, &data);
wlr_renderer_scissor(renderer, NULL);
if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
struct highlight_region *damage;
wl_list_for_each(damage, &scene_output->scene->damage_highlight_regions, link) {
struct timespec time_diff;
timespec_sub(&time_diff, &now, &damage->when);
int64_t time_diff_ms = timespec_to_msec(&time_diff);
float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME;
int nrects;
pixman_box32_t *rects = pixman_region32_rectangles(&damage->region, &nrects);
for (int i = 0; i < nrects; ++i) {
struct wlr_box box = {
.x = rects[i].x1,
.y = rects[i].y1,
.width = rects[i].x2 - rects[i].x1,
.height = rects[i].y2 - rects[i].y1,
};
float color[4] = { alpha * .5, 0.0, 0.0, alpha * .5 };
wlr_render_rect(renderer, &box, color, output->transform_matrix);
}
}
}
wlr_output_render_software_cursors(output, &damage);
wlr_renderer_end(renderer);
@ -1176,7 +1269,14 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
wlr_output_set_damage(output, &frame_damage);
pixman_region32_fini(&frame_damage);
return wlr_output_commit(output);
bool success = wlr_output_commit(output);
if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT &&
!wl_list_empty(&scene_output->scene->damage_highlight_regions)) {
wlr_output_schedule_frame(scene_output->output);
}
return success;
}
static void scene_node_send_frame_done(struct wlr_scene_node *node,