aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/rootston/output.h16
-rw-r--r--include/wlr/types/wlr_output.h9
-rw-r--r--include/wlr/types/wlr_output_damage.h77
-rw-r--r--rootston/output.c99
-rw-r--r--types/meson.build1
-rw-r--r--types/wlr_output_damage.c182
6 files changed, 288 insertions, 96 deletions
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 <time.h>
#include <pixman.h>
#include <wayland-server.h>
-
-/**
- * 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 <wlr/types/wlr_output_damage.h>
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.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
new file mode 100644
index 00000000..52cbce78
--- /dev/null
+++ b/include/wlr/types/wlr_output_damage.h
@@ -0,0 +1,77 @@
+#ifndef WLR_TYPES_WLR_OUTPUT_DAMAGE_H
+#define WLR_TYPES_WLR_OUTPUT_DAMAGE_H
+
+#include <time.h>
+#include <pixman.h>
+#include <wlr/types/wlr_output.h>
+
+/**
+ * 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
+
+/**
+ * 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;
+
+ 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_transform;
+ struct wl_listener output_scale;
+ 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);
+/**
+ * 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);
+
+#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)) {
- return;
- }
-
- int width, height;
- wlr_output_transformed_resolution(output->wlr_output, &width, &height);
-
- // Check if we can use damage tracking
+ bool needs_swap;
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]);
- }
+ if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) {
+ return;
}
- 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..354314b6
--- /dev/null
+++ b/types/wlr_output_damage.c
@@ -0,0 +1,182 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <time.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_damage.h>
+
+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_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);
+ 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.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);
+ 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_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);
+ 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);
+}