diff --git a/include/wlr/render/swapchain.h b/include/wlr/render/swapchain.h index 4ca1ac590..e7607eeec 100644 --- a/include/wlr/render/swapchain.h +++ b/include/wlr/render/swapchain.h @@ -1,12 +1,15 @@ #ifndef WLR_RENDER_SWAPCHAIN_H #define WLR_RENDER_SWAPCHAIN_H +#include #include #include #include #define WLR_SWAPCHAIN_CAP 4 +struct wlr_damage_ring; + struct wlr_swapchain_slot { struct wlr_buffer *buffer; bool acquired; // waiting for release @@ -33,13 +36,31 @@ struct wlr_swapchain *wlr_swapchain_create( struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format); void wlr_swapchain_destroy(struct wlr_swapchain *swapchain); + /** * Acquire a buffer from the swap chain. * + * Can return NULL if the swapchain failed to acquire a buffer either because + * the swapchain is full, or the buffer couldn't be allocated. + * * The returned buffer is locked. When the caller is done with it, they must * unlock it by calling wlr_buffer_unlock. */ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain); + +/** + * Acquire a buffer from the swap chain that is optimal for the damage ring. + * Will automatically rotate the swapchain. + * + * Can return NULL if the swapchain failed to acquire a buffer either because + * the swapchain is full, or the buffer couldn't be allocated. + * + * The returned buffer is locked. When the caller is done with it, they must + * unlock it by calling wlr_buffer_unlock. + */ +struct wlr_buffer *wlr_swapchain_acquire_from_damage_ring(struct wlr_swapchain *swapchain, + struct wlr_damage_ring *ring, pixman_region32_t *damage); + /** * Returns true if this buffer has been created by this swapchain, and false * otherwise. diff --git a/render/swapchain.c b/render/swapchain.c index 233d85eb0..1d9be4834 100644 --- a/render/swapchain.c +++ b/render/swapchain.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "render/allocator/allocator.h" #include "render/drm_format_set.h" @@ -108,6 +109,40 @@ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain) { return slot_acquire(swapchain, free_slot); } +struct wlr_buffer *wlr_swapchain_acquire_from_damage_ring(struct wlr_swapchain *swapchain, + struct wlr_damage_ring *ring, pixman_region32_t *damage) { + struct wlr_buffer *buffer = NULL; + + struct wlr_damage_ring_buffer *entry; + wl_list_for_each(entry, &ring->buffers, link) { + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->acquired) { + continue; + } + + if (slot->buffer == entry->buffer) { + buffer = slot_acquire(swapchain, slot); + break; + } + } + + if (buffer) { + break; + } + } + + if (!buffer) { + buffer = wlr_swapchain_acquire(swapchain); + if (!buffer) { + return NULL; + } + } + + wlr_damage_ring_rotate_buffer(ring, buffer, damage); + return buffer; +} + bool wlr_swapchain_has_buffer(struct wlr_swapchain *swapchain, struct wlr_buffer *buffer) { for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 766189485..f17182656 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2103,7 +2103,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, swapchain = output->swapchain; } - struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain); + pixman_region32_init(&render_data.damage); + struct wlr_buffer *buffer = wlr_swapchain_acquire_from_damage_ring(swapchain, + &scene_output->damage_ring, &render_data.damage); if (buffer == NULL) { return false; } @@ -2128,16 +2130,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, .signal_point = scene_output->in_point, }); if (render_pass == NULL) { - wlr_buffer_unlock(buffer); - return false; + goto fail; } render_data.render_pass = render_pass; - pixman_region32_init(&render_data.damage); - wlr_damage_ring_rotate_buffer(&scene_output->damage_ring, buffer, - &render_data.damage); - pixman_region32_t background; pixman_region32_init(&background); pixman_region32_copy(&background, &render_data.damage); @@ -2219,12 +2216,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)) { - 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; + goto fail; } wlr_output_state_set_buffer(state, buffer); @@ -2238,6 +2230,14 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, scene_output_state_attempt_gamma(scene_output, state); return true; + + fail: + 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; } int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) {