diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index d503a502..309d7ace 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -13,12 +13,22 @@ #include #include #include +#include /* For triple buffering, a history of two frames is required. */ #define WLR_DAMAGE_RING_PREVIOUS_LEN 2 +/* Keep track of as many buffers as a swapchain can hold */ +#define WLR_DAMAGE_RING_BUFFERS_LEN 4 struct wlr_box; +struct wlr_damage_ring_buffer { + struct wlr_buffer *buffer; + struct wl_listener destroy; + pixman_region32_t damage; + uint64_t seq; +}; + struct wlr_damage_ring { int32_t width, height; @@ -29,6 +39,9 @@ struct wlr_damage_ring { pixman_region32_t previous[WLR_DAMAGE_RING_PREVIOUS_LEN]; size_t previous_idx; + + uint64_t last_buffer_seq; + struct wlr_damage_ring_buffer buffers[WLR_DAMAGE_RING_BUFFERS_LEN]; }; void wlr_damage_ring_init(struct wlr_damage_ring *ring); @@ -81,4 +94,17 @@ 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 accumulated buffer damage and rotate the damage ring. + * + * The accumulated buffer damage is the difference between the to-be-painted + * buffer and the passed-in buffer. In other words, this is the region that + * needs to be redrawn. + * + * Users should damage the ring if an error occurs while rendering or + * submitting the new buffer to the backend. + */ +void wlr_damage_ring_rotate_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 439cceb4..ff6de4f4 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,12 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring) { for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { pixman_region32_init(&ring->previous[i]); } + + for (size_t i = 0; i < WLR_DAMAGE_RING_BUFFERS_LEN; ++i) { + struct wlr_damage_ring_buffer *ring_buffer = &ring->buffers[i]; + wl_list_init(&ring_buffer->destroy.link); + pixman_region32_init(&ring_buffer->damage); + } } void wlr_damage_ring_finish(struct wlr_damage_ring *ring) { @@ -24,6 +31,11 @@ void wlr_damage_ring_finish(struct wlr_damage_ring *ring) { for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { pixman_region32_fini(&ring->previous[i]); } + for (size_t i = 0; i < WLR_DAMAGE_RING_BUFFERS_LEN; ++i) { + struct wlr_damage_ring_buffer *ring_buffer = &ring->buffers[i]; + wl_list_remove(&ring_buffer->destroy.link); + pixman_region32_fini(&ring_buffer->damage); + } } void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring, @@ -114,3 +126,68 @@ void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, } } } + +static void damage_ring_buffer_reset_buffer(struct wlr_damage_ring_buffer *ring_buffer) { + ring_buffer->buffer = NULL; + wl_list_remove(&ring_buffer->destroy.link); + wl_list_init(&ring_buffer->destroy.link); +} + +static void damage_ring_handle_buffer_destroy(struct wl_listener *listener, void *data) { + struct wlr_damage_ring_buffer *ring_buffer = + wl_container_of(listener, ring_buffer, destroy); + damage_ring_buffer_reset_buffer(ring_buffer); +} + +void wlr_damage_ring_rotate_buffer(struct wlr_damage_ring *ring, + struct wlr_buffer *buffer, pixman_region32_t *damage) { + bool found = false; + struct wlr_damage_ring_buffer *ring_buffer, *oldest = NULL; + for (size_t i = 0; i < WLR_DAMAGE_RING_BUFFERS_LEN; i++) { + ring_buffer = &ring->buffers[i]; + if (ring_buffer->buffer == buffer) { + found = true; + break; + } + if (oldest == NULL || ring_buffer->seq < oldest->seq) { + oldest = ring_buffer; + } + } + + if (!found) { + damage_ring_buffer_reset_buffer(oldest); + ring_buffer = oldest; + + ring_buffer->buffer = buffer; + + ring_buffer->destroy.notify = damage_ring_handle_buffer_destroy; + wl_signal_add(&buffer->events.destroy, &ring_buffer->destroy); + + 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 (size_t i = 0; i < WLR_DAMAGE_RING_BUFFERS_LEN; i++) { + struct wlr_damage_ring_buffer *rb = &ring->buffers[i]; + if (rb->seq < ring_buffer->seq) { + pixman_region32_union(damage, damage, &rb->damage); + } + } + + // 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); + } + } + + ring_buffer->seq = ++ring->last_buffer_seq; + pixman_region32_copy(&ring_buffer->damage, &ring->current); + pixman_region32_clear(&ring->current); +}