From febb9a4261a5908b00e216261d8f22eadcbd64f0 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Fri, 17 Sep 2021 17:59:28 +0800 Subject: [PATCH] onscreen/native: Defer posting if there's already a post in progress And when the number of pending posts decreases we know it's safe to submit a new one. Since KMS generally only supports one outstanding post right now, "decreases" means equal to zero. Signed-off-by: Mingi Sung --- src/backends/native/meta-onscreen-native.c | 195 ++++++++++++++++++--- 1 file changed, 170 insertions(+), 25 deletions(-) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 150cc0d10..7f9777bf3 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -105,6 +105,7 @@ struct _MetaOnscreenNative ClutterFrame *presented_frame; ClutterFrame *posted_frame; + ClutterFrame *stalled_frame; ClutterFrame *next_frame; struct { @@ -119,6 +120,9 @@ struct _MetaOnscreenNative } egl; #endif + gboolean needs_flush; + unsigned int swaps_pending; + gboolean frame_sync_requested; gboolean frame_sync_enabled; @@ -141,7 +145,11 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, static GQuark blit_source_quark = 0; static void -post_latest_swap (CoglOnscreen *onscreen); +try_post_latest_swap (CoglOnscreen *onscreen); + +static void +post_finish_frame (MetaOnscreenNative *onscreen_native, + MetaKmsUpdate *kms_update); static gboolean init_secondary_gpu_state (MetaRendererNative *renderer_native, @@ -246,6 +254,7 @@ notify_view_crtc_presented (MetaRendererView *view, meta_onscreen_native_notify_frame_complete (onscreen); meta_onscreen_native_swap_drm_fb (onscreen); + try_post_latest_swap (onscreen); } static void @@ -295,15 +304,13 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); CoglFrameInfo *frame_info; frame_info = cogl_onscreen_peek_head_frame_info (onscreen); frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - g_warn_if_fail (!onscreen_native->posted_frame); - meta_onscreen_native_notify_frame_complete (onscreen); + try_post_latest_swap (onscreen); } static void @@ -374,6 +381,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, meta_onscreen_native_notify_frame_complete (onscreen); meta_onscreen_native_clear_posted_fb (onscreen); + try_post_latest_swap (onscreen); } static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { @@ -434,18 +442,36 @@ custom_egl_stream_page_flip (gpointer custom_page_flip_data, } #endif /* HAVE_EGL_DEVICE */ -void -meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) +static void +drop_stalled_swap (CoglOnscreen *onscreen) { CoglFrameInfo *frame_info; + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - meta_onscreen_native_swap_drm_fb (onscreen); + if (onscreen_native->swaps_pending <= 1) + return; + + onscreen_native->swaps_pending--; + + g_clear_pointer (&onscreen_native->stalled_frame, clutter_frame_unref); frame_info = cogl_onscreen_peek_tail_frame_info (onscreen); frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); } +void +meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) +{ + drop_stalled_swap (onscreen); + + /* If the monitor just woke up and the shell is fully idle (has nothing + * more to swap) then we just woke to an indefinitely black screen. Let's + * fix that using the last swap (which is never classified as "stalled"). + */ + try_post_latest_swap (onscreen); +} + static void apply_transform (MetaCrtcKms *crtc_kms, MetaKmsPlaneAssignment *kms_plane_assignment, @@ -1359,12 +1385,38 @@ swap_buffer_result_feedback (const MetaKmsFeedback *kms_feedback, g_warning ("Page flip failed: %s", error->message); frame_info = cogl_onscreen_peek_head_frame_info (onscreen); - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - meta_onscreen_native_notify_frame_complete (onscreen); + /* After resuming from suspend, drop_stalled_swap might have done this + * already and emptied the frame_info queue. + */ + if (frame_info) + { + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + meta_onscreen_native_notify_frame_complete (onscreen); + } + meta_onscreen_native_clear_posted_fb (onscreen); } +static void +assign_next_frame (MetaOnscreenNative *onscreen_native, + ClutterFrame *frame) +{ + CoglOnscreen *onscreen = COGL_ONSCREEN (onscreen_native); + + if (onscreen_native->next_frame != NULL) + { + g_warn_if_fail (onscreen_native->stalled_frame == NULL); + drop_stalled_swap (onscreen); + g_warn_if_fail (onscreen_native->stalled_frame == NULL); + g_clear_pointer (&onscreen_native->stalled_frame, clutter_frame_unref); + onscreen_native->stalled_frame = + g_steal_pointer (&onscreen_native->next_frame); + } + + onscreen_native->next_frame = clutter_frame_ref (frame); +} + static const MetaKmsResultListenerVtable swap_buffer_result_listener_vtable = { .feedback = swap_buffer_result_feedback, }; @@ -1494,14 +1546,14 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, #endif } - g_warn_if_fail (!onscreen_native->next_frame); - onscreen_native->next_frame = clutter_frame_ref (frame); + assign_next_frame (onscreen_native, frame); clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); meta_frame_native_set_damage (frame_native, rectangles, n_rectangles); - post_latest_swap (onscreen); + onscreen_native->swaps_pending++; + try_post_latest_swap (onscreen); return; swap_failed: @@ -1511,7 +1563,7 @@ swap_failed: } static void -post_latest_swap (CoglOnscreen *onscreen) +try_post_latest_swap (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); @@ -1535,16 +1587,41 @@ post_latest_swap (CoglOnscreen *onscreen) int sync_fd; COGL_TRACE_SCOPED_ANCHOR (MetaRendererNativePostKmsUpdate); + if (onscreen_native->next_frame == NULL) + return; + power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (power_save_mode == META_POWER_SAVE_ON) { + unsigned int frames_pending = + cogl_onscreen_get_pending_frame_count (onscreen); + unsigned int posts_pending; int n_rectangles; int *rectangles; + g_assert (frames_pending >= onscreen_native->swaps_pending); + posts_pending = frames_pending - onscreen_native->swaps_pending; + if (posts_pending > 0) + return; /* wait for the next frame notification and then try again */ + frame = clutter_frame_ref (onscreen_native->next_frame); frame_native = meta_frame_native_from_frame (frame); n_rectangles = meta_frame_native_get_damage (frame_native, &rectangles); + if (onscreen_native->swaps_pending == 0) + { + if (frame_native) + { + kms_update = meta_frame_native_steal_kms_update (frame_native); + if (kms_update) + post_finish_frame (onscreen_native, kms_update); + } + return; + } + + drop_stalled_swap (onscreen); + onscreen_native->swaps_pending--; + kms_update = meta_frame_native_ensure_kms_update (frame_native, kms_device); meta_kms_update_add_result_listener (kms_update, @@ -1570,7 +1647,7 @@ post_latest_swap (CoglOnscreen *onscreen) } COGL_TRACE_BEGIN_ANCHORED (MetaRendererNativePostKmsUpdate, - "Meta::OnscreenNative::post_latest_swap#post_pending_update()"); + "Meta::OnscreenNative::try_post_latest_swap#post_pending_update()"); switch (renderer_gpu_data->mode) { @@ -1765,13 +1842,24 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, return FALSE; } + /* Our direct scanout frame counts as 1, so more than that means we would + * be jumping the queue (and post would fail). + */ + if (cogl_onscreen_get_pending_frame_count (onscreen) > 1) + { + g_set_error_literal (error, + COGL_SCANOUT_ERROR, + COGL_SCANOUT_ERROR_INHIBITED, + "Direct scanout is inhibited during triple buffering"); + return FALSE; + } + renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, render_gpu); g_warn_if_fail (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_GBM); - g_warn_if_fail (!onscreen_native->next_frame); - onscreen_native->next_frame = clutter_frame_ref (frame); + assign_next_frame (onscreen_native, frame); meta_frame_native_set_scanout (frame_native, scanout); meta_frame_native_set_buffer (frame_native, @@ -2018,22 +2106,79 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); MetaKmsUpdate *kms_update; + unsigned int frames_pending = cogl_onscreen_get_pending_frame_count (onscreen); + unsigned int swaps_pending = onscreen_native->swaps_pending; + unsigned int posts_pending = frames_pending - swaps_pending; - kms_update = meta_frame_native_steal_kms_update (frame_native); - if (!kms_update) + onscreen_native->needs_flush |= meta_kms_device_handle_flush (kms_device, + kms_crtc); + + if (!meta_frame_native_has_kms_update (frame_native)) { - if (meta_kms_device_handle_flush (kms_device, kms_crtc)) - { - kms_update = meta_kms_update_new (kms_device); - meta_kms_update_set_flushing (kms_update, kms_crtc); - } - else + if (!onscreen_native->needs_flush || posts_pending) { clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); return; } } + if (posts_pending && !swaps_pending) + { + g_return_if_fail (meta_frame_native_has_kms_update (frame_native)); + g_warn_if_fail (onscreen_native->next_frame == NULL); + + g_clear_pointer (&onscreen_native->next_frame, clutter_frame_unref); + onscreen_native->next_frame = clutter_frame_ref (frame); + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + + kms_update = meta_frame_native_steal_kms_update (frame_native); + + if (posts_pending && swaps_pending) + { + MetaFrameNative *older_frame_native; + MetaKmsUpdate *older_kms_update; + + g_return_if_fail (kms_update); + g_return_if_fail (onscreen_native->next_frame != NULL); + + older_frame_native = + meta_frame_native_from_frame (onscreen_native->next_frame); + older_kms_update = + meta_frame_native_ensure_kms_update (older_frame_native, kms_device); + meta_kms_update_merge_from (older_kms_update, kms_update); + meta_kms_update_free (kms_update); + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); + return; + } + + if (!kms_update) + { + kms_update = meta_kms_update_new (kms_device); + g_warn_if_fail (onscreen_native->needs_flush); + } + + if (onscreen_native->needs_flush) + { + meta_kms_update_set_flushing (kms_update, kms_crtc); + onscreen_native->needs_flush = FALSE; + } + + post_finish_frame (onscreen_native, kms_update); + + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); +} + +static void +post_finish_frame (MetaOnscreenNative *onscreen_native, + MetaKmsUpdate *kms_update) +{ + MetaCrtc *crtc = onscreen_native->crtc; + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; + meta_kms_update_add_result_listener (kms_update, &finish_frame_result_listener_vtable, NULL, @@ -2056,7 +2201,6 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, meta_kms_update_set_flushing (kms_update, kms_crtc); meta_kms_device_post_update (kms_device, kms_update, META_KMS_UPDATE_FLAG_NONE); - clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); } static gboolean @@ -2961,6 +3105,7 @@ meta_onscreen_native_dispose (GObject *object) meta_onscreen_native_detach (onscreen_native); g_clear_pointer (&onscreen_native->next_frame, clutter_frame_unref); + g_clear_pointer (&onscreen_native->stalled_frame, clutter_frame_unref); g_clear_pointer (&onscreen_native->posted_frame, clutter_frame_unref); g_clear_pointer (&onscreen_native->presented_frame, clutter_frame_unref);