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,