aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Primak <vyivel@eclair.cafe>2022-04-30 08:55:16 +0300
committerKirill Primak <vyivel@eclair.cafe>2022-06-29 18:54:21 +0300
commite0accb2d50f8a3ba74802e6a8673af2eb1aa1e68 (patch)
tree793a78a19b281154a10b1d74508803912c071da4
parent10a821a87ddabfdef50f8c5a562c3aea44215019 (diff)
types: add wlr_damage_ring
wlr_damage_ring is effectively wlr_output_damage untied from wlr_output.
-rw-r--r--include/wlr/types/wlr_damage_ring.h81
-rw-r--r--types/meson.build1
-rw-r--r--types/wlr_damage_ring.c113
3 files changed, 195 insertions, 0 deletions
diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h
new file mode 100644
index 00000000..9415a556
--- /dev/null
+++ b/include/wlr/types/wlr_damage_ring.h
@@ -0,0 +1,81 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_DAMAGE_RING_H
+#define WLR_TYPES_WLR_DAMAGE_RING_H
+
+#include <stdbool.h>
+
+/* For triple buffering, a history of two frames is required. */
+#define WLR_DAMAGE_RING_PREVIOUS_LEN 2
+
+struct wlr_box;
+
+struct wlr_damage_ring {
+ int32_t width, height;
+
+ // Difference between the current buffer and the previous one
+ pixman_region32_t current;
+
+ // private state
+
+ pixman_region32_t previous[WLR_DAMAGE_RING_PREVIOUS_LEN];
+ size_t previous_idx;
+};
+
+void wlr_damage_ring_init(struct wlr_damage_ring *ring);
+
+void wlr_damage_ring_finish(struct wlr_damage_ring *ring);
+
+/**
+ * Set ring bounds and damage the ring fully.
+ *
+ * Next time damage will be added, it will be cropped to the ring bounds.
+ * If at least one of the dimensions is 0, bounds are removed.
+ *
+ * By default, a damage ring doesn't have bounds.
+ */
+void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring,
+ int32_t width, int32_t height);
+
+/**
+ * Add a region to the current damage.
+ *
+ * Returns true if the region intersects the ring bounds, false otherwise.
+ */
+bool wlr_damage_ring_add(struct wlr_damage_ring *ring,
+ pixman_region32_t *damage);
+
+/**
+ * Add a box to the current damage.
+ *
+ * Returns true if the box intersects the ring bounds, false otherwise.
+ */
+bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring,
+ const struct wlr_box *box);
+
+/**
+ * Damage the ring fully.
+ */
+void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring);
+
+/**
+ * Rotate the damage ring. This needs to be called after using the accumulated
+ * damage, e.g. after rendering to an output's back buffer.
+ */
+void wlr_damage_ring_rotate(struct wlr_damage_ring *ring);
+
+/**
+ * Get accumulated damage, which is the difference between the current buffer
+ * and the buffer with age of buffer_age; in context of rendering, this is
+ * the region that needs to be redrawn.
+ */
+void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring,
+ int buffer_age, pixman_region32_t *damage);
+
+#endif
diff --git a/types/meson.build b/types/meson.build
index 16870b83..53bf852b 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -30,6 +30,7 @@ wlr_files += files(
'wlr_buffer.c',
'wlr_compositor.c',
'wlr_cursor.c',
+ 'wlr_damage_ring.c',
'wlr_data_control_v1.c',
'wlr_drm.c',
'wlr_export_dmabuf_v1.c',
diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c
new file mode 100644
index 00000000..378051c7
--- /dev/null
+++ b/types/wlr_damage_ring.c
@@ -0,0 +1,113 @@
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pixman.h>
+#include <wlr/types/wlr_damage_ring.h>
+#include <wlr/util/box.h>
+#include "util/signal.h"
+
+#define WLR_DAMAGE_RING_MAX_RECTS 20
+
+void wlr_damage_ring_init(struct wlr_damage_ring *ring) {
+ memset(ring, 0, sizeof(*ring));
+
+ ring->width = INT_MAX;
+ ring->height = INT_MAX;
+
+ pixman_region32_init(&ring->current);
+ for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) {
+ pixman_region32_init(&ring->previous[i]);
+ }
+}
+
+void wlr_damage_ring_finish(struct wlr_damage_ring *ring) {
+ pixman_region32_fini(&ring->current);
+ for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) {
+ pixman_region32_fini(&ring->previous[i]);
+ }
+}
+
+void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring,
+ int32_t width, int32_t height) {
+ if (width == 0 || height == 0) {
+ ring->width = INT_MAX;
+ ring->height = INT_MAX;
+ } else {
+ ring->width = width;
+ ring->height = height;
+ }
+ wlr_damage_ring_add_whole(ring);
+}
+
+bool wlr_damage_ring_add(struct wlr_damage_ring *ring,
+ pixman_region32_t *damage) {
+ pixman_region32_t clipped;
+ pixman_region32_init(&clipped);
+ pixman_region32_intersect_rect(&clipped, damage,
+ 0, 0, ring->width, ring->height);
+ bool intersects = pixman_region32_not_empty(&clipped);
+ if (intersects) {
+ pixman_region32_union(&ring->current, &ring->current, &clipped);
+ }
+ pixman_region32_fini(&clipped);
+ return intersects;
+}
+
+bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring,
+ const struct wlr_box *box) {
+ struct wlr_box clipped = {
+ .x = 0,
+ .y = 0,
+ .width = ring->width,
+ .height = ring->height,
+ };
+ if (wlr_box_intersection(&clipped, &clipped, box)) {
+ pixman_region32_union_rect(&ring->current,
+ &ring->current, clipped.x, clipped.y,
+ clipped.width, clipped.height);
+ return true;
+ }
+ return false;
+}
+
+void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring) {
+ pixman_region32_union_rect(&ring->current,
+ &ring->current, 0, 0, ring->width, ring->height);
+}
+
+void wlr_damage_ring_rotate(struct wlr_damage_ring *ring) {
+ // modular decrement
+ ring->previous_idx = ring->previous_idx +
+ WLR_DAMAGE_RING_PREVIOUS_LEN - 1;
+ ring->previous_idx %= WLR_DAMAGE_RING_PREVIOUS_LEN;
+
+ pixman_region32_copy(&ring->previous[ring->previous_idx], &ring->current);
+ pixman_region32_clear(&ring->current);
+}
+
+void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring,
+ int buffer_age, pixman_region32_t *damage) {
+ if (buffer_age <= 0 || buffer_age - 1 > WLR_DAMAGE_RING_PREVIOUS_LEN) {
+ pixman_region32_clear(damage);
+ pixman_region32_union_rect(damage, damage,
+ 0, 0, ring->width, ring->height);
+ } else {
+ pixman_region32_copy(damage, &ring->current);
+
+ // Accumulate damage from old buffers
+ for (int i = 0; i < buffer_age - 1; ++i) {
+ int j = (ring->previous_idx + i) % WLR_DAMAGE_RING_PREVIOUS_LEN;
+ pixman_region32_union(damage, damage, &ring->previous[j]);
+ }
+
+ // Check the number of rectangles
+ int n_rects = pixman_region32_n_rects(damage);
+ if (n_rects > WLR_DAMAGE_RING_MAX_RECTS) {
+ pixman_box32_t *extents = pixman_region32_extents(damage);
+ pixman_region32_union_rect(damage, damage,
+ extents->x1, extents->y1,
+ extents->x2 - extents->x1,
+ extents->y2 - extents->y1);
+ }
+ }
+}