From ec837e3c9e08a8334fa7093d7f39af2a2f11e122 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 11 Feb 2018 12:49:30 +0100 Subject: [PATCH 1/3] Introduce wlr_output_damage --- include/rootston/output.h | 16 +-- include/wlr/types/wlr_output_damage.h | 45 +++++++ rootston/output.c | 101 +++------------- types/meson.build | 1 + types/wlr_output_damage.c | 164 ++++++++++++++++++++++++++ 5 files changed, 230 insertions(+), 97 deletions(-) create mode 100644 include/wlr/types/wlr_output_damage.h create mode 100644 types/wlr_output_damage.c diff --git a/include/rootston/output.h b/include/rootston/output.h index 3a6d3cc7..7f42904f 100644 --- a/include/rootston/output.h +++ b/include/rootston/output.h @@ -4,13 +4,7 @@ #include #include #include - -/** - * Damage tracking requires to keep track of previous frames' damage. To allow - * damage tracking to work with triple buffering, a history of two frames is - * required. - */ -#define ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN 2 +#include struct roots_desktop; @@ -22,15 +16,9 @@ struct roots_output { struct roots_view *fullscreen_view; struct timespec last_frame; - pixman_region32_t damage; // in ouput-local coordinates - - // circular queue for previous damage - pixman_region32_t previous_damage[ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN]; - size_t previous_damage_idx; + struct wlr_output_damage *damage; struct wl_listener frame; - struct wl_listener mode; - struct wl_listener needs_swap; }; void output_add_notify(struct wl_listener *listener, void *data); diff --git a/include/wlr/types/wlr_output_damage.h b/include/wlr/types/wlr_output_damage.h new file mode 100644 index 00000000..eadbff29 --- /dev/null +++ b/include/wlr/types/wlr_output_damage.h @@ -0,0 +1,45 @@ +#ifndef WLR_TYPES_WLR_OUTPUT_DAMAGE_H +#define WLR_TYPES_WLR_OUTPUT_DAMAGE_H + +#include + +/** + * Damage tracking requires to keep track of previous frames' damage. To allow + * damage tracking to work with triple buffering, a history of two frames is + * required. + */ +#define WLR_OUTPUT_DAMAGE_PREVIOUS_LEN 2 + +struct wlr_output_damage { + struct wlr_output *output; + + pixman_region32_t current; // in output-local coordinates + + // circular queue for previous damage + pixman_region32_t previous[WLR_OUTPUT_DAMAGE_PREVIOUS_LEN]; + size_t previous_idx; + + struct { + struct wl_signal frame; + struct wl_signal destroy; + } events; + + struct wl_listener output_destroy; + struct wl_listener output_mode; + struct wl_listener output_needs_swap; + struct wl_listener output_frame; +}; + +struct wlr_output_damage *wlr_output_damage_create(struct wlr_output *output); +void wlr_output_damage_destroy(struct wlr_output_damage *output_damage); +bool wlr_output_damage_make_current(struct wlr_output_damage *output_damage, + bool *needs_swap, pixman_region32_t *damage); +bool wlr_output_damage_swap_buffers(struct wlr_output_damage *output_damage, + struct timespec *when, pixman_region32_t *damage); +void wlr_output_damage_add(struct wlr_output_damage *output_damage, + pixman_region32_t *damage); +void wlr_output_damage_add_whole(struct wlr_output_damage *output_damage); +void wlr_output_damage_add_box(struct wlr_output_damage *output_damage, + struct wlr_box *box); + +#endif diff --git a/rootston/output.c b/rootston/output.c index 0e0c6114..fce875da 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -390,33 +390,14 @@ static void render_output(struct roots_output *output) { wlr_output_set_fullscreen_surface(wlr_output, NULL); } - int buffer_age = -1; - if (!wlr_output_make_current(wlr_output, &buffer_age)) { + bool needs_swap; + pixman_region32_t damage; + pixman_region32_init(&damage); + if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) { return; } - int width, height; - wlr_output_transformed_resolution(output->wlr_output, &width, &height); - - // Check if we can use damage tracking - pixman_region32_t damage; - pixman_region32_init(&damage); - if (buffer_age <= 0 || buffer_age - 1 > ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN) { - // Buffer new or too old, damage the whole output - pixman_region32_union_rect(&damage, &damage, 0, 0, width, height); - } else { - pixman_region32_copy(&damage, &output->damage); - - // Accumulate damage from old buffers - size_t idx = output->previous_damage_idx; - for (int i = 0; i < buffer_age - 1; ++i) { - int j = (idx + i) % ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN; - pixman_region32_union(&damage, &damage, &output->previous_damage[j]); - } - } - pixman_region32_intersect_rect(&damage, &damage, 0, 0, width, height); - - if (!pixman_region32_not_empty(&damage) && !wlr_output->needs_swap) { + if (!needs_swap) { // Output doesn't need swap and isn't damaged, skip rendering completely goto damage_finish; } @@ -487,34 +468,23 @@ static void render_output(struct roots_output *output) { renderer_end: wlr_renderer_scissor(renderer, NULL); wlr_renderer_end(renderer); - if (!wlr_output_swap_buffers(wlr_output, &now, &damage)) { + if (!wlr_output_damage_swap_buffers(output->damage, &now, &damage)) { goto damage_finish; } - // same as decrementing, but works on unsigned integers - output->previous_damage_idx += ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN - 1; - output->previous_damage_idx %= ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN; - pixman_region32_copy(&output->previous_damage[output->previous_damage_idx], - &output->damage); - pixman_region32_clear(&output->damage); output->last_frame = desktop->last_frame = now; damage_finish: pixman_region32_fini(&damage); } -static void output_handle_frame(struct wl_listener *listener, void *data) { +static void output_damage_handle_frame(struct wl_listener *listener, + void *data) { struct roots_output *output = wl_container_of(listener, output, frame); render_output(output); } void output_damage_whole(struct roots_output *output) { - int width, height; - wlr_output_transformed_resolution(output->wlr_output, &width, &height); - - pixman_region32_union_rect(&output->damage, &output->damage, 0, 0, - width, height); - - wlr_output_schedule_frame(output->wlr_output); + wlr_output_damage_add_whole(output->damage); } static bool view_accept_damage(struct roots_output *output, @@ -561,10 +531,7 @@ static void damage_whole_surface(struct wlr_surface *surface, wlr_box_rotated_bounds(&box, -rotation, &box); - pixman_region32_union_rect(&output->damage, &output->damage, - box.x, box.y, box.width, box.height); - - wlr_output_schedule_frame(output->wlr_output); + wlr_output_damage_add_box(output->damage, &box); } static void damage_whole_decoration(struct roots_view *view, @@ -578,8 +545,7 @@ static void damage_whole_decoration(struct roots_view *view, wlr_box_rotated_bounds(&box, -view->rotation, &box); - pixman_region32_union_rect(&output->damage, &output->damage, - box.x, box.y, box.width, box.height); + wlr_output_damage_add_box(output->damage, &box); } void output_damage_whole_view(struct roots_output *output, @@ -626,8 +592,7 @@ static void damage_from_surface(struct wlr_surface *surface, ceil(wlr_output->scale) - surface->current->scale); } pixman_region32_translate(&damage, box.x, box.y); - pixman_region32_union(&output->damage, &output->damage, &damage); - pixman_region32_fini(&damage); + wlr_output_damage_add(output->damage, &damage); } else { pixman_box32_t *extents = pixman_region32_extents(&surface->current->surface_damage); @@ -638,14 +603,8 @@ static void damage_from_surface(struct wlr_surface *surface, .height = (extents->y2 - extents->y1) * wlr_output->scale, }; wlr_box_rotated_bounds(&damage_box, -rotation, &damage_box); - pixman_region32_union_rect(&output->damage, &output->damage, - damage_box.x, damage_box.y, damage_box.width, damage_box.height); + wlr_output_damage_add_box(output->damage, &damage_box); } - - pixman_region32_intersect_rect(&output->damage, &output->damage, 0, 0, - ow, oh); - - wlr_output_schedule_frame(wlr_output); } void output_damage_from_view(struct roots_output *output, @@ -657,19 +616,6 @@ void output_damage_from_view(struct roots_output *output, view_for_each_surface(view, damage_from_surface, output); } -static void output_handle_mode(struct wl_listener *listener, void *data) { - struct roots_output *output = wl_container_of(listener, output, mode); - output_damage_whole(output); -} - -static void output_handle_needs_swap(struct wl_listener *listener, void *data) { - struct roots_output *output = - wl_container_of(listener, output, needs_swap); - pixman_region32_union(&output->damage, &output->damage, - &output->wlr_output->damage); - wlr_output_schedule_frame(output->wlr_output); -} - static void set_mode(struct wlr_output *output, struct roots_output_config *oc) { int mhz = (int)(oc->mode.refresh_rate * 1000); @@ -721,17 +667,11 @@ void output_add_notify(struct wl_listener *listener, void *data) { output->desktop = desktop; output->wlr_output = wlr_output; wl_list_insert(&desktop->outputs, &output->link); - pixman_region32_init(&output->damage); - for (size_t i = 0; i < ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN; ++i) { - pixman_region32_init(&output->previous_damage[i]); - } - output->frame.notify = output_handle_frame; - wl_signal_add(&wlr_output->events.frame, &output->frame); - output->mode.notify = output_handle_mode; - wl_signal_add(&wlr_output->events.mode, &output->mode); - output->needs_swap.notify = output_handle_needs_swap; - wl_signal_add(&wlr_output->events.needs_swap, &output->needs_swap); + output->damage = wlr_output_damage_create(wlr_output); + + output->frame.notify = output_damage_handle_frame; + wl_signal_add(&output->damage->events.frame, &output->frame); struct roots_output_config *output_config = roots_config_get_output(config, wlr_output); @@ -782,13 +722,8 @@ void output_remove_notify(struct wl_listener *listener, void *data) { //example_config_configure_cursor(sample->config, sample->cursor, // sample->compositor); - pixman_region32_fini(&output->damage); - for (size_t i = 0; i < ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN; ++i) { - pixman_region32_fini(&output->previous_damage[i]); - } wl_list_remove(&output->link); wl_list_remove(&output->frame.link); - wl_list_remove(&output->mode.link); - wl_list_remove(&output->needs_swap.link); + wlr_output_damage_destroy(output->damage); free(output); } diff --git a/types/meson.build b/types/meson.build index 62a2faec..9f37cc86 100644 --- a/types/meson.build +++ b/types/meson.build @@ -11,6 +11,7 @@ lib_wlr_types = static_library( 'wlr_keyboard.c', 'wlr_list.c', 'wlr_output.c', + 'wlr_output_damage.c', 'wlr_output_layout.c', 'wlr_pointer.c', 'wlr_primary_selection.c', diff --git a/types/wlr_output_damage.c b/types/wlr_output_damage.c new file mode 100644 index 00000000..741360ac --- /dev/null +++ b/types/wlr_output_damage.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include + +static void output_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_output_damage *output_damage = + wl_container_of(listener, output_damage, output_destroy); + wlr_output_damage_destroy(output_damage); +} + +static void output_handle_mode(struct wl_listener *listener, void *data) { + struct wlr_output_damage *output_damage = + wl_container_of(listener, output_damage, output_mode); + wlr_output_damage_add_whole(output_damage); +} + +static void output_handle_needs_swap(struct wl_listener *listener, void *data) { + struct wlr_output_damage *output_damage = + wl_container_of(listener, output_damage, output_needs_swap); + pixman_region32_union(&output_damage->current, &output_damage->current, + &output_damage->output->damage); + wlr_output_schedule_frame(output_damage->output); +} + +static void output_handle_frame(struct wl_listener *listener, void *data) { + struct wlr_output_damage *output_damage = + wl_container_of(listener, output_damage, output_frame); + + if (!output_damage->output->enabled) { + return; + } + + wl_signal_emit(&output_damage->events.frame, output_damage); +} + +struct wlr_output_damage *wlr_output_damage_create(struct wlr_output *output) { + struct wlr_output_damage *output_damage = + calloc(1, sizeof(struct wlr_output_damage)); + if (output_damage == NULL) { + return NULL; + } + + output_damage->output = output; + wl_signal_init(&output_damage->events.frame); + wl_signal_init(&output_damage->events.destroy); + + pixman_region32_init(&output_damage->current); + for (size_t i = 0; i < WLR_OUTPUT_DAMAGE_PREVIOUS_LEN; ++i) { + pixman_region32_init(&output_damage->previous[i]); + } + + wl_signal_add(&output->events.destroy, &output_damage->output_destroy); + output_damage->output_destroy.notify = output_handle_destroy; + wl_signal_add(&output->events.mode, &output_damage->output_mode); + output_damage->output_mode.notify = output_handle_mode; + wl_signal_add(&output->events.needs_swap, &output_damage->output_needs_swap); + output_damage->output_needs_swap.notify = output_handle_needs_swap; + wl_signal_add(&output->events.frame, &output_damage->output_frame); + output_damage->output_frame.notify = output_handle_frame; + + return output_damage; +} + +void wlr_output_damage_destroy(struct wlr_output_damage *output_damage) { + if (output_damage == NULL) { + return; + } + wl_signal_emit(&output_damage->events.destroy, output_damage); + wl_list_remove(&output_damage->output_destroy.link); + wl_list_remove(&output_damage->output_mode.link); + wl_list_remove(&output_damage->output_needs_swap.link); + wl_list_remove(&output_damage->output_frame.link); + pixman_region32_fini(&output_damage->current); + for (size_t i = 0; i < WLR_OUTPUT_DAMAGE_PREVIOUS_LEN; ++i) { + pixman_region32_fini(&output_damage->previous[i]); + } + free(output_damage); +} + +bool wlr_output_damage_make_current(struct wlr_output_damage *output_damage, + bool *needs_swap, pixman_region32_t *damage) { + struct wlr_output *output = output_damage->output; + + int buffer_age = -1; + if (!wlr_output_make_current(output, &buffer_age)) { + return false; + } + + // Check if we can use damage tracking + if (buffer_age <= 0 || buffer_age - 1 > WLR_OUTPUT_DAMAGE_PREVIOUS_LEN) { + int width, height; + wlr_output_transformed_resolution(output, &width, &height); + + // Buffer new or too old, damage the whole output + pixman_region32_union_rect(damage, damage, 0, 0, width, height); + } else { + pixman_region32_copy(damage, &output_damage->current); + + // Accumulate damage from old buffers + size_t idx = output_damage->previous_idx; + for (int i = 0; i < buffer_age - 1; ++i) { + int j = (idx + i) % WLR_OUTPUT_DAMAGE_PREVIOUS_LEN; + pixman_region32_union(damage, damage, &output_damage->previous[j]); + } + } + + *needs_swap = output->needs_swap || pixman_region32_not_empty(damage); + return true; +} + +bool wlr_output_damage_swap_buffers(struct wlr_output_damage *output_damage, + struct timespec *when, pixman_region32_t *damage) { + if (!wlr_output_swap_buffers(output_damage->output, when, damage)) { + return false; + } + + // same as decrementing, but works on unsigned integers + output_damage->previous_idx += WLR_OUTPUT_DAMAGE_PREVIOUS_LEN - 1; + output_damage->previous_idx %= WLR_OUTPUT_DAMAGE_PREVIOUS_LEN; + + pixman_region32_copy(&output_damage->previous[output_damage->previous_idx], + &output_damage->current); + pixman_region32_clear(&output_damage->current); + + return true; +} + +void wlr_output_damage_add(struct wlr_output_damage *output_damage, + pixman_region32_t *damage) { + int width, height; + wlr_output_transformed_resolution(output_damage->output, &width, &height); + + pixman_region32_union(&output_damage->current, &output_damage->current, + damage); + pixman_region32_intersect_rect(&output_damage->current, + &output_damage->current, 0, 0, width, height); + wlr_output_schedule_frame(output_damage->output); +} + +void wlr_output_damage_add_whole(struct wlr_output_damage *output_damage) { + int width, height; + wlr_output_transformed_resolution(output_damage->output, &width, &height); + + pixman_region32_union_rect(&output_damage->current, &output_damage->current, + 0, 0, width, height); + + wlr_output_schedule_frame(output_damage->output); +} + +void wlr_output_damage_add_box(struct wlr_output_damage *output_damage, + struct wlr_box *box) { + int width, height; + wlr_output_transformed_resolution(output_damage->output, &width, &height); + + pixman_region32_union_rect(&output_damage->current, &output_damage->current, + box->x, box->y, box->width, box->height); + pixman_region32_intersect_rect(&output_damage->current, + &output_damage->current, 0, 0, width, height); + wlr_output_schedule_frame(output_damage->output); +} From 5a8f098eea1637213d29a6ec831bcebc11c92aaa Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 11 Feb 2018 13:04:00 +0100 Subject: [PATCH 2/3] output, output_damage: add docs --- include/wlr/types/wlr_output.h | 9 +++++++++ include/wlr/types/wlr_output_damage.h | 28 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index a653d527..8eed06fe 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -38,6 +38,15 @@ struct wlr_output_cursor { struct wlr_output_impl; +/** + * A compositor output region. This typically corresponds to a monitor that + * displays part of the compositor space. + * + * Compositors should listen to the `frame` event to render an output. They + * should call `wlr_output_make_current`, render and then call + * `wlr_output_swap_buffers`. No rendering should happen outside a `frame` event + * handler. + */ struct wlr_output { const struct wlr_output_impl *impl; struct wlr_backend *backend; diff --git a/include/wlr/types/wlr_output_damage.h b/include/wlr/types/wlr_output_damage.h index eadbff29..ec5fcd0d 100644 --- a/include/wlr/types/wlr_output_damage.h +++ b/include/wlr/types/wlr_output_damage.h @@ -10,6 +10,14 @@ */ #define WLR_OUTPUT_DAMAGE_PREVIOUS_LEN 2 +/** + * Tracks damage for an output. + * + * When a `frame` event is emitted, `wlr_output_damage_make_current` should be + * called. If necessary, the output should be repainted and + * `wlr_output_damage_swap_buffers` should be called. No rendering should happen + * outside a `frame` event handler. + */ struct wlr_output_damage { struct wlr_output *output; @@ -32,13 +40,33 @@ struct wlr_output_damage { struct wlr_output_damage *wlr_output_damage_create(struct wlr_output *output); void wlr_output_damage_destroy(struct wlr_output_damage *output_damage); +/** + * Makes the output rendering context current. `needs_swap` is set to true if + * `wlr_output_damage_swap_buffers` needs to be called. The region of the output + * that needs to be repainted is added to `damage`. + */ bool wlr_output_damage_make_current(struct wlr_output_damage *output_damage, bool *needs_swap, pixman_region32_t *damage); +/** + * Swaps the output buffers. If the time of the frame isn't known, set `when` to + * NULL. + * + * Swapping buffers schedules a `frame` event. + */ bool wlr_output_damage_swap_buffers(struct wlr_output_damage *output_damage, struct timespec *when, pixman_region32_t *damage); +/** + * Accumulates damage and schedules a `frame` event. + */ void wlr_output_damage_add(struct wlr_output_damage *output_damage, pixman_region32_t *damage); +/** + * Damages the whole output and schedules a `frame` event. + */ void wlr_output_damage_add_whole(struct wlr_output_damage *output_damage); +/** + * Accumulates damage from a box and schedules a `frame` event. + */ void wlr_output_damage_add_box(struct wlr_output_damage *output_damage, struct wlr_box *box); From a9632341bfcb1e0a172d93c4fd331291a842332c Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 11 Feb 2018 13:07:00 +0100 Subject: [PATCH 3/3] output_damage: listen to transform and scale output events --- include/wlr/types/wlr_output_damage.h | 4 ++++ types/wlr_output_damage.c | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/include/wlr/types/wlr_output_damage.h b/include/wlr/types/wlr_output_damage.h index ec5fcd0d..52cbce78 100644 --- a/include/wlr/types/wlr_output_damage.h +++ b/include/wlr/types/wlr_output_damage.h @@ -1,7 +1,9 @@ #ifndef WLR_TYPES_WLR_OUTPUT_DAMAGE_H #define WLR_TYPES_WLR_OUTPUT_DAMAGE_H +#include #include +#include /** * Damage tracking requires to keep track of previous frames' damage. To allow @@ -34,6 +36,8 @@ struct wlr_output_damage { struct wl_listener output_destroy; struct wl_listener output_mode; + struct wl_listener output_transform; + struct wl_listener output_scale; struct wl_listener output_needs_swap; struct wl_listener output_frame; }; diff --git a/types/wlr_output_damage.c b/types/wlr_output_damage.c index 741360ac..354314b6 100644 --- a/types/wlr_output_damage.c +++ b/types/wlr_output_damage.c @@ -18,6 +18,18 @@ static void output_handle_mode(struct wl_listener *listener, void *data) { wlr_output_damage_add_whole(output_damage); } +static void output_handle_transform(struct wl_listener *listener, void *data) { + struct wlr_output_damage *output_damage = + wl_container_of(listener, output_damage, output_transform); + wlr_output_damage_add_whole(output_damage); +} + +static void output_handle_scale(struct wl_listener *listener, void *data) { + struct wlr_output_damage *output_damage = + wl_container_of(listener, output_damage, output_scale); + wlr_output_damage_add_whole(output_damage); +} + static void output_handle_needs_swap(struct wl_listener *listener, void *data) { struct wlr_output_damage *output_damage = wl_container_of(listener, output_damage, output_needs_swap); @@ -57,6 +69,10 @@ struct wlr_output_damage *wlr_output_damage_create(struct wlr_output *output) { output_damage->output_destroy.notify = output_handle_destroy; wl_signal_add(&output->events.mode, &output_damage->output_mode); output_damage->output_mode.notify = output_handle_mode; + wl_signal_add(&output->events.transform, &output_damage->output_transform); + output_damage->output_transform.notify = output_handle_transform; + wl_signal_add(&output->events.scale, &output_damage->output_scale); + output_damage->output_scale.notify = output_handle_scale; wl_signal_add(&output->events.needs_swap, &output_damage->output_needs_swap); output_damage->output_needs_swap.notify = output_handle_needs_swap; wl_signal_add(&output->events.frame, &output_damage->output_frame); @@ -72,6 +88,8 @@ void wlr_output_damage_destroy(struct wlr_output_damage *output_damage) { wl_signal_emit(&output_damage->events.destroy, output_damage); wl_list_remove(&output_damage->output_destroy.link); wl_list_remove(&output_damage->output_mode.link); + wl_list_remove(&output_damage->output_transform.link); + wl_list_remove(&output_damage->output_scale.link); wl_list_remove(&output_damage->output_needs_swap.link); wl_list_remove(&output_damage->output_frame.link); pixman_region32_fini(&output_damage->current);