diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index 457448c20..95ed4699e 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -16,10 +16,15 @@ #include struct wlr_box; +struct wlr_buffer; struct wlr_damage_ring_entry { pixman_region32_t damage; + struct wlr_buffer *buffer; + struct wl_listener buffer_destroy; + + struct wlr_damage_ring *ring; struct wl_list link; // struct wlr_damage_ring.previous }; @@ -84,4 +89,14 @@ void wlr_damage_ring_rotate(struct wlr_damage_ring *ring); void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, int buffer_age, pixman_region32_t *damage); +/** + * Get the since accumulated damage for this buffer. These buffers should + * typically come from a wlr_swapchain. In the context of rendering, the + * damage is the region that needs to be redrawn. + * + * The damage ring is automatically rotated during invocation. + */ +void wlr_damage_ring_damage_for_buffer(struct wlr_damage_ring *ring, + struct wlr_buffer *buffer, pixman_region32_t *damage); + #endif diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index 0862d91fa..3542767cf 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,10 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring) { } static void entry_destroy(struct wlr_damage_ring_entry *entry) { + if (entry->buffer) { + wl_list_remove(&entry->buffer_destroy.link); + } + wl_list_remove(&entry->link); pixman_region32_fini(&entry->damage); free(entry); @@ -132,3 +137,76 @@ void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, extents->y2 - extents->y1); } } + +static void entry_squash_damage(struct wlr_damage_ring_entry *entry) { + pixman_region32_t *prev; + if (entry->link.prev == &entry->ring->previous) { + // this entry is the first in the list + prev = &entry->ring->current; + } else { + struct wlr_damage_ring_entry *last = + wl_container_of(entry->link.prev, last, link); + prev = &last->damage; + } + + pixman_region32_union(prev, prev, &entry->damage); +} + +static void handle_buffer_destroy(struct wl_listener *listener, void *data) { + struct wlr_damage_ring_entry *entry = + wl_container_of(listener, entry, buffer_destroy); + entry_squash_damage(entry); + entry_destroy(entry); +} + +void wlr_damage_ring_damage_for_buffer(struct wlr_damage_ring *ring, + struct wlr_buffer *buffer, pixman_region32_t *damage) { + pixman_region32_copy(damage, &ring->current); + + struct wlr_damage_ring_entry *entry; + wl_list_for_each(entry, &ring->previous, link) { + if (entry->buffer != buffer) { + pixman_region32_union(damage, damage, &entry->damage); + continue; + } + + // 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); + } + + // rotate + entry_squash_damage(entry); + pixman_region32_copy(&entry->damage, &ring->current); + pixman_region32_clear(&ring->current); + + wl_list_remove(&entry->link); + wl_list_insert(&ring->previous, &entry->link); + return; + } + + pixman_region32_clear(damage); + pixman_region32_union_rect(damage, damage, + 0, 0, ring->width, ring->height); + + entry = calloc(1, sizeof(*entry)); + if (!entry) { + return; + } + + pixman_region32_init(&entry->damage); + pixman_region32_copy(&entry->damage, &ring->current); + pixman_region32_clear(&ring->current); + + wl_list_insert(&ring->previous, &entry->link); + entry->buffer = buffer; + entry->ring = ring; + + entry->buffer_destroy.notify = handle_buffer_destroy; + wl_signal_add(&buffer->events.destroy, &entry->buffer_destroy); +}