diff --git a/include/render/swapchain.h b/include/render/swapchain.h index 57a0cd3f..243f0404 100644 --- a/include/render/swapchain.h +++ b/include/render/swapchain.h @@ -10,6 +10,7 @@ struct wlr_swapchain_slot { struct wlr_buffer *buffer; bool acquired; // waiting for release + int age; struct wl_listener release; }; @@ -35,7 +36,15 @@ void wlr_swapchain_destroy(struct wlr_swapchain *swapchain); * 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); +struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, + int *age); +/** + * Mark the buffer as submitted for presentation. This needs to be called by + * swap chain users on frame boundaries. + * + * If the buffer hasn't been created via the swap chain, the call is ignored. + */ +void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer); #endif diff --git a/render/swapchain.c b/render/swapchain.c index 4145efd2..fd9f8725 100644 --- a/render/swapchain.c +++ b/render/swapchain.c @@ -63,7 +63,8 @@ static void slot_handle_release(struct wl_listener *listener, void *data) { slot->acquired = false; } -static struct wlr_buffer *slot_acquire(struct wlr_swapchain_slot *slot) { +static struct wlr_buffer *slot_acquire(struct wlr_swapchain *swapchain, + struct wlr_swapchain_slot *slot, int *age) { assert(!slot->acquired); assert(slot->buffer != NULL); @@ -72,11 +73,15 @@ static struct wlr_buffer *slot_acquire(struct wlr_swapchain_slot *slot) { slot->release.notify = slot_handle_release; wl_signal_add(&slot->buffer->events.release, &slot->release); + if (age != NULL) { + *age = slot->age; + } + return wlr_buffer_lock(slot->buffer); } -struct wlr_buffer *wlr_swapchain_acquire( - struct wlr_swapchain *swapchain) { +struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, + int *age) { struct wlr_swapchain_slot *free_slot = NULL; for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { struct wlr_swapchain_slot *slot = &swapchain->slots[i]; @@ -84,7 +89,7 @@ struct wlr_buffer *wlr_swapchain_acquire( continue; } if (slot->buffer != NULL) { - return slot_acquire(slot); + return slot_acquire(swapchain, slot, age); } free_slot = slot; } @@ -104,5 +109,36 @@ struct wlr_buffer *wlr_swapchain_acquire( wlr_log(WLR_ERROR, "Failed to allocate buffer"); return NULL; } - return slot_acquire(free_slot); + return slot_acquire(swapchain, free_slot, age); +} + +static bool swapchain_has_buffer(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer) { + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->buffer == buffer) { + return true; + } + } + return false; +} + +void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer) { + assert(buffer != NULL); + + if (!swapchain_has_buffer(swapchain, buffer)) { + return; + } + + // See the algorithm described in: + // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_buffer_age.txt + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->buffer == buffer) { + slot->age = 1; + } else if (slot->age > 0) { + slot->age++; + } + } }