From 41124592dd55b11e39a18b0aa843511e3e4597bb Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 23 May 2022 15:58:49 -0400 Subject: [PATCH] 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. --- include/wlr/types/wlr_scene.h | 1 + types/scene/wlr_scene.c | 102 +++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 2625dd80..0802014e 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -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. */ diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 6f0591bc..676fe127 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -12,6 +13,9 @@ #include #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(¤t_damage->region); + pixman_region32_copy(¤t_damage->region, + &scene_output->damage->current); + memcpy(¤t_damage->when, &now, sizeof(now)); + wl_list_insert(regions, ¤t_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,