diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 8e77133d..11b769bf 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -210,6 +210,11 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats); + struct wlr_wl_buffer *buffer, *tmp_buffer; + wl_list_for_each_safe(buffer, tmp_buffer, &wl->buffers, link) { + destroy_wl_buffer(buffer); + } + destroy_wl_seats(wl); if (wl->zxdg_decoration_manager_v1) { zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1); @@ -270,6 +275,7 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, wl_list_init(&wl->devices); wl_list_init(&wl->outputs); wl_list_init(&wl->seats); + wl_list_init(&wl->buffers); wl->remote_display = wl_display_connect(remote); if (!wl->remote_display) { diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 188c22b6..db9056d5 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -133,24 +133,33 @@ static bool output_attach_render(struct wlr_output *wlr_output, return true; } -static void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { +void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { if (buffer == NULL) { return; } + wl_list_remove(&buffer->buffer_destroy.link); + wl_list_remove(&buffer->link); wl_buffer_destroy(buffer->wl_buffer); - wlr_buffer_unlock(buffer->buffer); free(buffer); } static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { struct wlr_wl_buffer *buffer = data; - destroy_wl_buffer(buffer); + buffer->released = true; + wlr_buffer_unlock(buffer->buffer); // might free buffer } static const struct wl_buffer_listener buffer_listener = { .release = buffer_handle_release, }; +static void buffer_handle_buffer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_wl_buffer *buffer = + wl_container_of(listener, buffer, buffer_destroy); + destroy_wl_buffer(buffer); +} + static bool test_buffer(struct wlr_wl_backend *wl, struct wlr_buffer *wlr_buffer) { struct wlr_dmabuf_attributes attribs; @@ -207,12 +216,33 @@ static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl, } buffer->wl_buffer = wl_buffer; buffer->buffer = wlr_buffer_lock(wlr_buffer); + wl_list_insert(&wl->buffers, &buffer->link); wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer); + buffer->buffer_destroy.notify = buffer_handle_buffer_destroy; + wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); + return buffer; } +static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl, + struct wlr_buffer *wlr_buffer) { + struct wlr_wl_buffer *buffer; + wl_list_for_each(buffer, &wl->buffers, link) { + // We can only re-use a wlr_wl_buffer if the parent compositor has + // released it, because wl_buffer.release is per-wl_buffer, not per + // wl_surface.commit. + if (buffer->buffer == wlr_buffer && buffer->released) { + buffer->released = false; + wlr_buffer_lock(buffer->buffer); + return buffer; + } + } + + return create_wl_buffer(wl, wlr_buffer); +} + static bool output_test(struct wlr_output *wlr_output) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); @@ -287,7 +317,7 @@ static bool output_commit(struct wlr_output *wlr_output) { } struct wlr_wl_buffer *buffer = - create_wl_buffer(output->backend, wlr_buffer); + get_or_create_wl_buffer(output->backend, wlr_buffer); if (buffer == NULL) { return false; } diff --git a/include/backend/wayland.h b/include/backend/wayland.h index 0ff52b51..1c874b35 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -25,9 +25,11 @@ struct wlr_wl_backend { struct wlr_renderer *renderer; struct wlr_drm_format *format; struct wlr_allocator *allocator; + struct wl_list buffers; // wlr_wl_buffer.link size_t requested_outputs; size_t last_output_num; struct wl_listener local_display_destroy; + /* remote state */ struct wl_display *remote_display; struct wl_event_source *remote_display_src; @@ -47,6 +49,9 @@ struct wlr_wl_backend { struct wlr_wl_buffer { struct wlr_buffer *buffer; struct wl_buffer *wl_buffer; + bool released; + struct wl_list link; // wlr_wl_backend.buffers + struct wl_listener buffer_destroy; }; struct wlr_wl_presentation_feedback { @@ -130,6 +135,7 @@ struct wlr_wl_input_device *create_wl_input_device( struct wlr_wl_seat *seat, enum wlr_input_device_type type); bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl); void destroy_wl_seats(struct wlr_wl_backend *wl); +void destroy_wl_buffer(struct wlr_wl_buffer *buffer); extern const struct wl_seat_listener seat_listener;