Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:mantarimay:GNOME
mutter
_service:download_url:1441.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:download_url:1441.patch of Package mutter
From 76bbbd39dc248bbff121164e726e14e47d551d28 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Fri, 17 Sep 2021 17:48:20 +0800 Subject: [PATCH 01/30] cogl/onscreen: Add function cogl_onscreen_get_pending_frame_count --- cogl/cogl/cogl-onscreen-private.h | 3 +++ cogl/cogl/cogl-onscreen.c | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h index 959a60533b3..a35a4fcb12d 100644 --- a/cogl/cogl/cogl-onscreen-private.h +++ b/cogl/cogl/cogl-onscreen-private.h @@ -79,3 +79,6 @@ cogl_onscreen_peek_tail_frame_info (CoglOnscreen *onscreen); COGL_EXPORT CoglFrameInfo * cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen); + +COGL_EXPORT unsigned int +cogl_onscreen_get_pending_frame_count (CoglOnscreen *onscreen); diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c index 71a9367224d..bdcc429161b 100644 --- a/cogl/cogl/cogl-onscreen.c +++ b/cogl/cogl/cogl-onscreen.c @@ -494,6 +494,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen) return g_queue_pop_head (&priv->pending_frame_infos); } +unsigned int +cogl_onscreen_get_pending_frame_count (CoglOnscreen *onscreen) +{ + CoglOnscreenPrivate *priv = cogl_onscreen_get_instance_private (onscreen); + + return g_queue_get_length (&priv->pending_frame_infos); +} + CoglFrameClosure * cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, CoglFrameCallback callback, -- GitLab From fdfd6448c9591c696999ac4b72fe9392de6d6817 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Mon, 12 Feb 2024 17:16:03 +0800 Subject: [PATCH 02/30] cogl/onscreen: Indent declaration parameters to align with above/below This fixes warnings from check-code-style. --- cogl/cogl/cogl-onscreen-private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h index a35a4fcb12d..cfd45a62509 100644 --- a/cogl/cogl/cogl-onscreen-private.h +++ b/cogl/cogl/cogl-onscreen-private.h @@ -78,7 +78,7 @@ COGL_EXPORT CoglFrameInfo * cogl_onscreen_peek_tail_frame_info (CoglOnscreen *onscreen); COGL_EXPORT CoglFrameInfo * -cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen); +cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen); COGL_EXPORT unsigned int cogl_onscreen_get_pending_frame_count (CoglOnscreen *onscreen); -- GitLab From f06cdfb8bc589de578aeedf02e28822bfcf61a48 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Wed, 20 Apr 2022 18:33:43 +0800 Subject: [PATCH 03/30] kms: Keep a shutting_down flag --- src/backends/native/meta-kms.c | 9 +++++++++ src/backends/native/meta-kms.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c index 32f3a0088e7..d7d6a4001e8 100644 --- a/src/backends/native/meta-kms.c +++ b/src/backends/native/meta-kms.c @@ -66,6 +66,8 @@ struct _MetaKms int kernel_thread_inhibit_count; MetaKmsCursorManager *cursor_manager; + + gboolean shutting_down; }; G_DEFINE_TYPE (MetaKms, meta_kms, META_TYPE_THREAD) @@ -367,6 +369,7 @@ static void on_prepare_shutdown (MetaBackend *backend, MetaKms *kms) { + kms->shutting_down = TRUE; meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL); meta_thread_flush_callbacks (META_THREAD (kms)); @@ -428,6 +431,12 @@ meta_kms_new (MetaBackend *backend, return kms; } +gboolean +meta_kms_is_shutting_down (MetaKms *kms) +{ + return kms->shutting_down; +} + static void meta_kms_finalize (GObject *object) { diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h index 74340140639..f6b19520bec 100644 --- a/src/backends/native/meta-kms.h +++ b/src/backends/native/meta-kms.h @@ -60,6 +60,8 @@ MetaKmsDevice * meta_kms_create_device (MetaKms *kms, MetaKmsDeviceFlag flags, GError **error); +gboolean meta_kms_is_shutting_down (MetaKms *kms); + MetaKms * meta_kms_new (MetaBackend *backend, MetaKmsFlags flags, GError **error); -- GitLab From 1a4ee2752d3bff65aa2201a8ebec4392f7128a6f Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Tue, 26 Oct 2021 18:50:50 +0800 Subject: [PATCH 04/30] renderer/native: Avoid requeuing the same onscreen for a power save flip This is a case that triple buffering will encounter. We don't want it to queue the same onscreen multiple times because that would represent multiple flips occurring simultaneously. It's a linear search but the list length is typically only 1 or 2 so no need for anything fancier yet. --- src/backends/native/meta-renderer-native.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index b818faf6f52..0aeb050e560 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -748,6 +748,9 @@ meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_na { const unsigned int timeout_ms = 100; + if (g_list_find (renderer_native->power_save_page_flip_onscreens, onscreen)) + return; + if (!renderer_native->power_save_page_flip_source_id) { renderer_native->power_save_page_flip_source_id = -- GitLab From 3ad4e75300bdb4051a7d04de8240e0bdc770904c Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Mon, 1 Nov 2021 19:35:34 +0800 Subject: [PATCH 05/30] renderer/native: Steal the power save flip list before iterating over it Because a single iteration might also grow the list again. --- src/backends/native/meta-renderer-native.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 0aeb050e560..02d1e3e0c06 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -731,12 +731,18 @@ static gboolean dummy_power_save_page_flip_cb (gpointer user_data) { MetaRendererNative *renderer_native = user_data; + GList *old_list = + g_steal_pointer (&renderer_native->power_save_page_flip_onscreens); - g_list_foreach (renderer_native->power_save_page_flip_onscreens, + g_list_foreach (old_list, (GFunc) meta_onscreen_native_dummy_power_save_page_flip, NULL); - g_clear_list (&renderer_native->power_save_page_flip_onscreens, + g_clear_list (&old_list, g_object_unref); + + if (renderer_native->power_save_page_flip_onscreens != NULL) + return G_SOURCE_CONTINUE; + renderer_native->power_save_page_flip_source_id = 0; return G_SOURCE_REMOVE; -- GitLab From ead86d1e420f3b78f087ea65baf4cb457941db95 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Fri, 10 Dec 2021 16:40:58 +0800 Subject: [PATCH 06/30] onscreen/native: Log swapbuffers and N-buffering when MUTTER_DEBUG=kms --- src/backends/native/meta-onscreen-native.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 8a25fca8cc4..fa5479728e7 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -1294,6 +1294,19 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, "Meta::OnscreenNative::swap_buffers_with_damage()"); + if (meta_is_topic_enabled (META_DEBUG_KMS)) + { + unsigned int frames_pending = + cogl_onscreen_get_pending_frame_count (onscreen); + + meta_topic (META_DEBUG_KMS, + "Swap buffers: %u frames pending (%s-buffering)", + frames_pending, + frames_pending == 1 ? "double" : + frames_pending == 2 ? "triple" : + "?"); + } + secondary_gpu_fb = update_secondary_gpu_state_pre_swap_buffers (onscreen, rectangles, -- GitLab From 7950b58fbf552d73b3c1ff46fc3588762156fcf8 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Wed, 28 Jul 2021 16:35:56 +0800 Subject: [PATCH 07/30] onscreen/native: Replace an assertion that double buffering is the maximum Because it soon won't be the maximum. But we do want to verify that the frame info queue is not empty, to avoid NULL dereferencing and catch logic errors. --- src/backends/native/meta-onscreen-native.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index fa5479728e7..84ebefb93cb 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -192,7 +192,7 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen) info = cogl_onscreen_pop_head_frame_info (onscreen); - g_assert (!cogl_onscreen_peek_head_frame_info (onscreen)); + g_return_if_fail (info); _cogl_onscreen_notify_frame_sync (onscreen, info); _cogl_onscreen_notify_complete (onscreen, info); -- GitLab From 2ae32a6353188cd832054c772ce1285b6c079a9b Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Thu, 16 Sep 2021 16:26:25 +0800 Subject: [PATCH 08/30] onscreen/native: Deduplicate calls to clutter_frame_set_result All paths out of `meta_onscreen_native_swap_buffers_with_damage` from here onward would set the same `CLUTTER_FRAME_RESULT_PENDING_PRESENTED` (or terminate with `g_assert_not_reached`). Even failed posts set this result because they will do a `meta_onscreen_native_notify_frame_complete` in `page_flip_feedback_discarded`. --- src/backends/native/meta-onscreen-native.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 84ebefb93cb..28729dbde3f 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -1405,6 +1405,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, #endif } + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + /* * If we changed EGL context, cogl will have the wrong idea about what is * current, making it fail to set it when it needs to. Avoid that by making @@ -1441,8 +1444,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, { meta_renderer_native_queue_power_save_page_flip (renderer_native, onscreen); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } @@ -1462,8 +1463,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, kms_update = meta_frame_native_steal_kms_update (frame_native); meta_renderer_native_queue_mode_set_update (renderer_native, kms_update); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } else if (meta_renderer_native_has_pending_mode_set (renderer_native)) @@ -1477,8 +1476,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, meta_frame_native_steal_kms_update (frame_native); meta_renderer_native_post_mode_set_updates (renderer_native); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } break; @@ -1494,8 +1491,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, kms_update); meta_renderer_native_post_mode_set_updates (renderer_native); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } break; @@ -1510,7 +1505,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, kms_update = meta_frame_native_steal_kms_update (frame_native); meta_kms_device_post_update (kms_device, kms_update, META_KMS_UPDATE_FLAG_NONE); - clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); } gboolean -- GitLab From 633ea7607643f06e2a58feb603ec49ad07713a56 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Wed, 28 Jul 2021 16:29:27 +0800 Subject: [PATCH 09/30] onscreen/native: Split swap_buffers_with_damage into two functions 1. The EGL part: meta_onscreen_native_swap_buffers_with_damage 2. The KMS part: post_latest_swap --- src/backends/native/meta-onscreen-native.c | 69 ++++++++++++++++------ 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 28729dbde3f..ab9c3b0b652 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -97,6 +97,8 @@ struct _MetaOnscreenNative ClutterFrame *presented_frame; ClutterFrame *next_frame; + int *next_rectangles; /* 4 x n_rectangles */ + int next_n_rectangles; struct { struct gbm_surface *surface; @@ -131,6 +133,9 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, static GQuark blit_source_quark = 0; +static void +post_latest_swap (CoglOnscreen *onscreen); + static gboolean init_secondary_gpu_state (MetaRendererNative *renderer_native, CoglOnscreen *onscreen, @@ -1267,29 +1272,21 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; - MetaRenderer *renderer = META_RENDERER (renderer_native); - MetaBackend *backend = meta_renderer_get_backend (renderer); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; MetaGpuKms *render_gpu = onscreen_native->render_gpu; MetaDeviceFile *render_device_file; ClutterFrame *frame = user_data; - MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); - MetaKmsUpdate *kms_update; CoglOnscreenClass *parent_class; gboolean create_timestamp_query = TRUE; gboolean egl_context_changed = FALSE; - MetaPowerSave power_save_mode; g_autoptr (GError) error = NULL; MetaDrmBufferFlags buffer_flags; MetaDrmBufferGbm *buffer_gbm; g_autoptr (MetaDrmBuffer) primary_gpu_fb = NULL; g_autoptr (MetaDrmBuffer) secondary_gpu_fb = NULL; g_autoptr (MetaDrmBuffer) buffer = NULL; - MetaKmsCrtc *kms_crtc; - MetaKmsDevice *kms_device; + size_t rectangles_size; COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, "Meta::OnscreenNative::swap_buffers_with_damage()"); @@ -1378,13 +1375,11 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, primary_gpu_fb, secondary_gpu_fb); - g_warn_if_fail (!onscreen_native->next_frame); - onscreen_native->next_frame = clutter_frame_ref (frame); - switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: - meta_frame_native_set_buffer (frame_native, buffer); + meta_frame_native_set_buffer (meta_frame_native_from_frame (frame), + buffer); if (!meta_drm_buffer_ensure_fb_id (buffer, &error)) { @@ -1417,12 +1412,47 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, if (egl_context_changed) _cogl_winsys_egl_ensure_current (cogl_display); - kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); - kms_device = meta_kms_crtc_get_device (kms_crtc); + rectangles_size = n_rectangles * 4 * sizeof (int); + onscreen_native->next_rectangles = + g_realloc (onscreen_native->next_rectangles, rectangles_size); + memcpy (onscreen_native->next_rectangles, rectangles, rectangles_size); + onscreen_native->next_n_rectangles = n_rectangles; + + g_warn_if_fail (!onscreen_native->next_frame); + g_clear_pointer (&onscreen_native->next_frame, clutter_frame_unref); + onscreen_native->next_frame = clutter_frame_ref (frame); + + post_latest_swap (onscreen); +} + +static void +post_latest_swap (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + CoglRenderer *cogl_renderer = cogl_context->display->renderer; + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; + MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; + MetaRenderer *renderer = META_RENDERER (renderer_native); + MetaBackend *backend = meta_renderer_get_backend (renderer); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + MetaPowerSave power_save_mode; + MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + MetaKmsUpdate *kms_update; + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; + ClutterFrame *frame = onscreen_native->next_frame; + MetaFrameNative *frame_native; power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (power_save_mode == META_POWER_SAVE_ON) { + frame_native = meta_frame_native_from_frame (frame); + kms_update = meta_frame_native_ensure_kms_update (frame_native, kms_device); meta_kms_update_add_result_listener (kms_update, @@ -1437,8 +1467,8 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, onscreen_native->crtc, kms_update, META_KMS_ASSIGN_PLANE_FLAG_NONE, - rectangles, - n_rectangles); + onscreen_native->next_rectangles, + onscreen_native->next_n_rectangles); } else { @@ -1448,7 +1478,7 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, } COGL_TRACE_BEGIN_SCOPED (MetaRendererNativePostKmsUpdate, - "Meta::OnscreenNative::swap_buffers_with_damage#post_pending_update()"); + "Meta::OnscreenNative::post_latest_swap#post_pending_update()"); switch (renderer_gpu_data->mode) { @@ -2825,6 +2855,9 @@ meta_onscreen_native_dispose (GObject *object) g_clear_pointer (&onscreen_native->next_frame, clutter_frame_unref); g_clear_pointer (&onscreen_native->presented_frame, clutter_frame_unref); + g_clear_pointer (&onscreen_native->next_rectangles, g_free); + onscreen_native->next_n_rectangles = 0; + renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, onscreen_native->render_gpu); -- GitLab From 37f78af599cc08f58ee0feba58315c62e8105a6a Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Tue, 5 Dec 2023 17:50:44 +0800 Subject: [PATCH 10/30] onscreen/native: Insert a 'posted' frame between 'next' and 'presented' This will allow us to keep track of up to two buffers that have been swapped but not yet scanning out, for triple buffering. This commit replaces mutter!1968 --- src/backends/native/meta-onscreen-native.c | 36 +++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index ab9c3b0b652..bf08a18a3e9 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -96,6 +96,7 @@ struct _MetaOnscreenNative MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; ClutterFrame *presented_frame; + ClutterFrame *posted_frame; ClutterFrame *next_frame; int *next_rectangles; /* 4 x n_rectangles */ int next_n_rectangles; @@ -146,20 +147,20 @@ meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - if (!onscreen_native->next_frame) + if (!onscreen_native->posted_frame) return; g_clear_pointer (&onscreen_native->presented_frame, clutter_frame_unref); onscreen_native->presented_frame = - g_steal_pointer (&onscreen_native->next_frame); + g_steal_pointer (&onscreen_native->posted_frame); } static void -meta_onscreen_native_clear_next_fb (CoglOnscreen *onscreen) +meta_onscreen_native_clear_posted_fb (CoglOnscreen *onscreen) { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - g_clear_pointer (&onscreen_native->next_frame, clutter_frame_unref); + g_clear_pointer (&onscreen_native->posted_frame, clutter_frame_unref); } static void @@ -288,7 +289,7 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, frame_info = cogl_onscreen_peek_head_frame_info (onscreen); frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - g_warn_if_fail (!onscreen_native->next_frame); + g_warn_if_fail (!onscreen_native->posted_frame); meta_onscreen_native_notify_frame_complete (onscreen); } @@ -340,7 +341,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_clear_next_fb (onscreen); + meta_onscreen_native_clear_posted_fb (onscreen); } static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { @@ -489,7 +490,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); MetaRendererNative *renderer_native = onscreen_native->renderer_native; - ClutterFrame *frame = onscreen_native->next_frame; + g_autoptr (ClutterFrame) frame = NULL; MetaFrameNative *frame_native; MetaGpuKms *render_gpu = onscreen_native->render_gpu; MetaCrtcKms *crtc_kms = META_CRTC_KMS (crtc); @@ -505,6 +506,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs, "Meta::OnscreenNative::flip_crtc()"); + frame = g_steal_pointer (&onscreen_native->next_frame); g_return_if_fail (frame); gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); @@ -566,6 +568,10 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, #endif } + g_warn_if_fail (!onscreen_native->posted_frame); + g_clear_pointer (&onscreen_native->posted_frame, clutter_frame_unref); + onscreen_native->posted_frame = g_steal_pointer (&frame); + meta_kms_update_add_page_flip_listener (kms_update, kms_crtc, &page_flip_listener_vtable, @@ -1251,7 +1257,7 @@ swap_buffer_result_feedback (const MetaKmsFeedback *kms_feedback, frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_clear_next_fb (onscreen); + meta_onscreen_native_clear_posted_fb (onscreen); } static const MetaKmsResultListenerVtable swap_buffer_result_listener_vtable = { @@ -1445,12 +1451,13 @@ post_latest_swap (CoglOnscreen *onscreen) MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); MetaKmsUpdate *kms_update; g_autoptr (MetaKmsFeedback) kms_feedback = NULL; - ClutterFrame *frame = onscreen_native->next_frame; + g_autoptr (ClutterFrame) frame = NULL; MetaFrameNative *frame_native; power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (power_save_mode == META_POWER_SAVE_ON) { + frame = clutter_frame_ref (onscreen_native->next_frame); frame_native = meta_frame_native_from_frame (frame); kms_update = meta_frame_native_ensure_kms_update (frame_native, @@ -1602,11 +1609,11 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, G_IO_ERROR_PERMISSION_DENIED)) { ClutterStageView *view = CLUTTER_STAGE_VIEW (onscreen_native->view); - ClutterFrame *next_frame = onscreen_native->next_frame; - MetaFrameNative *next_frame_native = - meta_frame_native_from_frame (next_frame); + ClutterFrame *posted_frame = onscreen_native->posted_frame; + MetaFrameNative *posted_frame_native = + meta_frame_native_from_frame (posted_frame); CoglScanout *scanout = - meta_frame_native_get_scanout (next_frame_native); + meta_frame_native_get_scanout (posted_frame_native); g_warning ("Direct scanout page flip failed: %s", error->message); @@ -1619,7 +1626,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_clear_next_fb (onscreen); + meta_onscreen_native_clear_posted_fb (onscreen); } static const MetaKmsResultListenerVtable scanout_result_listener_vtable = { @@ -2853,6 +2860,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->posted_frame, clutter_frame_unref); g_clear_pointer (&onscreen_native->presented_frame, clutter_frame_unref); g_clear_pointer (&onscreen_native->next_rectangles, g_free); -- GitLab From 2328a5e5e7f50454e56ae75a99c6bd9860ae7ad5 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Fri, 17 Sep 2021 17:59:28 +0800 Subject: [PATCH 11/30] 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. --- src/backends/native/meta-onscreen-native.c | 198 ++++++++++++++++++--- 1 file changed, 172 insertions(+), 26 deletions(-) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index bf08a18a3e9..9370ee44d0f 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -97,6 +97,7 @@ struct _MetaOnscreenNative ClutterFrame *presented_frame; ClutterFrame *posted_frame; + ClutterFrame *stalled_frame; ClutterFrame *next_frame; int *next_rectangles; /* 4 x n_rectangles */ int next_n_rectangles; @@ -127,6 +128,10 @@ struct _MetaOnscreenNative gulong privacy_screen_changed_handler_id; gulong color_space_changed_handler_id; gulong hdr_metadata_changed_handler_id; + + gboolean needs_flush; + + unsigned int swaps_pending; }; G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, @@ -135,7 +140,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, @@ -234,6 +243,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 @@ -283,15 +293,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 @@ -342,6 +350,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 = { @@ -402,18 +411,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, @@ -1254,12 +1281,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, }; @@ -1424,15 +1477,14 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, memcpy (onscreen_native->next_rectangles, rectangles, rectangles_size); onscreen_native->next_n_rectangles = n_rectangles; - g_warn_if_fail (!onscreen_native->next_frame); - g_clear_pointer (&onscreen_native->next_frame, clutter_frame_unref); - onscreen_native->next_frame = clutter_frame_ref (frame); + assign_next_frame (onscreen_native, frame); - post_latest_swap (onscreen); + onscreen_native->swaps_pending++; + try_post_latest_swap (onscreen); } 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); @@ -1454,12 +1506,38 @@ post_latest_swap (CoglOnscreen *onscreen) g_autoptr (ClutterFrame) frame = NULL; MetaFrameNative *frame_native; + 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; + + 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); + 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, @@ -1485,7 +1563,7 @@ post_latest_swap (CoglOnscreen *onscreen) } COGL_TRACE_BEGIN_SCOPED (MetaRendererNativePostKmsUpdate, - "Meta::OnscreenNative::post_latest_swap#post_pending_update()"); + "Meta::OnscreenNative::try_post_latest_swap#post_pending_update()"); switch (renderer_gpu_data->mode) { @@ -1678,13 +1756,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, @@ -1914,22 +2003,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, @@ -1952,7 +2098,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 @@ -2860,6 +3005,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); -- GitLab From 8d2570133277c7f8c6a8df822f786414d5384c57 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Fri, 9 Dec 2022 14:22:31 +0800 Subject: [PATCH 12/30] onscreen/native: Increase secondary GPU dumb_fbs from 2 to 3 So that they don't get overwritten prematurely during triple buffering causing tearing. https://launchpad.net/bugs/1999216 --- src/backends/native/meta-onscreen-native.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 9370ee44d0f..7b49ec530f8 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -76,7 +76,7 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState struct { MetaDrmBufferDumb *current_dumb_fb; - MetaDrmBufferDumb *dumb_fbs[2]; + MetaDrmBufferDumb *dumb_fbs[3]; } cpu; gboolean noted_primary_gpu_copy_ok; @@ -946,12 +946,17 @@ static MetaDrmBufferDumb * secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { MetaDrmBufferDumb *current_dumb_fb; + const int n_dumb_fbs = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); + int i; current_dumb_fb = secondary_gpu_state->cpu.current_dumb_fb; - if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[0]) - return secondary_gpu_state->cpu.dumb_fbs[1]; - else - return secondary_gpu_state->cpu.dumb_fbs[0]; + for (i = 0; i < n_dumb_fbs; i++) + { + if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i]) + return secondary_gpu_state->cpu.dumb_fbs[(i + 1) % n_dumb_fbs]; + } + + return secondary_gpu_state->cpu.dumb_fbs[0]; } static MetaDrmBuffer * -- GitLab From cf1edb1c955c28c2ecfbb15886533631d3cd42a8 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Thu, 4 Nov 2021 16:09:26 +0800 Subject: [PATCH 13/30] onscreen/native: Add function meta_onscreen_native_discard_pending_swaps --- src/backends/native/meta-onscreen-native.c | 11 +++++++++++ src/backends/native/meta-onscreen-native.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 7b49ec530f8..eb9b26c1220 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -2105,6 +2105,17 @@ post_finish_frame (MetaOnscreenNative *onscreen_native, META_KMS_UPDATE_FLAG_NONE); } +void +meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen) +{ + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + + onscreen_native->swaps_pending = 0; + + g_clear_pointer (&onscreen_native->stalled_frame, clutter_frame_unref); + g_clear_pointer (&onscreen_native->next_frame, clutter_frame_unref); +} + static gboolean should_surface_be_sharable (CoglOnscreen *onscreen) { diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h index 0e1193325a9..e30357d19d1 100644 --- a/src/backends/native/meta-onscreen-native.h +++ b/src/backends/native/meta-onscreen-native.h @@ -48,6 +48,8 @@ void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen); gboolean meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen, CoglScanout *scanout); +void meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen); + void meta_onscreen_native_set_view (CoglOnscreen *onscreen, MetaRendererView *view); -- GitLab From 63fef10c24f1c0b8f0191a2db5f4e8927da7bd8e Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Wed, 11 May 2022 16:00:32 +0800 Subject: [PATCH 14/30] onscreen/native: Skip try_post_latest_swap if shutting down Otherwise we could get: meta_kms_prepare_shutdown -> flush_callbacks -> ... -> try_post_latest_swap -> post and queue more callbacks So later in shutdown those callbacks would trigger an assertion failure in meta_kms_impl_device_atomic_finalize: g_hash_table_size (impl_device_atomic->page_flip_datas) == 0 --- src/backends/native/meta-onscreen-native.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index eb9b26c1220..b4969c9b99c 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -1499,6 +1499,8 @@ try_post_latest_swap (CoglOnscreen *onscreen) MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); + MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); @@ -1511,7 +1513,8 @@ try_post_latest_swap (CoglOnscreen *onscreen) g_autoptr (ClutterFrame) frame = NULL; MetaFrameNative *frame_native; - if (onscreen_native->next_frame == NULL) + if (onscreen_native->next_frame == NULL || + meta_kms_is_shutting_down (kms)) return; power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); -- GitLab From ffcf4dfbb894b8fc92ac3666d10241f1fe7e66b4 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Thu, 4 Nov 2021 16:09:52 +0800 Subject: [PATCH 15/30] renderer/native: Discard pending swaps when rebuilding views It's analogous to discard_pending_page_flips but represents swaps that might become flips after the next frame notification callbacks, thanks to triple buffering. Since the views are being rebuilt and their onscreens are about to be destroyed, turning those swaps into more flips/posts would just lead to unexpected behaviour (like trying to flip on a half-destroyed inactive CRTC). --- src/backends/native/meta-renderer-native.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 02d1e3e0c06..120ac4a9be7 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -1511,6 +1511,26 @@ detach_onscreens (MetaRenderer *renderer) } } +static void +discard_pending_swaps (MetaRenderer *renderer) +{ + GList *views = meta_renderer_get_views (renderer);; + GList *l; + + for (l = views; l; l = l->next) + { + ClutterStageView *stage_view = l->data; + CoglFramebuffer *fb = clutter_stage_view_get_onscreen (stage_view); + CoglOnscreen *onscreen; + + if (!COGL_IS_ONSCREEN (fb)) + continue; + + onscreen = COGL_ONSCREEN (fb); + meta_onscreen_native_discard_pending_swaps (onscreen); + } +} + static void meta_renderer_native_rebuild_views (MetaRenderer *renderer) { @@ -1521,6 +1541,7 @@ meta_renderer_native_rebuild_views (MetaRenderer *renderer) MetaRendererClass *parent_renderer_class = META_RENDERER_CLASS (meta_renderer_native_parent_class); + discard_pending_swaps (renderer); meta_kms_discard_pending_page_flips (kms); g_hash_table_remove_all (renderer_native->mode_set_updates); -- GitLab From febe563f91e136d7bcc6d0c7729a642ca2248f94 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Thu, 7 Dec 2023 16:28:20 +0800 Subject: [PATCH 16/30] clutter/frame-clock: Reuse existing idle period detection While it was correct for double buffering, the test was wrong for triple buffering and would lead to accidentally staying in triple buffering for too long. Fixes: 8f27ebf87eee6057992a90560d4118ab7bdf138d --- clutter/clutter/clutter-frame-clock.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 75563161f28..30f4938ffe5 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -496,6 +496,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, int64_t min_render_time_allowed_us; int64_t max_render_time_allowed_us; int64_t next_presentation_time_us; + int64_t next_smooth_presentation_time_us; int64_t next_update_time_us; now_us = g_get_monotonic_time (); @@ -540,7 +541,8 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, * */ last_presentation_time_us = frame_clock->last_presentation_time_us; - next_presentation_time_us = last_presentation_time_us + refresh_interval_us; + next_smooth_presentation_time_us = last_presentation_time_us + refresh_interval_us; + next_presentation_time_us = next_smooth_presentation_time_us; /* * However, the last presentation could have happened more than a frame ago. @@ -607,7 +609,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, } if (frame_clock->last_presentation_flags & CLUTTER_FRAME_INFO_FLAG_VSYNC && - next_presentation_time_us != last_presentation_time_us + refresh_interval_us) + next_presentation_time_us != next_smooth_presentation_time_us) { /* There was an idle period since the last presentation, so there seems * be no constantly updating actor. In this case it's best to start -- GitLab From 17ea7eb2d85b5f1583d805eeadd335c1dd5e64c0 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Tue, 10 Aug 2021 17:46:49 +0800 Subject: [PATCH 17/30] clutter/frame-clock: Lower the threshold for disabling error diffusion Error diffusion was introduced in 0555a5bbc15 for Nvidia where last presentation time is always unknown (zero). Dispatch times would drift apart always being a fraction of a frame late, and accumulated to cause periodic frame skips. So error diffusion corrected that precisely and avoided the skips. That works great with double buffering but less great with triple buffering. It's certainly still needed with triple buffering but correcting for a lateness of many milliseconds isn't a good idea. That's because a dispatch being that late is not due to main loop jitter but due to Nvidia's swap buffers blocking when the queue is full. So scheduling the next frame even earlier using last_dispatch_lateness_us would just perpetuate the problem of swap buffers blocking for too long. So now we lower the threshold of when error diffusion gets disabled. It's still high enough to fix the original smoothness problem it was for, but now low enough to detect Nvidia's occasionally blocking swaps and backs off in that case. Since the average duration of a blocking swap is half a frame interval and we want to distinguish between that and sub-millisecond jitter, the logical threshold is halfway again: refresh_interval_us/4. --- clutter/clutter/clutter-frame-clock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 30f4938ffe5..f5abde28944 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -930,7 +930,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, frame_clock->refresh_interval_us; lateness_us = time_us - ideal_dispatch_time_us; - if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us) + if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us / 4) frame_clock->last_dispatch_lateness_us = 0; else frame_clock->last_dispatch_lateness_us = lateness_us; -- GitLab From 22495459f4451dc673c3e5548dc35215fcaf288a Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Fri, 24 Jul 2020 14:13:11 +0800 Subject: [PATCH 18/30] clutter/frame-clock: Merge states DISPATCHING and PENDING_PRESENTED Chronologically they already overlap in time as presentation may complete in the middle of the dispatch function, otherwise they are contiguous in time. And most switch statements treated the two states the same already so they're easy to merge into a single `DISPATCHED` state. Having fewer states now will make life easier when we add more states later. --- clutter/clutter/clutter-frame-clock.c | 31 ++++++++++----------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index f5abde28944..ec96f9e351f 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -70,8 +70,7 @@ typedef enum _ClutterFrameClockState CLUTTER_FRAME_CLOCK_STATE_IDLE, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW, - CLUTTER_FRAME_CLOCK_STATE_DISPATCHING, - CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED, } ClutterFrameClockState; struct _ClutterFrameClock @@ -418,8 +417,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: g_warn_if_reached (); break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; @@ -440,8 +438,7 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: g_warn_if_reached (); break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; @@ -741,8 +738,7 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) frame_clock->pending_reschedule_now = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: break; } @@ -781,8 +777,7 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: frame_clock->pending_reschedule = TRUE; frame_clock->pending_reschedule_now = TRUE; return; @@ -837,8 +832,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: frame_clock->pending_reschedule = TRUE; return; } @@ -893,8 +887,7 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, frame_clock->pending_reschedule_now = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: break; } @@ -954,7 +947,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, frame_clock->last_dispatch_time_us = time_us; g_source_set_ready_time (frame_clock->source, -1); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHING; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED; frame_count = frame_clock->frame_count++; @@ -988,20 +981,20 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, switch (frame_clock->state) { case CLUTTER_FRAME_CLOCK_STATE_INIT: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: g_warn_if_reached (); break; case CLUTTER_FRAME_CLOCK_STATE_IDLE: + /* Presentation completed synchronously in the above listener */ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: switch (result) { case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED; break; case CLUTTER_FRAME_RESULT_IDLE: + /* The frame was aborted; nothing to paint/present */ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; @@ -1218,8 +1211,6 @@ clutter_frame_clock_dispose (GObject *object) { ClutterFrameClock *frame_clock = CLUTTER_FRAME_CLOCK (object); - g_warn_if_fail (frame_clock->state != CLUTTER_FRAME_CLOCK_STATE_DISPATCHING); - if (frame_clock->source) { g_signal_emit (frame_clock, signals[DESTROY], 0); -- GitLab From a2135eaaa819aae0691c4c8e487015b4e3b16258 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Mon, 13 Dec 2021 17:03:44 +0800 Subject: [PATCH 19/30] clutter/frame-clock: Clamp render times to two frames, not one Because the buffer queue may grow by an extra frame in triple buffering, we need measurements exceeding one frame to indicate when and by how much the scaling is necessary. --- clutter/clutter/clutter-frame-clock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index ec96f9e351f..bb6d7ab333f 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -390,7 +390,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, CLAMP (frame_clock->last_dispatch_lateness_us + dispatch_to_swap_us + MAX (swap_to_rendering_done_us, swap_to_flip_us), frame_clock->shortterm_max_update_duration_us, - frame_clock->refresh_interval_us); + 2 * frame_clock->refresh_interval_us); maybe_update_longterm_max_duration_us (frame_clock, frame_info); @@ -476,7 +476,7 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) frame_clock->vblank_duration_us + clutter_max_render_time_constant_us; - max_render_time_us = CLAMP (max_render_time_us, 0, refresh_interval_us); + max_render_time_us = CLAMP (max_render_time_us, 0, 2 * refresh_interval_us); return max_render_time_us; } -- GitLab From e4eb68274831cf6c36cb77ee880e0d9c98ee53e4 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Thu, 10 Sep 2020 16:34:53 +0800 Subject: [PATCH 20/30] clutter/frame-clock: Add triple buffering support --- clutter/clutter/clutter-frame-clock.c | 191 ++++++++++++++++++++++---- 1 file changed, 163 insertions(+), 28 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index bb6d7ab333f..7f39d3fbb98 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -70,7 +70,10 @@ typedef enum _ClutterFrameClockState CLUTTER_FRAME_CLOCK_STATE_IDLE, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW, - CLUTTER_FRAME_CLOCK_STATE_DISPATCHED, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO, } ClutterFrameClockState; struct _ClutterFrameClock @@ -91,6 +94,7 @@ struct _ClutterFrameClock ClutterFrameClockMode mode; int64_t last_dispatch_time_us; + int64_t prev_last_dispatch_time_us; int64_t last_dispatch_lateness_us; int64_t last_presentation_time_us; int64_t next_update_time_us; @@ -112,6 +116,7 @@ struct _ClutterFrameClock int64_t vblank_duration_us; /* Last KMS buffer submission time. */ int64_t last_flip_time_us; + int64_t prev_last_flip_time_us; /* Last time we promoted short-term maximum to long-term one */ int64_t longterm_promotion_us; @@ -369,14 +374,35 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, frame_info->has_valid_gpu_rendering_duration) { int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us; + int64_t dispatch_time_us = 0, flip_time_us = 0; + + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + g_warn_if_reached (); + G_GNUC_FALLTHROUGH; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + dispatch_time_us = frame_clock->last_dispatch_time_us; + flip_time_us = frame_clock->last_flip_time_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + dispatch_time_us = frame_clock->prev_last_dispatch_time_us; + flip_time_us = frame_clock->prev_last_flip_time_us; + break; + } dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us - - frame_clock->last_dispatch_time_us; + dispatch_time_us; swap_to_rendering_done_us = frame_info->gpu_rendering_duration_ns / 1000; swap_to_flip_us = - frame_clock->last_flip_time_us - + flip_time_us - frame_info->cpu_time_before_buffer_swap_us; CLUTTER_NOTE (FRAME_TIMINGS, @@ -417,10 +443,22 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: g_warn_if_reached (); break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } } @@ -438,10 +476,22 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: g_warn_if_reached (); break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } } @@ -456,7 +506,14 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) if (!frame_clock->ever_got_measurements || G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME)) - return (int64_t) (refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION); + { + int64_t ret = (int64_t) (refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION); + + if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE) + ret += refresh_interval_us; + + return ret; + } /* Max render time shows how early the frame clock needs to be dispatched * to make it to the predicted next presentation time. It is an estimate of @@ -493,7 +550,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, int64_t min_render_time_allowed_us; int64_t max_render_time_allowed_us; int64_t next_presentation_time_us; - int64_t next_smooth_presentation_time_us; + int64_t next_smooth_presentation_time_us = 0; int64_t next_update_time_us; now_us = g_get_monotonic_time (); @@ -538,7 +595,27 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, * */ last_presentation_time_us = frame_clock->last_presentation_time_us; - next_smooth_presentation_time_us = last_presentation_time_us + refresh_interval_us; + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + next_smooth_presentation_time_us = last_presentation_time_us + + refresh_interval_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + next_smooth_presentation_time_us = last_presentation_time_us + + 2 * refresh_interval_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + next_smooth_presentation_time_us = last_presentation_time_us + + 3 * refresh_interval_us; + break; + } + next_presentation_time_us = next_smooth_presentation_time_us; /* @@ -738,7 +815,17 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) frame_clock->pending_reschedule_now = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->pending_reschedule = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: break; } @@ -774,10 +861,17 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; frame_clock->pending_reschedule_now = TRUE; return; @@ -806,7 +900,6 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) frame_clock->next_update_time_us = next_update_time_us; g_source_set_ready_time (frame_clock->source, next_update_time_us); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; } void @@ -828,11 +921,17 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; return; case CLUTTER_FRAME_CLOCK_STATE_IDLE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; return; } @@ -861,7 +960,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->next_update_time_us = next_update_time_us; g_source_set_ready_time (frame_clock->source, next_update_time_us); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; } void @@ -877,6 +975,8 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, { case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: frame_clock->pending_reschedule = TRUE; @@ -887,7 +987,14 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, frame_clock->pending_reschedule_now = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->pending_reschedule = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; break; } @@ -944,10 +1051,27 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, } #endif + frame_clock->prev_last_dispatch_time_us = frame_clock->last_dispatch_time_us; frame_clock->last_dispatch_time_us = time_us; g_source_set_ready_time (frame_clock->source, -1); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED; + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + g_warn_if_reached (); + return; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO; + break; + } frame_count = frame_clock->frame_count++; @@ -978,26 +1102,36 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, result = iface->frame (frame_clock, frame, frame_clock->listener.user_data); COGL_TRACE_END (ClutterFrameClockFrame); - switch (frame_clock->state) + switch (result) { - case CLUTTER_FRAME_CLOCK_STATE_INIT: - g_warn_if_reached (); + case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: break; - case CLUTTER_FRAME_CLOCK_STATE_IDLE: - /* Presentation completed synchronously in the above listener */ - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: - break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: - switch (result) + case CLUTTER_FRAME_RESULT_IDLE: + /* The frame was aborted; nothing to paint/present */ + switch (frame_clock->state) { - case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + g_warn_if_reached (); break; - case CLUTTER_FRAME_RESULT_IDLE: - /* The frame was aborted; nothing to paint/present */ + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } break; } @@ -1033,6 +1167,7 @@ void clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, int64_t flip_time_us) { + frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us; frame_clock->last_flip_time_us = flip_time_us; } -- GitLab From 35b909ec9d1e55b6088ff60ef154e9888934be59 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Fri, 10 Dec 2021 16:28:04 +0800 Subject: [PATCH 21/30] clutter/frame-clock: Log N-buffers in CLUTTTER_DEBUG=frame-timings --- clutter/clutter/clutter-frame-clock.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 7f39d3fbb98..03ebca88023 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -281,6 +281,12 @@ void clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, ClutterFrameInfo *frame_info) { +#ifdef CLUTTER_ENABLE_DEBUG + const char *debug_state = + frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ? + "Triple buffering" : "Double buffering"; +#endif + COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented, "Clutter::FrameClock::presented()"); COGL_TRACE_DESCRIBE (ClutterFrameClockNotifyPresented, @@ -406,7 +412,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, frame_info->cpu_time_before_buffer_swap_us; CLUTTER_NOTE (FRAME_TIMINGS, - "update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", + "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", + debug_state, frame_clock->last_dispatch_lateness_us, dispatch_to_swap_us, swap_to_rendering_done_us, @@ -425,7 +432,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, } else { - CLUTTER_NOTE (FRAME_TIMINGS, "update2dispatch %ld µs", + CLUTTER_NOTE (FRAME_TIMINGS, "%s: update2dispatch %ld µs", + debug_state, frame_clock->last_dispatch_lateness_us); } -- GitLab From eb69d0c2959c866beac9fe2d8dc3575db014de5d Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Tue, 7 Sep 2021 19:08:15 +0800 Subject: [PATCH 22/30] clutter/frame: Add ClutterFrameHint to ClutterFrame This will allow the backend to provide performance hints to the frame clock in future. --- clutter/clutter/clutter-frame-clock.h | 6 ++++++ clutter/clutter/clutter-frame-private.h | 1 + clutter/clutter/clutter-frame.c | 13 +++++++++++++ clutter/clutter/clutter-frame.h | 7 +++++++ 4 files changed, 27 insertions(+) diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h index a7be5ef3167..5adf995098b 100644 --- a/clutter/clutter/clutter-frame-clock.h +++ b/clutter/clutter/clutter-frame-clock.h @@ -33,6 +33,12 @@ typedef enum _ClutterFrameResult CLUTTER_FRAME_RESULT_IDLE, } ClutterFrameResult; +typedef enum _ClutterFrameHint +{ + CLUTTER_FRAME_HINT_NONE = 0, + CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED = 1 << 0, +} ClutterFrameHint; + #define CLUTTER_TYPE_FRAME_CLOCK (clutter_frame_clock_get_type ()) CLUTTER_EXPORT G_DECLARE_FINAL_TYPE (ClutterFrameClock, clutter_frame_clock, diff --git a/clutter/clutter/clutter-frame-private.h b/clutter/clutter/clutter-frame-private.h index ef66b874edf..ce140560a89 100644 --- a/clutter/clutter/clutter-frame-private.h +++ b/clutter/clutter/clutter-frame-private.h @@ -36,6 +36,7 @@ struct _ClutterFrame gboolean has_result; ClutterFrameResult result; + ClutterFrameHint hints; }; CLUTTER_EXPORT diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c index 7436f9f182d..53c289b2c5a 100644 --- a/clutter/clutter/clutter-frame.c +++ b/clutter/clutter/clutter-frame.c @@ -115,3 +115,16 @@ clutter_frame_set_result (ClutterFrame *frame, frame->result = result; frame->has_result = TRUE; } + +void +clutter_frame_set_hint (ClutterFrame *frame, + ClutterFrameHint hint) +{ + frame->hints |= hint; +} + +ClutterFrameHint +clutter_frame_get_hints (ClutterFrame *frame) +{ + return frame->hints; +} diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h index 34f0770bd7d..c7b3d02acb9 100644 --- a/clutter/clutter/clutter-frame.h +++ b/clutter/clutter/clutter-frame.h @@ -54,4 +54,11 @@ void clutter_frame_set_result (ClutterFrame *frame, CLUTTER_EXPORT gboolean clutter_frame_has_result (ClutterFrame *frame); +CLUTTER_EXPORT +void clutter_frame_set_hint (ClutterFrame *frame, + ClutterFrameHint hint); + +CLUTTER_EXPORT +ClutterFrameHint clutter_frame_get_hints (ClutterFrame *frame); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFrame, clutter_frame_unref) -- GitLab From cd149ab246b3373bad15081e9d4cfbb77ff7fc8a Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Tue, 7 Sep 2021 19:10:26 +0800 Subject: [PATCH 23/30] backends: Flag that the frame attempted direct scanout We need this hint whether direct scanout succeeds or fails because it's the mechanism by which we will tell the clock to enforce double buffering, thus making direct scanout possible on future frames. Triple buffering will be disabled until such time that direct scanout is not being attempted. --- src/backends/meta-stage-impl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c index 552cc1637cb..c4bed1a83dd 100644 --- a/src/backends/meta-stage-impl.c +++ b/src/backends/meta-stage-impl.c @@ -774,6 +774,8 @@ meta_stage_impl_redraw_view (ClutterStageWindow *stage_window, { g_autoptr (GError) error = NULL; + clutter_frame_set_hint (frame, CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED); + if (meta_stage_impl_scanout_view (stage_impl, stage_view, scanout, -- GitLab From 039208d748eb16c439f3872625441e2aa79f0d80 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Tue, 7 Sep 2021 19:15:18 +0800 Subject: [PATCH 24/30] clutter: Pass ClutterFrameHint(s) to the frame clock --- clutter/clutter/clutter-frame-clock.c | 8 ++++++-- clutter/clutter/clutter-frame-clock.h | 5 +++-- clutter/clutter/clutter-stage-view.c | 11 +++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 03ebca88023..cc4242abfe3 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -118,6 +118,8 @@ struct _ClutterFrameClock int64_t last_flip_time_us; int64_t prev_last_flip_time_us; + ClutterFrameHint last_flip_hints; + /* Last time we promoted short-term maximum to long-term one */ int64_t longterm_promotion_us; /* Long-term maximum update duration */ @@ -1172,11 +1174,13 @@ frame_clock_source_dispatch (GSource *source, } void -clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, - int64_t flip_time_us) +clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, + int64_t flip_time_us, + ClutterFrameHint hints) { frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us; frame_clock->last_flip_time_us = flip_time_us; + frame_clock->last_flip_hints = hints; } GString * diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h index 5adf995098b..bfc89bde091 100644 --- a/clutter/clutter/clutter-frame-clock.h +++ b/clutter/clutter/clutter-frame-clock.h @@ -108,7 +108,8 @@ void clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock, CLUTTER_EXPORT float clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock); -void clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, - int64_t flip_time_us); +void clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, + int64_t flip_time_us, + ClutterFrameHint hints); GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock); diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c index 1e9f98cfc9f..3686c25e065 100644 --- a/clutter/clutter/clutter-stage-view.c +++ b/clutter/clutter/clutter-stage-view.c @@ -898,14 +898,21 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock, _clutter_stage_window_redraw_view (stage_window, view, frame); - clutter_frame_clock_record_flip_time (frame_clock, - g_get_monotonic_time ()); + clutter_frame_clock_record_flip (frame_clock, + g_get_monotonic_time (), + clutter_frame_get_hints (frame)); clutter_stage_emit_after_paint (stage, view, frame); if (_clutter_context_get_show_fps ()) end_frame_timing_measurement (view); } + else + { + clutter_frame_clock_record_flip (frame_clock, + g_get_monotonic_time (), + clutter_frame_get_hints (frame)); + } _clutter_stage_window_finish_frame (stage_window, view, frame); -- GitLab From 5d853d34625322a961d966d58d9b2a20ffc0c466 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Tue, 7 Sep 2021 19:15:55 +0800 Subject: [PATCH 25/30] clutter/frame-clock: Conditionally disable triple buffering 1. When direct scanout is attempted There's no compositing during direct scanout so the "render" time is zero. Thus there is no need to implement triple buffering for direct scanouts. Stick to double buffering and enjoy the lower latency. 2. If disabled by environment variable MUTTER_DEBUG_TRIPLE_BUFFERING With possible values {never, auto, always} where auto is the default. 3. When VRR is in use VRR calls `clutter_frame_clock_schedule_update_now` which would keep the buffer queue full, which in turn prevented direct scanout mode. Because OnscreenNative currently only supports direct scanout with double buffering. We now break that feedback loop by preventing triple buffering from being scheduled when the frame clock mode becomes variable. Long term this could also be solved by supporting triple buffering in direct scanout mode. But whether or not that would be desirable given the latency penalty remains to be seen. --- clutter/clutter/clutter-frame-clock.c | 70 ++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index cc4242abfe3..4b83501b43e 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -42,6 +42,15 @@ enum static guint signals[N_SIGNALS]; +typedef enum +{ + TRIPLE_BUFFERING_MODE_NEVER, + TRIPLE_BUFFERING_MODE_AUTO, + TRIPLE_BUFFERING_MODE_ALWAYS, +} TripleBufferingMode; + +static TripleBufferingMode triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; + #define SYNC_DELAY_FALLBACK_FRACTION 0.875f #define MINIMUM_REFRESH_RATE 30.f @@ -854,6 +863,25 @@ clutter_frame_clock_uninhibit (ClutterFrameClock *frame_clock) maybe_reschedule_update (frame_clock); } +static gboolean +want_triple_buffering (ClutterFrameClock *frame_clock) +{ + switch (triple_buffering_mode) + { + case TRIPLE_BUFFERING_MODE_NEVER: + return FALSE; + case TRIPLE_BUFFERING_MODE_AUTO: + return frame_clock->mode == CLUTTER_FRAME_CLOCK_MODE_FIXED && + !(frame_clock->last_flip_hints & + CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED); + case TRIPLE_BUFFERING_MODE_ALWAYS: + return TRUE; + } + + g_assert_not_reached (); + return FALSE; +} + void clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) { @@ -876,11 +904,18 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + if (want_triple_buffering (frame_clock)) + { + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; + break; + } + G_GNUC_FALLTHROUGH; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; frame_clock->pending_reschedule_now = TRUE; @@ -916,6 +951,11 @@ void clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) { int64_t next_update_time_us = -1; + TripleBufferingMode current_mode = triple_buffering_mode; + + if (current_mode == TRIPLE_BUFFERING_MODE_AUTO && + !want_triple_buffering (frame_clock)) + current_mode = TRIPLE_BUFFERING_MODE_NEVER; if (frame_clock->inhibit_count > 0) { @@ -939,7 +979,23 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: return; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + switch (current_mode) + { + case TRIPLE_BUFFERING_MODE_NEVER: + frame_clock->pending_reschedule = TRUE; + return; + case TRIPLE_BUFFERING_MODE_AUTO: + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + break; + case TRIPLE_BUFFERING_MODE_ALWAYS: + next_update_time_us = g_get_monotonic_time (); + frame_clock->next_presentation_time_us = 0; + frame_clock->is_next_presentation_time_valid = FALSE; + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + goto got_update_time; + } break; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; @@ -966,6 +1022,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) break; } +got_update_time: g_warn_if_fail (next_update_time_us != -1); frame_clock->next_update_time_us = next_update_time_us; @@ -1381,6 +1438,15 @@ static void clutter_frame_clock_class_init (ClutterFrameClockClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + const char *mode_str; + + mode_str = g_getenv ("MUTTER_DEBUG_TRIPLE_BUFFERING"); + if (!g_strcmp0 (mode_str, "never")) + triple_buffering_mode = TRIPLE_BUFFERING_MODE_NEVER; + else if (!g_strcmp0 (mode_str, "auto")) + triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; + else if (!g_strcmp0 (mode_str, "always")) + triple_buffering_mode = TRIPLE_BUFFERING_MODE_ALWAYS; object_class->dispose = clutter_frame_clock_dispose; -- GitLab From a1ce0f7a322416122c9519df84aeafe60f34df60 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Thu, 30 Jun 2022 18:56:06 +0800 Subject: [PATCH 26/30] tests/native-kms-render: Fix failing client-scanout test It was assuming an immediate transition from compositing (triple buffering) to direct scanout (double buffering), whereas there is a one frame delay in that transition as the buffer queue shrinks. We don't lose any frames in the transition. --- src/tests/native-kms-render.c | 106 ++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/src/tests/native-kms-render.c b/src/tests/native-kms-render.c index f5ebc23fec7..2f870fdc331 100644 --- a/src/tests/native-kms-render.c +++ b/src/tests/native-kms-render.c @@ -39,6 +39,8 @@ #include "tests/meta-wayland-test-driver.h" #include "tests/meta-wayland-test-utils.h" +#define N_FRAMES_PER_TEST 30 + typedef struct { int number_of_frames_left; @@ -46,12 +48,15 @@ typedef struct struct { int n_paints; - uint32_t fb_id; + int n_presentations; + int n_direct_scanouts; + GList *fb_ids; } scanout; gboolean wait_for_scanout; struct { + int scanouts_attempted; gboolean scanout_sabotaged; gboolean fallback_painted; guint repaint_guard_id; @@ -101,7 +106,7 @@ meta_test_kms_render_basic (void) gulong handler_id; test = (KmsRenderingTest) { - .number_of_frames_left = 10, + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), }; handler_id = g_signal_connect (stage, "after-update", @@ -123,7 +128,6 @@ on_scanout_before_update (ClutterStage *stage, KmsRenderingTest *test) { test->scanout.n_paints = 0; - test->scanout.fb_id = 0; } static void @@ -135,6 +139,7 @@ on_scanout_before_paint (ClutterStage *stage, CoglScanout *scanout; CoglScanoutBuffer *scanout_buffer; MetaDrmBuffer *buffer; + uint32_t fb_id; scanout = clutter_stage_view_peek_scanout (stage_view); if (!scanout) @@ -143,8 +148,13 @@ on_scanout_before_paint (ClutterStage *stage, scanout_buffer = cogl_scanout_get_buffer (scanout); g_assert_true (META_IS_DRM_BUFFER (scanout_buffer)); buffer = META_DRM_BUFFER (scanout_buffer); - test->scanout.fb_id = meta_drm_buffer_get_fb_id (buffer); - g_assert_cmpuint (test->scanout.fb_id, >, 0); + fb_id = meta_drm_buffer_get_fb_id (buffer); + g_assert_cmpuint (fb_id, >, 0); + test->scanout.fb_ids = g_list_append (test->scanout.fb_ids, + GUINT_TO_POINTER (fb_id)); + + /* Triple buffering, but no higher */ + g_assert_cmpuint (g_list_length (test->scanout.fb_ids), <=, 2); } static void @@ -173,12 +183,12 @@ on_scanout_presented (ClutterStage *stage, MetaDeviceFile *device_file; GError *error = NULL; drmModeCrtc *drm_crtc; + uint32_t first_fb_id_expected; - if (test->wait_for_scanout && test->scanout.n_paints > 0) + if (test->wait_for_scanout && test->scanout.fb_ids == NULL) return; - if (test->wait_for_scanout && test->scanout.fb_id == 0) - return; + test->scanout.n_presentations++; device_pool = meta_backend_native_get_device_pool (backend_native); @@ -197,15 +207,41 @@ on_scanout_presented (ClutterStage *stage, drm_crtc = drmModeGetCrtc (meta_device_file_get_fd (device_file), meta_kms_crtc_get_id (kms_crtc)); g_assert_nonnull (drm_crtc); - if (test->scanout.fb_id == 0) - g_assert_cmpuint (drm_crtc->buffer_id, !=, test->scanout.fb_id); + + if (test->scanout.fb_ids) + { + test->scanout.n_direct_scanouts++; + first_fb_id_expected = GPOINTER_TO_UINT (test->scanout.fb_ids->data); + test->scanout.fb_ids = g_list_delete_link (test->scanout.fb_ids, + test->scanout.fb_ids); + } else - g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id); + { + first_fb_id_expected = 0; + } + + /* The buffer ID won't match on the first frame because switching from + * triple buffered compositing to double buffered direct scanout takes + * an extra frame to drain the queue. Thereafter we are in direct scanout + * mode and expect the buffer IDs to match. + */ + if (test->scanout.n_presentations > 1) + { + if (first_fb_id_expected == 0) + g_assert_cmpuint (drm_crtc->buffer_id, !=, first_fb_id_expected); + else + g_assert_cmpuint (drm_crtc->buffer_id, ==, first_fb_id_expected); + } + drmModeFreeCrtc (drm_crtc); meta_device_file_release (device_file); - g_main_loop_quit (test->loop); + test->number_of_frames_left--; + if (test->number_of_frames_left <= 0) + g_main_loop_quit (test->loop); + else + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } typedef enum @@ -244,7 +280,9 @@ meta_test_kms_render_client_scanout (void) g_assert_nonnull (wayland_test_client); test = (KmsRenderingTest) { + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), + .scanout = {0}, .wait_for_scanout = TRUE, }; @@ -270,7 +308,8 @@ meta_test_kms_render_client_scanout (void) clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, >, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST); g_debug ("Unmake fullscreen"); window = meta_find_window_from_title (test_context, "dma-buf-scanout-test"); @@ -292,10 +331,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.y, ==, 10); test.wait_for_scanout = FALSE; + test.number_of_frames_left = N_FRAMES_PER_TEST; + test.scanout.n_presentations = 0; + test.scanout.n_direct_scanouts = 0; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, ==, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, 0); g_debug ("Moving back to 0, 0"); meta_window_move_frame (window, TRUE, 0, 0); @@ -307,10 +351,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.y, ==, 0); test.wait_for_scanout = TRUE; + test.number_of_frames_left = N_FRAMES_PER_TEST; + test.scanout.n_presentations = 0; + test.scanout.n_direct_scanouts = 0; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, >, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST); g_signal_handler_disconnect (stage, before_update_handler_id); g_signal_handler_disconnect (stage, before_paint_handler_id); @@ -364,6 +413,15 @@ on_scanout_fallback_before_paint (ClutterStage *stage, if (!scanout) return; + test->scanout_fallback.scanouts_attempted++; + + /* The first scanout candidate frame will get composited due to triple + * buffering draining the queue to drop to double buffering. So don't + * sabotage that first frame. + */ + if (test->scanout_fallback.scanouts_attempted < 2) + return; + g_assert_false (test->scanout_fallback.scanout_sabotaged); if (is_atomic_mode_setting (kms_device)) @@ -401,6 +459,15 @@ on_scanout_fallback_paint_view (ClutterStage *stage, g_clear_handle_id (&test->scanout_fallback.repaint_guard_id, g_source_remove); test->scanout_fallback.fallback_painted = TRUE; + test->scanout_fallback.scanout_sabotaged = FALSE; + } + else if (test->scanout_fallback.scanouts_attempted == 1) + { + /* Now that we've seen the first scanout attempt that was inhibited by + * triple buffering, try a second frame. The second one should scanout + * and will be sabotaged. + */ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } } @@ -410,11 +477,11 @@ on_scanout_fallback_presented (ClutterStage *stage, ClutterFrameInfo *frame_info, KmsRenderingTest *test) { - if (!test->scanout_fallback.scanout_sabotaged) - return; + if (test->scanout_fallback.fallback_painted) + g_main_loop_quit (test->loop); - g_assert_true (test->scanout_fallback.fallback_painted); - g_main_loop_quit (test->loop); + test->number_of_frames_left--; + g_assert_cmpint (test->number_of_frames_left, >, 0); } static void @@ -443,6 +510,7 @@ meta_test_kms_render_client_scanout_fallback (void) g_assert_nonnull (wayland_test_client); test = (KmsRenderingTest) { + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), }; -- GitLab From aa723fc76a6cbe93709cc07f3ca9d3b457d576ce Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Thu, 22 Jun 2023 15:19:53 +0800 Subject: [PATCH 27/30] onscreen/native: Avoid callbacks on "detached" onscreens Detached onscreens have no valid view so avoid servicing callbacks on them during/after sleep mode. As previously mentioned in 45bda2d969f. Closes: https://launchpad.net/bugs/2020049 --- src/backends/native/meta-onscreen-native.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index b4969c9b99c..26d8c7bcfa2 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -1514,6 +1514,7 @@ try_post_latest_swap (CoglOnscreen *onscreen) MetaFrameNative *frame_native; if (onscreen_native->next_frame == NULL || + onscreen_native->view == NULL || meta_kms_is_shutting_down (kms)) return; -- GitLab From 5567e8d0ea2977589d410b86dc75e35f47da26fb Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Tue, 18 Jul 2023 16:08:25 +0800 Subject: [PATCH 28/30] clutter/frame-clock: Record measurements of zero for cursor-only updates But only if we've ever got actual swap measurements (COGL_FEATURE_ID_TIMESTAMP_QUERY). If it's supported then we now drop to double buffering and get optimal latency on a burst of cursor-only updates. Closes: https://launchpad.net/bugs/2023363 --- clutter/clutter/clutter-frame-clock.c | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 4b83501b43e..87928771300 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -262,10 +262,6 @@ static void maybe_update_longterm_max_duration_us (ClutterFrameClock *frame_clock, ClutterFrameInfo *frame_info) { - /* Do not update long-term max if there has been no measurement */ - if (!frame_clock->shortterm_max_update_duration_us) - return; - if ((frame_info->presentation_time - frame_clock->longterm_promotion_us) < G_USEC_PER_SEC) return; @@ -387,8 +383,9 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, frame_clock->got_measurements_last_frame = FALSE; - if (frame_info->cpu_time_before_buffer_swap_us != 0 && - frame_info->has_valid_gpu_rendering_duration) + if ((frame_info->cpu_time_before_buffer_swap_us != 0 && + frame_info->has_valid_gpu_rendering_duration) || + frame_clock->ever_got_measurements) { int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us; int64_t dispatch_time_us = 0, flip_time_us = 0; @@ -413,14 +410,21 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, break; } - dispatch_to_swap_us = - frame_info->cpu_time_before_buffer_swap_us - - dispatch_time_us; + if (frame_info->cpu_time_before_buffer_swap_us == 0) + { + /* Cursor-only updates with no "swap" or "flip" */ + dispatch_to_swap_us = 0; + swap_to_flip_us = 0; + } + else + { + dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us - + dispatch_time_us; + swap_to_flip_us = flip_time_us - + frame_info->cpu_time_before_buffer_swap_us; + } swap_to_rendering_done_us = frame_info->gpu_rendering_duration_ns / 1000; - swap_to_flip_us = - flip_time_us - - frame_info->cpu_time_before_buffer_swap_us; CLUTTER_NOTE (FRAME_TIMINGS, "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", -- GitLab From 45f45da3f337cbf396166c8398abb9933ac14afd Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Wed, 26 Jul 2023 17:27:10 +0800 Subject: [PATCH 29/30] backends/native: Make await_flush later and inlined "Later" as in after GL is finished rendering so as to not block the deadline timer from updating the cursor smoothly during long GL renders. Indeed this could make a primary plane frame late in some cases but that's only when going from idle to active, so there is no previous frame to observe stutter within the primary plane. It does however fix observable stutter in the cursor plane by not dropping/stalling an otherwise good cursor update in a continuous stream of cursor updates. "Inlined" as in we don't need most of `meta_kms_device_await_flush`. The important part is that it calls `disarm_crtc_frame_deadline_timer` to avoid attempting two posts at once. So that's all that is kept. This also fixes a deadlock in triple buffering. By not cancelling the cursor update during a GL render we're also not cancelling a primary plane update that might have already been piggybacked onto it by `queue_update`. Cherry picked from !3149 --- src/backends/native/meta-kms-impl-device.c | 4 +++- src/backends/native/meta-onscreen-native.c | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c index 946af626f4a..65857c51a79 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c @@ -1779,9 +1779,11 @@ meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device, meta_kms_update_merge_from (crtc_frame->pending_update, update); meta_kms_update_free (update); update = g_steal_pointer (&crtc_frame->pending_update); - disarm_crtc_frame_deadline_timer (crtc_frame); } + if (crtc_frame->deadline.armed) + disarm_crtc_frame_deadline_timer (crtc_frame); + meta_kms_device_handle_flush (priv->device, latch_crtc); feedback = do_process (impl_device, latch_crtc, update, flags); diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 26d8c7bcfa2..4f7267afe48 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -1892,11 +1892,7 @@ meta_onscreen_native_before_redraw (CoglOnscreen *onscreen, ClutterFrame *frame) { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); - MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); - meta_kms_device_await_flush (meta_kms_crtc_get_device (kms_crtc), - kms_crtc); maybe_update_frame_sync (onscreen_native, frame); } -- GitLab From 316385e14acc63fa31818a28003de43ea40eb31c Mon Sep 17 00:00:00 2001 From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Wed, 17 Jan 2024 17:21:03 +0800 Subject: [PATCH 30/30] clutter/frame-clock: Optimize latency for platforms missing TIMESTAMP_QUERY Previously if we had no measurements then `compute_max_render_time_us` would pessimise its answer to ensure triple buffering could be reached: ``` if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE) ret += refresh_interval_us; ``` But that also meant entering triple buffering even when not required. Now we make `compute_max_render_time_us` more honest and return failure if the answer isn't known (or is disabled). This in turn allows us to optimize `calculate_next_update_time_us` for this special case, ensuring triple buffering can be used, but isn't blindly always used. This makes a visible difference to the latency when dragging windows in Xorg, but will also help Wayland sessions on platforms lacking TIMESTAMP_QUERY such as Raspberry Pi. --- clutter/clutter/clutter-frame-clock.c | 69 +++++++++++++++++---------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 87928771300..72f1dd3a394 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -518,25 +518,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) } } -static int64_t -clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) +static gboolean +clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock, + int64_t *max_render_time_us) { int64_t refresh_interval_us; - int64_t max_render_time_us; refresh_interval_us = frame_clock->refresh_interval_us; if (!frame_clock->ever_got_measurements || G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME)) - { - int64_t ret = (int64_t) (refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION); - - if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE) - ret += refresh_interval_us; - - return ret; - } + return FALSE; /* Max render time shows how early the frame clock needs to be dispatched * to make it to the predicted next presentation time. It is an estimate of @@ -550,15 +543,15 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) * - The duration of vertical blank. * - A constant to account for variations in the above estimates. */ - max_render_time_us = + *max_render_time_us = MAX (frame_clock->longterm_max_update_duration_us, frame_clock->shortterm_max_update_duration_us) + frame_clock->vblank_duration_us + clutter_max_render_time_constant_us; - max_render_time_us = CLAMP (max_render_time_us, 0, 2 * refresh_interval_us); + *max_render_time_us = CLAMP (*max_render_time_us, 0, 2 * refresh_interval_us); - return max_render_time_us; + return TRUE; } static void @@ -575,6 +568,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, int64_t next_presentation_time_us; int64_t next_smooth_presentation_time_us = 0; int64_t next_update_time_us; + gboolean max_render_time_is_known; now_us = g_get_monotonic_time (); @@ -594,10 +588,13 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, } min_render_time_allowed_us = refresh_interval_us / 2; - max_render_time_allowed_us = - clutter_frame_clock_compute_max_render_time_us (frame_clock); - if (min_render_time_allowed_us > max_render_time_allowed_us) + max_render_time_is_known = + clutter_frame_clock_compute_max_render_time_us (frame_clock, + &max_render_time_allowed_us); + + if (max_render_time_is_known && + min_render_time_allowed_us > max_render_time_allowed_us) min_render_time_allowed_us = max_render_time_allowed_us; /* @@ -718,6 +715,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, } else { + /* If the max render time isn't known then using the current value of + * next_presentation_time_us is suboptimal. Targeting always one frame + * prior to that we'd lose the ability to scale up to triple buffering + * on late presentation. But targeting two frames prior we would be + * always triple buffering even when not required. + * So the algorithm for deciding when to scale up to triple buffering + * in the absence of render time measurements is to simply target full + * frame rate. If we're keeping up then we'll stay double buffering. If + * we're not keeping up then this will switch us to triple buffering. + */ + if (!max_render_time_is_known) + { + max_render_time_allowed_us = + (int64_t) (refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION); + next_presentation_time_us = + last_presentation_time_us + refresh_interval_us; + } + while (next_presentation_time_us - min_render_time_allowed_us < now_us) next_presentation_time_us += refresh_interval_us; @@ -749,7 +764,9 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock, refresh_interval_us = frame_clock->refresh_interval_us; - if (frame_clock->last_presentation_time_us == 0) + if (frame_clock->last_presentation_time_us == 0 || + !clutter_frame_clock_compute_max_render_time_us (frame_clock, + &max_render_time_allowed_us)) { *out_next_update_time_us = frame_clock->last_dispatch_time_us ? @@ -762,9 +779,6 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock, return; } - max_render_time_allowed_us = - clutter_frame_clock_compute_max_render_time_us (frame_clock); - last_presentation_time_us = frame_clock->last_presentation_time_us; next_presentation_time_us = last_presentation_time_us + refresh_interval_us; @@ -1247,12 +1261,19 @@ clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock) { + int64_t max_render_time_us; int64_t max_update_duration_us; GString *string; - string = g_string_new (NULL); - g_string_append_printf (string, "Max render time: %ld µs", - clutter_frame_clock_compute_max_render_time_us (frame_clock)); + string = g_string_new ("Max render time: "); + if (!clutter_frame_clock_compute_max_render_time_us (frame_clock, + &max_render_time_us)) + { + g_string_append (string, "unknown"); + return string; + } + + g_string_append_printf (string, "%ld µs", max_render_time_us); if (frame_clock->got_measurements_last_frame) g_string_append_printf (string, " ="); -- GitLab
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor