From b8196df34d1948f90e23b7401f77d8d3f0f29b8c Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 24 Aug 2023 18:03:19 -0400 Subject: [PATCH 01/11] wlr_output: Add applied state to commit event --- include/wlr/types/wlr_output.h | 1 + types/output/output.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 30331fbaa..9a9202ee1 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -221,6 +221,7 @@ struct wlr_output_event_commit { uint32_t committed; // bitmask of enum wlr_output_state_field struct timespec *when; struct wlr_buffer *buffer; // NULL if no buffer is committed + const struct wlr_output_state *state; }; enum wlr_output_present_flag { diff --git a/types/output/output.c b/types/output/output.c index b43018027..efc984c58 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -833,6 +833,7 @@ bool wlr_output_commit_state(struct wlr_output *output, .committed = pending.committed, .when = &now, .buffer = (pending.committed & WLR_OUTPUT_STATE_BUFFER) ? pending.buffer : NULL, + .state = &pending, }; wl_signal_emit_mutable(&output->events.commit, &event); From 772b29256011c6377bb64cbf0768fba45f05807f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 24 Aug 2023 18:12:27 -0400 Subject: [PATCH 02/11] output_event_commit: Remove committed and buffer The newly introduced state struct can be used to retrieve this. --- include/wlr/types/wlr_output.h | 2 -- types/output/output.c | 2 -- types/scene/wlr_scene.c | 3 ++- types/wlr_cursor.c | 4 ++-- types/wlr_export_dmabuf_v1.c | 4 ++-- types/wlr_output_layout.c | 3 ++- types/wlr_output_power_management_v1.c | 2 +- types/wlr_screencopy_v1.c | 7 +++---- 8 files changed, 12 insertions(+), 15 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 9a9202ee1..faa05d740 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -218,9 +218,7 @@ struct wlr_output_event_precommit { struct wlr_output_event_commit { struct wlr_output *output; - uint32_t committed; // bitmask of enum wlr_output_state_field struct timespec *when; - struct wlr_buffer *buffer; // NULL if no buffer is committed const struct wlr_output_state *state; }; diff --git a/types/output/output.c b/types/output/output.c index efc984c58..267006763 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -830,9 +830,7 @@ bool wlr_output_commit_state(struct wlr_output *output, struct wlr_output_event_commit event = { .output = output, - .committed = pending.committed, .when = &now, - .buffer = (pending.committed & WLR_OUTPUT_STATE_BUFFER) ? pending.buffer : NULL, .state = &pending, }; wl_signal_emit_mutable(&output->events.commit, &event); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 03d80eab4..097d47c39 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1266,8 +1266,9 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data) struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_commit); struct wlr_output_event_commit *event = data; + const struct wlr_output_state *state = event->state; - if (event->committed & (WLR_OUTPUT_STATE_MODE | + if (state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_ENABLED)) { diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index a228bc9fb..032eb8c81 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -567,13 +567,13 @@ static void output_cursor_output_handle_output_commit( wl_container_of(listener, output_cursor, output_commit); const struct wlr_output_event_commit *event = data; - if (event->committed & WLR_OUTPUT_STATE_SCALE) { + if (event->state->committed & WLR_OUTPUT_STATE_SCALE) { cursor_output_cursor_update(output_cursor); } struct wlr_surface *surface = output_cursor->cursor->state->surface; if (surface && output_cursor->output_cursor->visible && - (event->committed & WLR_OUTPUT_STATE_BUFFER)) { + (event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { wlr_surface_send_frame_done(surface, event->when); } } diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index d31f40e60..c3411ec88 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -57,7 +57,7 @@ static void frame_output_handle_commit(struct wl_listener *listener, wl_container_of(listener, frame, output_commit); struct wlr_output_event_commit *event = data; - if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { + if (!(event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } @@ -65,7 +65,7 @@ static void frame_output_handle_commit(struct wl_listener *listener, wl_list_init(&frame->output_commit.link); struct wlr_dmabuf_attributes attribs = {0}; - if (!wlr_buffer_get_dmabuf(event->buffer, &attribs)) { + if (!wlr_buffer_get_dmabuf(event->state->buffer, &attribs)) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY); frame_destroy(frame); diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c index 22a79c893..70ff78762 100644 --- a/types/wlr_output_layout.c +++ b/types/wlr_output_layout.c @@ -119,7 +119,8 @@ static void handle_output_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, l_output, commit); struct wlr_output_event_commit *event = data; - if (event->committed & (WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM | + if (event->state->committed & (WLR_OUTPUT_STATE_SCALE | + WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_MODE)) { output_layout_reconfigure(l_output->layout); output_update_global(l_output->output); diff --git a/types/wlr_output_power_management_v1.c b/types/wlr_output_power_management_v1.c index 7e4d1b3f7..1b2da49a4 100644 --- a/types/wlr_output_power_management_v1.c +++ b/types/wlr_output_power_management_v1.c @@ -62,7 +62,7 @@ static void output_power_handle_output_commit(struct wl_listener *listener, struct wlr_output_power_v1 *output_power = wl_container_of(listener, output_power, output_commit_listener); struct wlr_output_event_commit *event = data; - if (event->committed & WLR_OUTPUT_STATE_ENABLED) { + if (event->state->committed & WLR_OUTPUT_STATE_ENABLED) { output_power_v1_send_mode(output_power); } } diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index a421954b1..bb06ce8e2 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -273,10 +273,9 @@ static void frame_handle_output_commit(struct wl_listener *listener, struct wlr_output_event_commit *event = data; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = output->renderer; - struct wlr_buffer *buffer = event->buffer; assert(renderer); - if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { + if (!(event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } @@ -298,10 +297,10 @@ static void frame_handle_output_commit(struct wl_listener *listener, bool ok; switch (frame->buffer_cap) { case WLR_BUFFER_CAP_DMABUF: - ok = frame_dma_copy(frame, buffer); + ok = frame_dma_copy(frame, event->state->buffer); break; case WLR_BUFFER_CAP_DATA_PTR: - ok = frame_shm_copy(frame, buffer); + ok = frame_shm_copy(frame, event->state->buffer); break; default: abort(); // unreachable From a6d3eee63ad6073ec7d859f57ddef7059a822d3c Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 12 Jul 2023 22:10:45 -0400 Subject: [PATCH 03/11] damage_ring: Wrap previous damage in struct We will add buffers here to track those --- include/wlr/types/wlr_damage_ring.h | 6 +++++- types/wlr_damage_ring.c | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index d503a502a..3f1ad70ed 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -19,6 +19,10 @@ struct wlr_box; +struct wlr_damage_ring_entry { + pixman_region32_t damage; +}; + struct wlr_damage_ring { int32_t width, height; @@ -27,7 +31,7 @@ struct wlr_damage_ring { // private state - pixman_region32_t previous[WLR_DAMAGE_RING_PREVIOUS_LEN]; + struct wlr_damage_ring_entry previous[WLR_DAMAGE_RING_PREVIOUS_LEN]; size_t previous_idx; }; diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index 439cceb4b..361f4802f 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -15,14 +15,14 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring) { pixman_region32_init(&ring->current); for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { - pixman_region32_init(&ring->previous[i]); + pixman_region32_init(&ring->previous[i].damage); } } 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]); + pixman_region32_fini(&ring->previous[i].damage); } } @@ -84,7 +84,7 @@ void wlr_damage_ring_rotate(struct wlr_damage_ring *ring) { 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_copy(&ring->previous[ring->previous_idx].damage, &ring->current); pixman_region32_clear(&ring->current); } @@ -100,7 +100,7 @@ void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, // 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]); + pixman_region32_union(damage, damage, &ring->previous[j].damage); } // Check the number of rectangles From 600d995e94dc3892b727d787e722eadb4fcbad5e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 12 Jul 2023 23:08:34 -0400 Subject: [PATCH 04/11] wlr_damage_ring: Rework to use list instead of fixed length ring When we add buffer support this model of keeping a fixed size list simply won't be compatible. --- include/wlr/types/wlr_damage_ring.h | 9 ++-- types/wlr_damage_ring.c | 72 ++++++++++++++++++----------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index 3f1ad70ed..457448c20 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -13,14 +13,14 @@ #include #include #include - -/* For triple buffering, a history of two frames is required. */ -#define WLR_DAMAGE_RING_PREVIOUS_LEN 2 +#include struct wlr_box; struct wlr_damage_ring_entry { pixman_region32_t damage; + + struct wl_list link; // struct wlr_damage_ring.previous }; struct wlr_damage_ring { @@ -31,8 +31,7 @@ struct wlr_damage_ring { // private state - struct wlr_damage_ring_entry previous[WLR_DAMAGE_RING_PREVIOUS_LEN]; - size_t previous_idx; + struct wl_list previous; // struct wlr_damage_ring_entry.link }; void wlr_damage_ring_init(struct wlr_damage_ring *ring); diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index 361f4802f..0862d91fa 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -14,15 +14,21 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring) { }; pixman_region32_init(&ring->current); - for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { - pixman_region32_init(&ring->previous[i].damage); - } + wl_list_init(&ring->previous); +} + +static void entry_destroy(struct wlr_damage_ring_entry *entry) { + wl_list_remove(&entry->link); + pixman_region32_fini(&entry->damage); + free(entry); } 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].damage); + + struct wlr_damage_ring_entry *entry, *tmp_entry; + wl_list_for_each_safe(entry, tmp_entry, &ring->previous, link) { + entry_destroy(entry); } } @@ -79,38 +85,50 @@ void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring) { } 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; + struct wlr_damage_ring_entry *last = + wl_container_of(ring->previous.prev, last, link); + wl_list_remove(&last->link); + wl_list_insert(&ring->previous, &last->link); - pixman_region32_copy(&ring->previous[ring->previous_idx].damage, &ring->current); + pixman_region32_copy(&last->damage, &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_copy(damage, &ring->current); + + // Accumulate damage from old buffers + struct wlr_damage_ring_entry *entry; + wl_list_for_each(entry, &ring->previous, link) { + if (--buffer_age <= 0) { + break; + } + + pixman_region32_union(damage, damage, &entry->damage); + } + + // if our buffer age is older than anything we are keeping track of, increase + // the size + if (buffer_age > 0) { 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].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); + struct wlr_damage_ring_entry *entry = calloc(1, sizeof(*entry)); + if (entry) { + pixman_region32_init(&entry->damage); + wl_list_insert(ring->previous.prev, &entry->link); } } + + // 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); + } } From a6676e81edbcb7173b9a06924232e5c38c36705e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 14 Jul 2023 03:03:38 -0400 Subject: [PATCH 05/11] wlr_damage_ring: Introduce wlr_damage_ring_damage_for_buffer This cannot be used along with `wlr_damage_ring_get_buffer_damage` in the same damage ring. --- include/wlr/types/wlr_damage_ring.h | 15 ++++++ types/wlr_damage_ring.c | 78 +++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) 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); +} From 9781ba73e564e1fa8fca315c0ff2a1717e9cc977 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 14 Sep 2023 16:32:04 -0400 Subject: [PATCH 06/11] wlr_scene: Track output damage separately The damage ring is only supposed to keep track of damage within the swapchain --- include/wlr/types/wlr_scene.h | 2 ++ types/scene/wlr_scene.c | 20 ++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 3af839aba..17acf3388 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -204,6 +204,8 @@ struct wlr_scene_output { // private state + pixman_region32_t pending_commit_damage; + uint8_t index; bool prev_scanout; diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 097d47c39..75ea6bccd 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1274,6 +1274,14 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data) WLR_OUTPUT_STATE_ENABLED)) { scene_output_update_geometry(scene_output); } + + // if the output has been committed with a certain damage, we know that region + // will be acknowledged by the backend so we don't need to keep track of it + // anymore + if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { + pixman_region32_subtract(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &state->damage); + } } static void scene_output_handle_damage(struct wl_listener *listener, void *data) { @@ -1303,6 +1311,7 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl); wlr_damage_ring_init(&scene_output->damage_ring); + pixman_region32_init(&scene_output->pending_commit_damage); wl_list_init(&scene_output->damage_highlight_regions); int prev_output_index = -1; @@ -1361,6 +1370,7 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wlr_addon_finish(&scene_output->addon); wlr_damage_ring_finish(&scene_output->damage_ring); + pixman_region32_fini(&scene_output->pending_commit_damage); wl_list_remove(&scene_output->link); wl_list_remove(&scene_output->output_commit.link); wl_list_remove(&scene_output->output_damage.link); @@ -1464,9 +1474,15 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, static void output_state_apply_damage(const struct render_data *data, struct wlr_output_state *state) { + struct wlr_scene_output *output = data->output; + pixman_region32_union(&output->pending_commit_damage, + &output->pending_commit_damage, &output->damage_ring.current); + pixman_region32_intersect_rect(&output->pending_commit_damage, + &output->pending_commit_damage, 0, 0, data->trans_width, data->trans_height); + pixman_region32_t frame_damage; pixman_region32_init(&frame_damage); - pixman_region32_copy(&frame_damage, &data->output->damage_ring.current); + pixman_region32_copy(&frame_damage, &output->pending_commit_damage); transform_output_damage(&frame_damage, data); wlr_output_state_set_damage(state, &frame_damage); pixman_region32_fini(&frame_damage); @@ -1599,7 +1615,7 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, const struct wlr_scene_output_state_options *options) { if (!scene_output->output->needs_frame && !pixman_region32_not_empty( - &scene_output->damage_ring.current)) { + &scene_output->pending_commit_damage)) { return true; } From e66c02751b47b84ba247dae33d36bf45cc8ae2bd Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 14 Sep 2023 16:30:37 -0400 Subject: [PATCH 07/11] wlr_scene: Use wlr_damage_ring_damage_for_buffer() Compositors who use _build_state are no longer required to call damage_ring_rotate themselves. This is a minor breaking change. --- types/scene/wlr_scene.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 75ea6bccd..53bb890d6 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1631,8 +1631,6 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, goto out; } - wlr_damage_ring_rotate(&scene_output->damage_ring); - out: wlr_output_state_finish(&state); return ok; @@ -1783,8 +1781,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, return false; } - int buffer_age; - struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, &buffer_age); + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, NULL); if (buffer == NULL) { return false; } @@ -1808,9 +1805,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } render_data.render_pass = render_pass; + + output_state_apply_damage(&render_data, state); pixman_region32_init(&render_data.damage); - wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, - buffer_age, &render_data.damage); + wlr_damage_ring_damage_for_buffer(&scene_output->damage_ring, buffer, + &render_data.damage); pixman_region32_t background; pixman_region32_init(&background); @@ -1896,12 +1895,15 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, if (!wlr_render_pass_submit(render_pass)) { wlr_buffer_unlock(buffer); + + // if we failed to render the buffer, it will have undefined contents + // Trash the damage ring + wlr_damage_ring_add_whole(&scene_output->damage_ring); return false; } wlr_output_state_set_buffer(state, buffer); wlr_buffer_unlock(buffer); - output_state_apply_damage(&render_data, state); if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && !wl_list_empty(&scene_output->damage_highlight_regions)) { From 35845a94a5980a352454d2a50ea92666d0c87aa5 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Wed, 23 Aug 2023 11:39:14 -0400 Subject: [PATCH 08/11] wlr_scene: Add all nodes to render_list In preparation for implementing wlr_output_layers. No functional changes are intended. Instead of filtering entries by visibility, add all entries to the render_list with an additional visibility flag. Consumers of this list will consult this flag to keep functionally behavior. This work is based on !4015, re-spun due to large changes since then. --- types/scene/wlr_scene.c | 117 ++++++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 34 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 53bb890d6..042afea6c 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1107,6 +1107,7 @@ struct render_list_entry { struct wlr_scene_node *node; bool sent_dmabuf_feedback; int x, y; + bool visible; }; static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) { @@ -1426,50 +1427,71 @@ struct render_list_constructor_data { bool calculate_visibility; }; -static bool construct_render_list_iterator(struct wlr_scene_node *node, - int lx, int ly, void *_data) { - struct render_list_constructor_data *data = _data; +static bool scene_construct_render_list(struct wlr_scene_node *node, + struct wl_array *render_list, struct wlr_box *box, bool calculate_visibility, + bool enabled, int lx, int ly) { - if (scene_node_invisible(node)) { + enabled = enabled && node->enabled; + + if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each_reverse(child, &scene_tree->children, link) { + if (!scene_construct_render_list(child, render_list, box, calculate_visibility, enabled, + lx + child->x, ly + child->y)) { + return false; + } + } + + return true; + } + + struct render_list_entry *entry = wl_array_add(render_list, sizeof(*entry)); + if (!entry) { return false; } + memset(entry, 0, sizeof(*entry)); + entry->node = node; + entry->x = lx; + entry->y = ly; + + if (!enabled) { + return true; + } + + struct wlr_box node_box = { .x = lx, .y = ly }; + scene_node_get_size(node, &node_box.width, &node_box.height); + + if (!wlr_box_intersection(&node_box, &node_box, box) || scene_node_invisible(node)) { + return true; + } + // while rendering, the background should always be black. // If we see a black rect, we can ignore rendering everything under the rect // and even the rect itself. - if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) { + if (node->type == WLR_SCENE_NODE_RECT && calculate_visibility) { struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { - return false; + return true; } } pixman_region32_t intersection; pixman_region32_init(&intersection); pixman_region32_intersect_rect(&intersection, &node->visible, - data->box.x, data->box.y, - data->box.width, data->box.height); + box->x, box->y, box->width, box->height); if (!pixman_region32_not_empty(&intersection)) { pixman_region32_fini(&intersection); - return false; + return true; } pixman_region32_fini(&intersection); - struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); - if (!entry) { - return false; - } - - *entry = (struct render_list_entry){ - .node = node, - .x = lx, - .y = ly, - }; - - return false; + entry->visible = true; + return true; } static void output_state_apply_damage(const struct render_data *data, @@ -1694,22 +1716,40 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, render_data.logical.width = render_data.trans_width / render_data.scale; render_data.logical.height = render_data.trans_height / render_data.scale; - struct render_list_constructor_data list_con = { - .box = render_data.logical, - .render_list = &scene_output->render_list, - .calculate_visibility = scene_output->scene->calculate_visibility, - }; + bool calculate_visibility = scene_output->scene->calculate_visibility; + struct wlr_scene_node *tree_node = &scene_output->scene->tree.node; + struct wl_array *render_list = &scene_output->render_list; + int x, y; - list_con.render_list->size = 0; - scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, - construct_render_list_iterator, &list_con); - array_realloc(list_con.render_list, list_con.render_list->size); + render_list->size = 0; + wlr_scene_node_coords(tree_node, &x, &y); - struct render_list_entry *list_data = list_con.render_list->data; - int list_len = list_con.render_list->size / sizeof(*list_data); + // There is at least one render list entry for the scene tree itself, therefore + // construct_render_list should always return true. + if (!scene_construct_render_list(tree_node, render_list, &render_data.logical, + calculate_visibility, true, x, y)) { + return false; + } + array_realloc(render_list, render_list->size); - bool scanout = list_len == 1 && - scene_entry_try_direct_scanout(&list_data[0], state, &render_data); + // Find number of visible render entries. If only one, try direct scanout. + struct render_list_entry *list_data = render_list->data; + struct render_list_entry *scanout_entry = NULL; + int list_len = render_list->size / sizeof(*list_data); + int render_count = 0; + + for (int i = list_len - 1; i >= 0; i--) { + struct render_list_entry *entry = &list_data[i]; + if (!entry->visible) { + continue; + } + + scanout_entry = entry; + render_count++; + } + + bool scanout = render_count == 1 && + scene_entry_try_direct_scanout(scanout_entry, state, &render_data); if (scene_output->prev_scanout != scanout) { scene_output->prev_scanout = scanout; @@ -1822,6 +1862,10 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, for (int i = list_len - 1; i >= 0; i--) { struct render_list_entry *entry = &list_data[i]; + if (!entry->visible) { + continue; + } + // We must only cull opaque regions that are visible by the node. // The node's visibility will have the knowledge of a black rect // that may have been omitted from the render list via the black @@ -1857,6 +1901,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, for (int i = list_len - 1; i >= 0; i--) { struct render_list_entry *entry = &list_data[i]; + + if (!entry->visible) { + continue; + } + scene_entry_render(entry, &render_data); if (entry->node->type == WLR_SCENE_NODE_BUFFER) { From 3c71f61c0b6dc191ffbf049c7bb6ec86a8c09023 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 10 Mar 2023 13:14:51 +0100 Subject: [PATCH 09/11] wlr_scene: Maintain one render_list per scene Since we're now adding every node into the tree into the render_list, the length won't change depending on the output. --- include/wlr/types/wlr_scene.h | 4 ++-- types/scene/wlr_scene.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 17acf3388..812804c66 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -110,6 +110,8 @@ struct wlr_scene { enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; bool calculate_visibility; + + struct wl_array render_list; }; /** A scene-graph node displaying a single surface. */ @@ -214,8 +216,6 @@ struct wlr_scene_output { struct wl_listener output_needs_frame; struct wl_list damage_highlight_regions; - - struct wl_array render_list; }; struct wlr_scene_timer { diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 042afea6c..5dc2d702e 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -125,6 +125,8 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { wl_list_remove(&scene->presentation_destroy.link); wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); + + wl_array_release(&scene->render_list); } else { assert(node->parent); } @@ -1377,7 +1379,6 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_needs_frame.link); - wl_array_release(&scene_output->render_list); free(scene_output); } @@ -1718,7 +1719,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, bool calculate_visibility = scene_output->scene->calculate_visibility; struct wlr_scene_node *tree_node = &scene_output->scene->tree.node; - struct wl_array *render_list = &scene_output->render_list; + struct wl_array *render_list = &scene_output->scene->render_list; int x, y; render_list->size = 0; From 55bc5a0d99c1118c6842d42e958c000ed696706c Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 25 Aug 2022 21:55:46 -0400 Subject: [PATCH 10/11] util/array: Add reverse helpers --- include/util/array.h | 11 +++++++++++ util/array.c | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/util/array.h b/include/util/array.h index a51bdb640..862921680 100644 --- a/include/util/array.h +++ b/include/util/array.h @@ -15,4 +15,15 @@ void array_remove_at(struct wl_array *arr, size_t offset, size_t size); */ bool array_realloc(struct wl_array *arr, size_t size); +/** + * Returns a pointer to the first valid element in a reversed array. + */ +void *array_reversed_start(struct wl_array *arr); + +/** + * Adds a new element to the array inserting them starting from a higher + * memory address effectively inserting them in reverse order. + */ +void *array_reversed_add(struct wl_array *arr, size_t size); + #endif diff --git a/util/array.c b/util/array.c index ec16a7b13..427cf725a 100644 --- a/util/array.c +++ b/util/array.c @@ -38,3 +38,26 @@ bool array_realloc(struct wl_array *arr, size_t size) { arr->alloc = alloc; return true; } + +void *array_reversed_start(struct wl_array *arr) { + char *data = arr->data; + return data + arr->alloc - arr->size; +} + +void *array_reversed_add(struct wl_array *arr, size_t size) { + if (arr->size + size > arr->alloc) { + size_t new_alloc = arr->alloc * 2; + char *new = malloc(new_alloc); + if (!new) { + return NULL; + } + + memcpy(new + (new_alloc - arr->size), array_reversed_start(arr), arr->size); + free(arr->data); + arr->data = new; + arr->alloc = new_alloc; + } + + arr->size += size; + return array_reversed_start(arr); +} From 494a87c954a4810915f95ced7d411855c2f13366 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 4 Apr 2023 20:45:32 +0200 Subject: [PATCH 11/11] wlr_scene: Implement output layers v2: wlr_scene builds output state in a local state variable, and not in output->pending. Use the local state instead. --- include/wlr/types/wlr_scene.h | 2 + types/scene/wlr_scene.c | 295 ++++++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 812804c66..ddc249136 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -216,6 +216,8 @@ struct wlr_scene_output { struct wl_listener output_needs_frame; struct wl_list damage_highlight_regions; + + struct wl_array output_layers; }; struct wlr_scene_timer { diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 5dc2d702e..fc0d2990d 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1107,6 +1108,7 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, struct render_list_entry { struct wlr_scene_node *node; + struct wlr_output_layer_state *state; bool sent_dmabuf_feedback; int x, y; bool visible; @@ -1379,6 +1381,8 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_needs_frame.link); + wl_array_release(&scene_output->output_layers); + free(scene_output); } @@ -1635,6 +1639,259 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, return true; } +static bool scene_buffer_can_be_output_layer(struct wlr_scene_buffer *buffer, + struct wlr_scene_output *scene_output) { + struct wlr_fbox default_box = {0}; + if (buffer->transform & WL_OUTPUT_TRANSFORM_90) { + default_box.width = buffer->buffer->height; + default_box.height = buffer->buffer->width; + } else { + default_box.width = buffer->buffer->width; + default_box.height = buffer->buffer->height; + } + + if (!wlr_fbox_empty(&buffer->src_box) && + !wlr_fbox_equal(&buffer->src_box, &default_box)) { + return false; + } + + if (buffer->transform != scene_output->output->transform) { + return false; + } + + return true; +} + +struct wlr_scene_node_output_layer { + struct wlr_addon addon; + struct wlr_output_layer *layer; + struct wlr_scene_buffer *buffer; + struct wlr_scene_output *output; + struct render_list_entry *current_render_entry; + + struct wl_listener layer_feedback; + struct wl_listener output_destroy; +}; + +static void node_output_layer_destroy(struct wlr_scene_node_output_layer *layer) { + wlr_output_layer_destroy(layer->layer); + wlr_addon_finish(&layer->addon); + wl_list_remove(&layer->output_destroy.link); + free(layer); +} + +static void handle_output_layer_addon_finish(struct wlr_addon *addon) { + struct wlr_scene_node_output_layer *layer = wl_container_of(addon, layer, addon); + node_output_layer_destroy(layer); +} + +static void handle_output_layer_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_node_output_layer *layer = wl_container_of(listener, layer, output_destroy); + wl_list_remove(&layer->layer_feedback.link); + node_output_layer_destroy(layer); +} + +static const struct wlr_addon_interface node_addon_output_layer = { + .name = "wlr_scene_output_layer", + .destroy = handle_output_layer_addon_finish, +}; + +static struct wlr_scene_node_output_layer *scene_node_get_output_layer( + struct wlr_scene_node *node, struct wlr_scene_output *output) { + struct wlr_addon *addon = wlr_addon_find( + &node->addons, output, &node_addon_output_layer); + if (!addon) { + return NULL; + } + + struct wlr_scene_node_output_layer *layer = wl_container_of(addon, layer, addon); + return layer; +} + +static void output_handle_layer_feedback(struct wl_listener *listener, void *data) { + struct wlr_scene_node_output_layer *layer = + wl_container_of(listener, layer, layer_feedback); + struct wlr_output_layer_feedback_event *event = data; + struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = layer->buffer->primary_output->output->renderer, + .output_layer_feedback_event = event, + }; + + if (layer->buffer->primary_output == layer->output) { + scene_buffer_send_dmabuf_feedback(scene_node_get_root(&layer->buffer->node), + layer->buffer, &options); + layer->current_render_entry->sent_dmabuf_feedback = true; + } +} + +static struct wlr_scene_node_output_layer *scene_buffer_create_output_layer( + struct wlr_scene_buffer *buffer, struct wlr_scene_output *output) { + struct wlr_scene_node_output_layer *layer = calloc(1, sizeof(*layer)); + if (!layer) { + return NULL; + } + + layer->layer = wlr_output_layer_create(output->output); + if (!layer->layer) { + free(layer); + return NULL; + } + + layer->layer_feedback.notify = output_handle_layer_feedback; + wl_signal_add(&layer->layer->events.feedback, &layer->layer_feedback); + + layer->output_destroy.notify = handle_output_layer_output_destroy; + wl_signal_add(&output->output->events.destroy, &layer->output_destroy); + + layer->buffer = buffer; + layer->output = output; + + wlr_addon_init(&layer->addon, &buffer->node.addons, output, &node_addon_output_layer); + return layer; +} + +static bool scene_submit_output_layers(struct wlr_scene_output *scene_output, + struct wlr_output_state *state, struct wl_array *render_list) { + // if we render software cursors, that means they will be a part of the + // primary plane. If we render output layers, it means we could render + // over the cursor. A lock will also be registered if there is screen share. + // Screen share cannot use output layers. + if (scene_output->output->software_cursor_locks) { + return false; + } + + if (scene_output->scene->debug_damage_option == + WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { + return false; + } + + struct render_list_entry *list_data = render_list->data; + int list_len = render_list->size / sizeof(*list_data); + + scene_output->output_layers.size = 0; + if (!array_realloc(&scene_output->output_layers, + list_len * sizeof(struct wlr_output_layer_state))) { + return false; + } + + // area of that the base layer must render (such as rects) + pixman_region32_t base_layer_area; + pixman_region32_init(&base_layer_area); + + for (int i = 0; i < list_len; i++) { + struct render_list_entry *entry = &list_data[i]; + struct wlr_scene_node *node = entry->node; + + if (node->type != WLR_SCENE_NODE_BUFFER) { + continue; + } + + struct wlr_box box = { .x = entry->x, .y = entry->y }; + scene_node_get_size(node, &box.width, &box.height); + + box.x = floor(box.x * scene_output->output->scale) - scene_output->x; + box.y = floor(box.y * scene_output->output->scale) - scene_output->y; + box.width = ceil(box.width * scene_output->output->scale); + box.height = ceil(box.height * scene_output->output->scale); + + struct wlr_scene_node_output_layer *layer = + scene_node_get_output_layer(node, scene_output); + + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + + if (entry->visible && !scene_buffer_can_be_output_layer(buffer, scene_output)) { + pixman_region32_union_rect(&base_layer_area, &base_layer_area, + box.x, box.y, box.width, box.height); + + if (layer) { + node_output_layer_destroy(layer); + } + + continue; + } + + if (entry->visible && !layer) { + layer = scene_buffer_create_output_layer(buffer, scene_output); + if (!layer) { + pixman_region32_union_rect(&base_layer_area, &base_layer_area, + box.x, box.y, box.width, box.height); + continue; + } + } + + if (layer) { + // we preallocted the array length, this should never return NULL. + entry->state = array_reversed_add(&scene_output->output_layers, sizeof(*entry->state)); + + layer->current_render_entry = entry; + *entry->state = (struct wlr_output_layer_state){ + .layer = layer->layer, + .buffer = NULL, + .dst_box = box, + }; + } + + if (!entry->visible) { + continue; + } + + pixman_region32_t intersection; + pixman_region32_init(&intersection); + pixman_region32_intersect_rect(&intersection, &base_layer_area, + box.x, box.y, box.width, box.height); + if (pixman_region32_not_empty(&intersection)) { + pixman_region32_union_rect(&base_layer_area, &base_layer_area, + box.x, box.y, box.width, box.height); + pixman_region32_fini(&intersection); + continue; + } + pixman_region32_fini(&intersection); + + // only consider nodes that are in the damage + pixman_region32_init(&intersection); + pixman_region32_intersect_rect(&intersection, &scene_output->damage_ring.current, + box.x, box.y, box.width, box.height); + if (!pixman_region32_not_empty(&intersection)) { + pixman_region32_union_rect(&base_layer_area, &base_layer_area, + box.x, box.y, box.width, box.height); + pixman_region32_fini(&intersection); + continue; + } + + if (!buffer_is_opaque(buffer->buffer)) { + // With transparent nodes, we can't get away with just having the + // node partially in the damage or else the user can see through + // the node to find an older version of the node software composited + // in the base layer. + pixman_region32_t node_box; + pixman_region32_init_rect(&node_box, box.x, box.y, box.width, box.height); + pixman_region32_subtract(&intersection, &node_box, &intersection); + pixman_region32_fini(&node_box); + if (pixman_region32_not_empty(&intersection)) { + pixman_region32_union_rect(&base_layer_area, &base_layer_area, + box.x, box.y, box.width, box.height); + pixman_region32_fini(&intersection); + continue; + } + } + pixman_region32_fini(&intersection); + + entry->state->buffer = buffer->buffer; + } + + pixman_region32_fini(&base_layer_area); + + wlr_output_state_set_layers(state, array_reversed_start(&scene_output->output_layers), + scene_output->output_layers.size / sizeof(struct wlr_output_layer_state)); + + if (!wlr_output_test_state(scene_output->output, state)) { + return false; + } + + return true; +} + bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, const struct wlr_scene_output_state_options *options) { if (!scene_output->output->needs_frame && !pixman_region32_not_empty( @@ -1733,11 +1990,15 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } array_realloc(render_list, render_list->size); + scene_submit_output_layers(scene_output, state, render_list); + // Find number of visible render entries. If only one, try direct scanout. struct render_list_entry *list_data = render_list->data; struct render_list_entry *scanout_entry = NULL; int list_len = render_list->size / sizeof(*list_data); int render_count = 0; + pixman_region32_t forward_damage; + pixman_region32_init(&forward_damage); for (int i = list_len - 1; i >= 0; i--) { struct render_list_entry *entry = &list_data[i]; @@ -1745,10 +2006,30 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, continue; } + if (entry->state && entry->state->accepted && entry->state->buffer) { + // we need to damage the output here for the next frame because since + // the we're using output layers here. The contents won't be part + // of the primary plane and will therefore have stale contents. + pixman_region32_union(&forward_damage, &forward_damage, &entry->node->visible); + + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node); + struct wlr_scene_output_sample_event sample_event = { + .output = scene_output, + .direct_scanout = true, + }; + wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event); + + continue; + } + scanout_entry = entry; render_count++; } + pixman_region32_translate(&forward_damage, -scene_output->x, -scene_output->y); + scale_output_damage(&forward_damage, scene_output->output->scale); + pixman_region32_intersect(&forward_damage, &forward_damage, &scene_output->damage_ring.current); + bool scanout = render_count == 1 && scene_entry_try_direct_scanout(scanout_entry, state, &render_data); @@ -1769,6 +2050,10 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, timespec_sub(&duration, &end_time, &start_time); timer->pre_render_duration = timespec_to_nsec(&duration); } + + wlr_damage_ring_add(&scene_output->damage_ring, &forward_damage); + pixman_region32_fini(&forward_damage); + return true; } @@ -1824,6 +2109,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, NULL); if (buffer == NULL) { + pixman_region32_fini(&forward_damage); return false; } @@ -1842,6 +2128,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, }); if (render_pass == NULL) { wlr_buffer_unlock(buffer); + pixman_region32_fini(&forward_damage); return false; } @@ -1907,6 +2194,10 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, continue; } + if (entry->state && entry->state->accepted && entry->state->buffer) { + continue; + } + scene_entry_render(entry, &render_data); if (entry->node->type == WLR_SCENE_NODE_BUFFER) { @@ -1944,6 +2235,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_fini(&render_data.damage); if (!wlr_render_pass_submit(render_pass)) { + pixman_region32_fini(&forward_damage); wlr_buffer_unlock(buffer); // if we failed to render the buffer, it will have undefined contents @@ -1960,6 +2252,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, wlr_output_schedule_frame(scene_output->output); } + wlr_damage_ring_add(&scene_output->damage_ring, &forward_damage); + pixman_region32_fini(&forward_damage); + return true; }