From a1f8c49d5bdd20eda96b3744334dc197fb8f1727 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Tue, 2 Jan 2024 15:59:25 +0100 Subject: [PATCH] compositor: repaint backends separately When a repaint is triggered then not all backends may have outputs that need to be repainted. Check which outputs will be repainted first and then repaint only the backends that need it. This way unnecessary repaint_begin() and repaint_flush()/repaint_cancel() can be skipped and errors are handled for each backend separately. Signed-off-by: Michael Olbrich --- include/libweston/libweston.h | 5 + libweston/backend.h | 9 ++ libweston/compositor.c | 172 +++++++++++++++++++--------------- 3 files changed, 109 insertions(+), 77 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index bedff8a96..4f44cd6fc 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -579,6 +579,10 @@ struct weston_output { /** True if the entire contents of the output should be redrawn */ bool full_repaint_needed; + /** True if the output will be repainted in the currently active + * repaint handler. */ + bool will_repaint; + /** Used only between repaint_begin and repaint_cancel. */ bool repainted; @@ -632,6 +636,7 @@ struct weston_output { struct weston_log_pacer pixman_overdraw_pacer; int (*start_repaint_loop)(struct weston_output *output); + void (*prepare_repaint)(struct weston_output *output); int (*repaint)(struct weston_output *output); void (*destroy)(struct weston_output *output); void (*assign_planes)(struct weston_output *output); diff --git a/libweston/backend.h b/libweston/backend.h index 4312727b5..a9f3c2adb 100644 --- a/libweston/backend.h +++ b/libweston/backend.h @@ -48,6 +48,15 @@ struct weston_backend { */ unsigned int supported_presentation_clocks; + /** True if the output will be repainted in the currently active + * repaint handler. + * + * This will be set if the same flag for any output of this backend is + * also true. It is used to determine if repaint_begin() and + * repaint_flush()/repaint_cancel() need to be called. + */ + bool will_repaint; + /** Prepare for compositor shutdown (optional) * * This will be called before weston_compositor_shutdown() diff --git a/libweston/compositor.c b/libweston/compositor.c index 15b707b7f..fdf1d0003 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -3606,8 +3606,16 @@ weston_output_flush_damage_for_primary_plane(struct weston_output *output, } } +static void +weston_output_schedule_repaint_reset(struct weston_output *output) +{ + output->repaint_status = REPAINT_NOT_SCHEDULED; + TL_POINT(output->compositor, "core_repaint_exit_loop", + TLP_OUTPUT(output), TLP_END); +} + static int -weston_output_repaint(struct weston_output *output) +weston_output_repaint(struct weston_output *output, struct timespec *now) { struct weston_compositor *ec = output->compositor; struct weston_paint_node *pnode; @@ -3618,9 +3626,6 @@ weston_output_repaint(struct weston_output *output) uint32_t frame_time_msec; enum weston_hdcp_protection highest_requested = WESTON_HDCP_DISABLE; - if (output->destroying) - return 0; - TL_POINT(ec, "core_repaint_begin", TLP_OUTPUT(output), TLP_END); /* Rebuild the surface list and update surface transforms up front. */ @@ -3673,8 +3678,10 @@ weston_output_repaint(struct weston_output *output) r = output->repaint(output); output->repaint_needed = false; - if (r == 0) + if (r == 0) { output->repaint_status = REPAINT_AWAITING_COMPLETION; + output->repainted = true; + } weston_compositor_repick(ec); @@ -3719,66 +3726,58 @@ weston_output_repaint(struct weston_output *output) TL_POINT(ec, "core_repaint_posted", TLP_OUTPUT(output), TLP_END); - return r; -} - -static void -weston_output_schedule_repaint_reset(struct weston_output *output) -{ - output->repaint_status = REPAINT_NOT_SCHEDULED; - TL_POINT(output->compositor, "core_repaint_exit_loop", - TLP_OUTPUT(output), TLP_END); -} - -static int -weston_output_maybe_repaint(struct weston_output *output, struct timespec *now) -{ - struct weston_compositor *compositor = output->compositor; - int ret = 0; - int64_t msec_to_repaint; - - /* We're not ready yet; come back to make a decision later. */ - if (output->repaint_status != REPAINT_SCHEDULED) - return ret; - - msec_to_repaint = timespec_sub_to_msec(&output->next_repaint, now); - if (msec_to_repaint > 1) - return ret; - - /* If we're sleeping, drop the repaint machinery entirely; we will - * explicitly repaint all outputs when we come back. */ - if (compositor->state == WESTON_COMPOSITOR_SLEEPING || - compositor->state == WESTON_COMPOSITOR_OFFSCREEN) - goto err; - - /* We don't actually need to repaint this output; drop it from - * repaint until something causes damage. */ - if (!output->repaint_needed) - goto err; - - if (output->power_state == WESTON_OUTPUT_POWER_FORCED_OFF) - goto err; - - if (output->repaint_only_on_capture && - !weston_output_has_renderer_capture_tasks(output)) - goto err; - /* If repaint fails, we aren't going to get weston_output_finish_frame * to trigger a new repaint, so drop it from repaint and hope * something schedules a successful repaint later. As repainting may * take some time, re-read our clock as a courtesy to the next * output. */ - ret = weston_output_repaint(output); - weston_compositor_read_presentation_clock(compositor, now); - if (ret != 0) - goto err; + weston_compositor_read_presentation_clock(ec, now); + if (r != 0) + weston_output_schedule_repaint_reset(output); - output->repainted = true; - return ret; + return r; +} -err: +static bool +weston_output_check_repaint(struct weston_output *output, struct timespec *now) +{ + struct weston_compositor *compositor = output->compositor; + int64_t msec_to_repaint; + + /* We're not ready yet; come back to make a decision later. */ + if (output->repaint_status != REPAINT_SCHEDULED) + return false; + + msec_to_repaint = timespec_sub_to_msec(&output->next_repaint, now); + if (msec_to_repaint > 1) + return false; + + /* If we're sleeping, drop the repaint machinery entirely; we will + * explicitly repaint all outputs when we come back. */ + if (compositor->state == WESTON_COMPOSITOR_SLEEPING || + compositor->state == WESTON_COMPOSITOR_OFFSCREEN) + goto out; + + /* We don't actually need to repaint this output; drop it from + * repaint until something causes damage. */ + if (!output->repaint_needed) + goto out; + + if (output->power_state == WESTON_OUTPUT_POWER_FORCED_OFF) + goto out; + + if (output->repaint_only_on_capture && + !weston_output_has_renderer_capture_tasks(output)) + goto out; + + if (output->destroying) + goto out; + + return true; + +out: weston_output_schedule_repaint_reset(output); - return ret; + return false; } static void @@ -3846,36 +3845,55 @@ output_repaint_timer_handler(void *data) weston_compositor_read_presentation_clock(compositor, &now); compositor->last_repaint_start = now; + wl_list_for_each(output, &compositor->output_list, link) { + if (!weston_output_check_repaint(output, &now)) + continue; + + output->will_repaint = true; + output->backend->will_repaint = true; + + if (output->prepare_repaint) + output->prepare_repaint(output); + } + wl_list_for_each(backend, &compositor->backend_list, link) { + if (!backend->will_repaint) + continue; + + backend->will_repaint = false; + if (backend->repaint_begin) backend->repaint_begin(backend); - } - wl_list_for_each(output, &compositor->output_list, link) { - ret = weston_output_maybe_repaint(output, &now); - if (ret) - break; - } + wl_list_for_each(output, &compositor->output_list, link) { + if (output->backend != backend) + continue; - if (ret == 0) { - wl_list_for_each(backend, &compositor->backend_list, link) { + if (!output->will_repaint) + continue; + + output->will_repaint = false; + ret = weston_output_repaint(output, &now); + if (ret) + break; + } + if (ret == 0) { if (backend->repaint_flush) ret = backend->repaint_flush(backend); - } - } else { - wl_list_for_each(backend, &compositor->backend_list, link) { + } else { if (backend->repaint_cancel) backend->repaint_cancel(backend); - } - } - if (ret != 0) { - wl_list_for_each(output, &compositor->output_list, link) { - if (output->repainted) { - if (ret == -EBUSY) - weston_output_schedule_repaint_restart(output); - else - weston_output_schedule_repaint_reset(output); + wl_list_for_each(output, &compositor->output_list, link) { + if (output->backend != backend) + continue; + + if (output->repainted) { + if (ret == -EBUSY) + weston_output_schedule_repaint_restart(output); + else + weston_output_schedule_repaint_reset(output); + } } } }