From 5da9ad3e34b82224095d00376f8b75433a8ea46d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 16 Aug 2023 10:42:56 +0200 Subject: [PATCH] backend/x11: re-send last buffer on expose event Instead of requesting a new frame from the compositor when we receive an expose event, re-submit the last frame. This makes the X11 backend behave like the other backends and removes the last wlr_output_update_needs_frame() call in backends, at the cost of keeping the current buffer locked for potentially a longer time. --- backend/x11/backend.c | 5 +-- backend/x11/output.c | 98 +++++++++++++++++++++++++++++++++---------- include/backend/x11.h | 5 +++ 3 files changed, 81 insertions(+), 27 deletions(-) diff --git a/backend/x11/backend.c b/backend/x11/backend.c index fcaab6186..a97c0d390 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -69,10 +69,7 @@ static void handle_x11_event(struct wlr_x11_backend *x11, struct wlr_x11_output *output = get_x11_output_from_window_id(x11, ev->window); if (output != NULL) { - pixman_region32_union_rect( - &output->exposed, &output->exposed, - ev->x, ev->y, ev->width, ev->height); - wlr_output_update_needs_frame(&output->wlr_output); + handle_x11_expose_event(output, ev); } break; } diff --git a/backend/x11/output.c b/backend/x11/output.c index 2d433f818..d4a529956 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -92,6 +92,7 @@ static void output_destroy(struct wlr_output *wlr_output) { wlr_pointer_finish(&output->pointer); wlr_touch_finish(&output->touch); + wlr_buffer_unlock(output->current_buffer); struct wlr_x11_buffer *buffer, *buffer_tmp; wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) { @@ -286,6 +287,34 @@ static struct wlr_x11_buffer *get_or_create_x11_buffer( return create_x11_buffer(output, wlr_buffer); } +static xcb_xfixes_region_t create_region(struct wlr_x11_backend *x11, + const pixman_region32_t *region) { + int rects_len = 0; + const pixman_box32_t *rects = pixman_region32_rectangles(region, &rects_len); + + xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t)); + if (!xcb_rects) { + return XCB_NONE; + } + + for (int i = 0; i < rects_len; i++) { + const pixman_box32_t *box = &rects[i]; + xcb_rects[i] = (struct xcb_rectangle_t){ + .x = box->x1, + .y = box->y1, + .width = box->x2 - box->x1, + .height = box->y2 - box->y1, + }; + } + + xcb_xfixes_region_t x11_region = xcb_generate_id(x11->xcb); + xcb_xfixes_create_region(x11->xcb, x11_region, rects_len, xcb_rects); + + free(xcb_rects); + + return x11_region; +} + static bool output_commit_buffer(struct wlr_x11_output *output, const struct wlr_output_state *state) { struct wlr_x11_backend *x11 = output->x11; @@ -300,29 +329,7 @@ static bool output_commit_buffer(struct wlr_x11_output *output, xcb_xfixes_region_t region = XCB_NONE; if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { pixman_region32_union(&output->exposed, &output->exposed, &state->damage); - - int rects_len = 0; - const pixman_box32_t *rects = pixman_region32_rectangles(&output->exposed, &rects_len); - - xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t)); - if (!xcb_rects) { - goto error; - } - - for (int i = 0; i < rects_len; i++) { - const pixman_box32_t *box = &rects[i]; - xcb_rects[i] = (struct xcb_rectangle_t){ - .x = box->x1, - .y = box->y1, - .width = box->x2 - box->x1, - .height = box->y2 - box->y1, - }; - } - - region = xcb_generate_id(x11->xcb); - xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects); - - free(xcb_rects); + region = create_region(x11, &output->exposed); } pixman_region32_clear(&output->exposed); @@ -338,6 +345,9 @@ static bool output_commit_buffer(struct wlr_x11_output *output, xcb_xfixes_destroy_region(x11->xcb, region); } + wlr_buffer_unlock(output->current_buffer); + output->current_buffer = wlr_buffer_lock(buffer); + return true; error: @@ -705,6 +715,12 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, return; } + if (output->last_msc == complete_notify->msc && complete_notify->serial == 0) { + // This is a present complete event triggered by a synthetic + // present request sent from handle_x11_expose_event() + return; + } + output->last_msc = complete_notify->msc; struct timespec t; @@ -732,3 +748,39 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type); } } + +void handle_x11_expose_event(struct wlr_x11_output *output, + xcb_expose_event_t *ev) { + struct wlr_x11_backend *x11 = output->x11; + + pixman_region32_union_rect(&output->exposed, &output->exposed, + ev->x, ev->y, ev->width, ev->height); + + if (output->current_buffer == NULL) { + return; + } + + // If we have access to the last submitted buffer, re-send the previous + // present request + + struct wlr_x11_buffer *x11_buffer = + get_or_create_x11_buffer(output, output->current_buffer); + if (!x11_buffer) { + return; + } + + xcb_xfixes_region_t region = create_region(x11, &output->exposed); + + pixman_region32_clear(&output->exposed); + + uint32_t serial = 0; + uint32_t options = 0; + uint64_t target_msc = output->last_msc; + xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial, + 0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc, + 0, 0, 0, NULL); + + if (region != XCB_NONE) { + xcb_xfixes_destroy_region(x11->xcb, region); + } +} diff --git a/include/backend/x11.h b/include/backend/x11.h index f66fed2af..28cb3fa04 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -44,6 +44,9 @@ struct wlr_x11_output { pixman_region32_t exposed; + // Last submitted buffer, to be re-submitted on expose events + struct wlr_buffer *current_buffer; + uint64_t last_msc; struct { @@ -141,5 +144,7 @@ void handle_x11_configure_notify(struct wlr_x11_output *output, xcb_configure_notify_event_t *event); void handle_x11_present_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *event); +void handle_x11_expose_event(struct wlr_x11_output *output, + xcb_expose_event_t *event); #endif